blob: e2ab858122f9745dfb2e0408894eda8651c8980f [file] [log] [blame]
/**************************************************************************
* Copyright (c) 2011, Intel Corporation.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
**************************************************************************/
#include "psb_drv.h"
#include "mid_bios.h"
#include "mdfld_output.h"
#include "mdfld_dsi_output.h"
#include "tc35876x-dsi-lvds.h"
#include <asm/intel_scu_ipc.h>
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF
#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */
#define BLC_PWM_FREQ_CALC_CONSTANT 32
#define MHz 1000000
#define BRIGHTNESS_MIN_LEVEL 1
#define BRIGHTNESS_MAX_LEVEL 100
#define BRIGHTNESS_MASK 0xFF
#define BLC_POLARITY_NORMAL 0
#define BLC_POLARITY_INVERSE 1
#define BLC_ADJUSTMENT_MAX 100
#define MDFLD_BLC_PWM_PRECISION_FACTOR 10
#define MDFLD_BLC_MAX_PWM_REG_FREQ 0xFFFE
#define MDFLD_BLC_MIN_PWM_REG_FREQ 0x2
#define MDFLD_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
#define MDFLD_BACKLIGHT_PWM_CTL_SHIFT (16)
static struct backlight_device *mdfld_backlight_device;
int mdfld_set_brightness(struct backlight_device *bd)
{
struct drm_device *dev =
(struct drm_device *)bl_get_data(mdfld_backlight_device);
struct drm_psb_private *dev_priv = dev->dev_private;
int level = bd->props.brightness;
DRM_DEBUG_DRIVER("backlight level set to %d\n", level);
/* Perform value bounds checking */
if (level < BRIGHTNESS_MIN_LEVEL)
level = BRIGHTNESS_MIN_LEVEL;
if (gma_power_begin(dev, false)) {
u32 adjusted_level = 0;
/*
* Adjust the backlight level with the percent in
* dev_priv->blc_adj2
*/
adjusted_level = level * dev_priv->blc_adj2;
adjusted_level = adjusted_level / BLC_ADJUSTMENT_MAX;
dev_priv->brightness_adjusted = adjusted_level;
if (mdfld_get_panel_type(dev, 0) == TC35876X) {
if (dev_priv->dpi_panel_on[0] ||
dev_priv->dpi_panel_on[2])
tc35876x_brightness_control(dev,
dev_priv->brightness_adjusted);
} else {
if (dev_priv->dpi_panel_on[0])
mdfld_dsi_brightness_control(dev, 0,
dev_priv->brightness_adjusted);
}
if (dev_priv->dpi_panel_on[2])
mdfld_dsi_brightness_control(dev, 2,
dev_priv->brightness_adjusted);
gma_power_end(dev);
}
/* cache the brightness for later use */
dev_priv->brightness = level;
return 0;
}
static int mdfld_get_brightness(struct backlight_device *bd)
{
struct drm_device *dev =
(struct drm_device *)bl_get_data(mdfld_backlight_device);
struct drm_psb_private *dev_priv = dev->dev_private;
DRM_DEBUG_DRIVER("brightness = 0x%x \n", dev_priv->brightness);
/* return locally cached var instead of HW read (due to DPST etc.) */
return dev_priv->brightness;
}
static const struct backlight_ops mdfld_ops = {
.get_brightness = mdfld_get_brightness,
.update_status = mdfld_set_brightness,
};
static int device_backlight_init(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = (struct drm_psb_private *)
dev->dev_private;
dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX;
dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX;
return 0;
}
static int mdfld_backlight_init(struct drm_device *dev)
{
struct backlight_properties props;
int ret = 0;
memset(&props, 0, sizeof(struct backlight_properties));
props.max_brightness = BRIGHTNESS_MAX_LEVEL;
props.type = BACKLIGHT_PLATFORM;
mdfld_backlight_device = backlight_device_register("mdfld-bl",
NULL, (void *)dev, &mdfld_ops, &props);
if (IS_ERR(mdfld_backlight_device))
return PTR_ERR(mdfld_backlight_device);
ret = device_backlight_init(dev);
if (ret)
return ret;
mdfld_backlight_device->props.brightness = BRIGHTNESS_MAX_LEVEL;
mdfld_backlight_device->props.max_brightness = BRIGHTNESS_MAX_LEVEL;
backlight_update_status(mdfld_backlight_device);
return 0;
}
#endif
struct backlight_device *mdfld_get_backlight_device(void)
{
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
return mdfld_backlight_device;
#else
return NULL;
#endif
}
/*
* mdfld_save_display_registers
*
* Description: We are going to suspend so save current display
* register state.
*
* Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio
*/
static int mdfld_save_display_registers(struct drm_device *dev, int pipenum)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct medfield_state *regs = &dev_priv->regs.mdfld;
struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum];
const struct psb_offset *map = &dev_priv->regmap[pipenum];
int i;
u32 *mipi_val;
/* register */
u32 mipi_reg = MIPI;
switch (pipenum) {
case 0:
mipi_val = &regs->saveMIPI;
break;
case 1:
mipi_val = &regs->saveMIPI;
break;
case 2:
/* register */
mipi_reg = MIPI_C;
/* pointer to values */
mipi_val = &regs->saveMIPI_C;
break;
default:
DRM_ERROR("%s, invalid pipe number.\n", __func__);
return -EINVAL;
}
/* Pipe & plane A info */
pipe->dpll = PSB_RVDC32(map->dpll);
pipe->fp0 = PSB_RVDC32(map->fp0);
pipe->conf = PSB_RVDC32(map->conf);
pipe->htotal = PSB_RVDC32(map->htotal);
pipe->hblank = PSB_RVDC32(map->hblank);
pipe->hsync = PSB_RVDC32(map->hsync);
pipe->vtotal = PSB_RVDC32(map->vtotal);
pipe->vblank = PSB_RVDC32(map->vblank);
pipe->vsync = PSB_RVDC32(map->vsync);
pipe->src = PSB_RVDC32(map->src);
pipe->stride = PSB_RVDC32(map->stride);
pipe->linoff = PSB_RVDC32(map->linoff);
pipe->tileoff = PSB_RVDC32(map->tileoff);
pipe->size = PSB_RVDC32(map->size);
pipe->pos = PSB_RVDC32(map->pos);
pipe->surf = PSB_RVDC32(map->surf);
pipe->cntr = PSB_RVDC32(map->cntr);
pipe->status = PSB_RVDC32(map->status);
/*save palette (gamma) */
for (i = 0; i < 256; i++)
pipe->palette[i] = PSB_RVDC32(map->palette + (i << 2));
if (pipenum == 1) {
regs->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL);
regs->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS);
regs->saveHDMIPHYMISCCTL = PSB_RVDC32(HDMIPHYMISCCTL);
regs->saveHDMIB_CONTROL = PSB_RVDC32(HDMIB_CONTROL);
return 0;
}
*mipi_val = PSB_RVDC32(mipi_reg);
return 0;
}
/*
* mdfld_restore_display_registers
*
* Description: We are going to resume so restore display register state.
*
* Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio
*/
static int mdfld_restore_display_registers(struct drm_device *dev, int pipenum)
{
/* To get panel out of ULPS mode. */
u32 temp = 0;
u32 device_ready_reg = DEVICE_READY_REG;
struct drm_psb_private *dev_priv = dev->dev_private;
struct mdfld_dsi_config *dsi_config = NULL;
struct medfield_state *regs = &dev_priv->regs.mdfld;
struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum];
const struct psb_offset *map = &dev_priv->regmap[pipenum];
u32 i;
u32 dpll;
u32 timeout = 0;
/* register */
u32 mipi_reg = MIPI;
/* values */
u32 dpll_val = pipe->dpll;
u32 mipi_val = regs->saveMIPI;
switch (pipenum) {
case 0:
dpll_val &= ~DPLL_VCO_ENABLE;
dsi_config = dev_priv->dsi_configs[0];
break;
case 1:
dpll_val &= ~DPLL_VCO_ENABLE;
break;
case 2:
mipi_reg = MIPI_C;
mipi_val = regs->saveMIPI_C;
dsi_config = dev_priv->dsi_configs[1];
break;
default:
DRM_ERROR("%s, invalid pipe number.\n", __func__);
return -EINVAL;
}
/*make sure VGA plane is off. it initializes to on after reset!*/
PSB_WVDC32(0x80000000, VGACNTRL);
if (pipenum == 1) {
PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, map->dpll);
PSB_RVDC32(map->dpll);
PSB_WVDC32(pipe->fp0, map->fp0);
} else {
dpll = PSB_RVDC32(map->dpll);
if (!(dpll & DPLL_VCO_ENABLE)) {
/* When ungating power of DPLL, needs to wait 0.5us
before enable the VCO */
if (dpll & MDFLD_PWR_GATE_EN) {
dpll &= ~MDFLD_PWR_GATE_EN;
PSB_WVDC32(dpll, map->dpll);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
}
PSB_WVDC32(pipe->fp0, map->fp0);
PSB_WVDC32(dpll_val, map->dpll);
/* FIXME_MDFLD PO - change 500 to 1 after PO */
udelay(500);
dpll_val |= DPLL_VCO_ENABLE;
PSB_WVDC32(dpll_val, map->dpll);
PSB_RVDC32(map->dpll);
/* wait for DSI PLL to lock */
while (timeout < 20000 &&
!(PSB_RVDC32(map->conf) & PIPECONF_DSIPLL_LOCK)) {
udelay(150);
timeout++;
}
if (timeout == 20000) {
DRM_ERROR("%s, can't lock DSIPLL.\n",
__func__);
return -EINVAL;
}
}
}
/* Restore mode */
PSB_WVDC32(pipe->htotal, map->htotal);
PSB_WVDC32(pipe->hblank, map->hblank);
PSB_WVDC32(pipe->hsync, map->hsync);
PSB_WVDC32(pipe->vtotal, map->vtotal);
PSB_WVDC32(pipe->vblank, map->vblank);
PSB_WVDC32(pipe->vsync, map->vsync);
PSB_WVDC32(pipe->src, map->src);
PSB_WVDC32(pipe->status, map->status);
/*set up the plane*/
PSB_WVDC32(pipe->stride, map->stride);
PSB_WVDC32(pipe->linoff, map->linoff);
PSB_WVDC32(pipe->tileoff, map->tileoff);
PSB_WVDC32(pipe->size, map->size);
PSB_WVDC32(pipe->pos, map->pos);
PSB_WVDC32(pipe->surf, map->surf);
if (pipenum == 1) {
/* restore palette (gamma) */
/*DRM_UDELAY(50000); */
for (i = 0; i < 256; i++)
PSB_WVDC32(pipe->palette[i], map->palette + (i << 2));
PSB_WVDC32(regs->savePFIT_CONTROL, PFIT_CONTROL);
PSB_WVDC32(regs->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS);
/*TODO: resume HDMI port */
/*TODO: resume pipe*/
/*enable the plane*/
PSB_WVDC32(pipe->cntr & ~DISPLAY_PLANE_ENABLE, map->cntr);
return 0;
}
/*set up pipe related registers*/
PSB_WVDC32(mipi_val, mipi_reg);
/*setup MIPI adapter + MIPI IP registers*/
if (dsi_config)
mdfld_dsi_controller_init(dsi_config, pipenum);
if (in_atomic() || in_interrupt())
mdelay(20);
else
msleep(20);
/*enable the plane*/
PSB_WVDC32(pipe->cntr, map->cntr);
if (in_atomic() || in_interrupt())
mdelay(20);
else
msleep(20);
/* LP Hold Release */
temp = REG_READ(mipi_reg);
temp |= LP_OUTPUT_HOLD_RELEASE;
REG_WRITE(mipi_reg, temp);
mdelay(1);
/* Set DSI host to exit from Utra Low Power State */
temp = REG_READ(device_ready_reg);
temp &= ~ULPS_MASK;
temp |= 0x3;
temp |= EXIT_ULPS_DEV_READY;
REG_WRITE(device_ready_reg, temp);
mdelay(1);
temp = REG_READ(device_ready_reg);
temp &= ~ULPS_MASK;
temp |= EXITING_ULPS;
REG_WRITE(device_ready_reg, temp);
mdelay(1);
/*enable the pipe*/
PSB_WVDC32(pipe->conf, map->conf);
/* restore palette (gamma) */
/*DRM_UDELAY(50000); */
for (i = 0; i < 256; i++)
PSB_WVDC32(pipe->palette[i], map->palette + (i << 2));
return 0;
}
static int mdfld_save_registers(struct drm_device *dev)
{
/* mdfld_save_cursor_overlay_registers(dev); */
mdfld_save_display_registers(dev, 0);
mdfld_save_display_registers(dev, 2);
mdfld_disable_crtc(dev, 0);
mdfld_disable_crtc(dev, 2);
return 0;
}
static int mdfld_restore_registers(struct drm_device *dev)
{
mdfld_restore_display_registers(dev, 2);
mdfld_restore_display_registers(dev, 0);
/* mdfld_restore_cursor_overlay_registers(dev); */
return 0;
}
static int mdfld_power_down(struct drm_device *dev)
{
/* FIXME */
return 0;
}
static int mdfld_power_up(struct drm_device *dev)
{
/* FIXME */
return 0;
}
/* Medfield */
static const struct psb_offset mdfld_regmap[3] = {
{
.fp0 = MRST_FPA0,
.fp1 = MRST_FPA1,
.cntr = DSPACNTR,
.conf = PIPEACONF,
.src = PIPEASRC,
.dpll = MRST_DPLL_A,
.htotal = HTOTAL_A,
.hblank = HBLANK_A,
.hsync = HSYNC_A,
.vtotal = VTOTAL_A,
.vblank = VBLANK_A,
.vsync = VSYNC_A,
.stride = DSPASTRIDE,
.size = DSPASIZE,
.pos = DSPAPOS,
.surf = DSPASURF,
.addr = MRST_DSPABASE,
.status = PIPEASTAT,
.linoff = DSPALINOFF,
.tileoff = DSPATILEOFF,
.palette = PALETTE_A,
},
{
.fp0 = MDFLD_DPLL_DIV0,
.cntr = DSPBCNTR,
.conf = PIPEBCONF,
.src = PIPEBSRC,
.dpll = MDFLD_DPLL_B,
.htotal = HTOTAL_B,
.hblank = HBLANK_B,
.hsync = HSYNC_B,
.vtotal = VTOTAL_B,
.vblank = VBLANK_B,
.vsync = VSYNC_B,
.stride = DSPBSTRIDE,
.size = DSPBSIZE,
.pos = DSPBPOS,
.surf = DSPBSURF,
.addr = MRST_DSPBBASE,
.status = PIPEBSTAT,
.linoff = DSPBLINOFF,
.tileoff = DSPBTILEOFF,
.palette = PALETTE_B,
},
{
.fp0 = MRST_FPA0, /* This is what the old code did ?? */
.cntr = DSPCCNTR,
.conf = PIPECCONF,
.src = PIPECSRC,
/* No DPLL_C */
.dpll = MRST_DPLL_A,
.htotal = HTOTAL_C,
.hblank = HBLANK_C,
.hsync = HSYNC_C,
.vtotal = VTOTAL_C,
.vblank = VBLANK_C,
.vsync = VSYNC_C,
.stride = DSPCSTRIDE,
.size = DSPBSIZE,
.pos = DSPCPOS,
.surf = DSPCSURF,
.addr = MDFLD_DSPCBASE,
.status = PIPECSTAT,
.linoff = DSPCLINOFF,
.tileoff = DSPCTILEOFF,
.palette = PALETTE_C,
},
};
static int mdfld_chip_setup(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
if (pci_enable_msi(dev->pdev))
dev_warn(dev->dev, "Enabling MSI failed!\n");
dev_priv->regmap = mdfld_regmap;
return mid_chip_setup(dev);
}
const struct psb_ops mdfld_chip_ops = {
.name = "mdfld",
.accel_2d = 0,
.pipes = 3,
.crtcs = 3,
.lvds_mask = (1 << 1),
.hdmi_mask = (1 << 1),
.cursor_needs_phys = 0,
.sgx_offset = MRST_SGX_OFFSET,
.chip_setup = mdfld_chip_setup,
.crtc_helper = &mdfld_helper_funcs,
.crtc_funcs = &psb_intel_crtc_funcs,
.output_init = mdfld_output_init,
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
.backlight_init = mdfld_backlight_init,
#endif
.save_regs = mdfld_save_registers,
.restore_regs = mdfld_restore_registers,
.save_crtc = gma_crtc_save,
.restore_crtc = gma_crtc_restore,
.power_down = mdfld_power_down,
.power_up = mdfld_power_up,
};