diff options
Diffstat (limited to 'drivers/gpu/drm/gma500')
42 files changed, 6890 insertions, 988 deletions
diff --git a/drivers/gpu/drm/gma500/Kconfig b/drivers/gpu/drm/gma500/Kconfig index 754e14bdc801..42e665c7e90a 100644 --- a/drivers/gpu/drm/gma500/Kconfig +++ b/drivers/gpu/drm/gma500/Kconfig @@ -16,8 +16,7 @@ config DRM_GMA600 depends on DRM_GMA500 help Say yes to include support for GMA600 (Intel Moorestown/Oaktrail) - platforms with LVDS ports. HDMI and MIPI are not currently - supported. + platforms with LVDS ports. MIPI is not currently supported. config DRM_GMA3600 bool "Intel GMA3600/3650 support (Experimental)" @@ -25,3 +24,10 @@ config DRM_GMA3600 help Say yes to include basic support for Intel GMA3600/3650 (Intel Cedar Trail) platforms. + +config DRM_MEDFIELD + bool "Intel Medfield support (Experimental)" + depends on DRM_GMA500 && X86_INTEL_MID + help + Say yes to include support for the Intel Medfield platform. + diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile index 81c103be5e21..1583982917ce 100644 --- a/drivers/gpu/drm/gma500/Makefile +++ b/drivers/gpu/drm/gma500/Makefile @@ -37,4 +37,14 @@ gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \ oaktrail_hdmi.o \ oaktrail_hdmi_i2c.o +gma500_gfx-$(CONFIG_DRM_MEDFIELD) += mdfld_device.o \ + mdfld_output.o \ + mdfld_intel_display.o \ + mdfld_dsi_output.o \ + mdfld_dsi_dpi.o \ + mdfld_dsi_pkg_sender.o \ + mdfld_tpo_vid.o \ + mdfld_tmd_vid.o \ + tc35876x-dsi-lvds.o + obj-$(CONFIG_DRM_GMA500) += gma500_gfx.o diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index 4a5b099c3bc5..a54cc738926a 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -202,13 +202,12 @@ static inline void CDV_MSG_WRITE32(uint port, uint offset, u32 value) pci_dev_put(pci_root); } -#define PSB_APM_CMD 0x0 -#define PSB_APM_STS 0x04 #define PSB_PM_SSC 0x20 #define PSB_PM_SSS 0x30 -#define PSB_PWRGT_GFX_MASK 0x3 -#define CDV_PWRGT_DISPLAY_CNTR 0x000fc00c -#define CDV_PWRGT_DISPLAY_STS 0x000fc00c +#define PSB_PWRGT_GFX_ON 0x02 +#define PSB_PWRGT_GFX_OFF 0x01 +#define PSB_PWRGT_GFX_D0 0x00 +#define PSB_PWRGT_GFX_D3 0x03 static void cdv_init_pm(struct drm_device *dev) { @@ -221,26 +220,22 @@ static void cdv_init_pm(struct drm_device *dev) dev_priv->ospm_base = CDV_MSG_READ32(PSB_PUNIT_PORT, PSB_OSPMBA) & 0xFFFF; - /* Force power on for now */ + /* Power status */ pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD); - pwr_cnt &= ~PSB_PWRGT_GFX_MASK; + /* Enable the GPU */ + pwr_cnt &= ~PSB_PWRGT_GFX_MASK; + pwr_cnt |= PSB_PWRGT_GFX_ON; outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD); + + /* Wait for the GPU power */ for (i = 0; i < 5; i++) { u32 pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS); if ((pwr_sts & PSB_PWRGT_GFX_MASK) == 0) - break; - udelay(10); - } - pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC); - pwr_cnt &= ~CDV_PWRGT_DISPLAY_CNTR; - outl(pwr_cnt, dev_priv->ospm_base + PSB_PM_SSC); - for (i = 0; i < 5; i++) { - u32 pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS); - if ((pwr_sts & CDV_PWRGT_DISPLAY_STS) == 0) - break; + return; udelay(10); } + dev_err(dev->dev, "GPU: power management timed out.\n"); } /** @@ -249,11 +244,50 @@ static void cdv_init_pm(struct drm_device *dev) * * Save the state we need in order to be able to restore the interface * upon resume from suspend - * - * FIXME: review */ static int cdv_save_display_registers(struct drm_device *dev) { + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_save_area *regs = &dev_priv->regs; + struct drm_connector *connector; + + dev_info(dev->dev, "Saving GPU registers.\n"); + + pci_read_config_byte(dev->pdev, 0xF4, ®s->cdv.saveLBB); + + regs->cdv.saveDSPCLK_GATE_D = REG_READ(DSPCLK_GATE_D); + regs->cdv.saveRAMCLK_GATE_D = REG_READ(RAMCLK_GATE_D); + + regs->cdv.saveDSPARB = REG_READ(DSPARB); + regs->cdv.saveDSPFW[0] = REG_READ(DSPFW1); + regs->cdv.saveDSPFW[1] = REG_READ(DSPFW2); + regs->cdv.saveDSPFW[2] = REG_READ(DSPFW3); + regs->cdv.saveDSPFW[3] = REG_READ(DSPFW4); + regs->cdv.saveDSPFW[4] = REG_READ(DSPFW5); + regs->cdv.saveDSPFW[5] = REG_READ(DSPFW6); + + regs->cdv.saveADPA = REG_READ(ADPA); + + regs->cdv.savePP_CONTROL = REG_READ(PP_CONTROL); + regs->cdv.savePFIT_PGM_RATIOS = REG_READ(PFIT_PGM_RATIOS); + regs->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); + regs->saveBLC_PWM_CTL2 = REG_READ(BLC_PWM_CTL2); + regs->cdv.saveLVDS = REG_READ(LVDS); + + regs->cdv.savePFIT_CONTROL = REG_READ(PFIT_CONTROL); + + regs->cdv.savePP_ON_DELAYS = REG_READ(PP_ON_DELAYS); + regs->cdv.savePP_OFF_DELAYS = REG_READ(PP_OFF_DELAYS); + regs->cdv.savePP_CYCLE = REG_READ(PP_CYCLE); + + regs->cdv.saveVGACNTRL = REG_READ(VGACNTRL); + + regs->cdv.saveIER = REG_READ(PSB_INT_ENABLE_R); + regs->cdv.saveIMR = REG_READ(PSB_INT_MASK_R); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF); + return 0; } @@ -267,16 +301,113 @@ static int cdv_save_display_registers(struct drm_device *dev) */ static int cdv_restore_display_registers(struct drm_device *dev) { + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_save_area *regs = &dev_priv->regs; + struct drm_connector *connector; + u32 temp; + + pci_write_config_byte(dev->pdev, 0xF4, regs->cdv.saveLBB); + + REG_WRITE(DSPCLK_GATE_D, regs->cdv.saveDSPCLK_GATE_D); + REG_WRITE(RAMCLK_GATE_D, regs->cdv.saveRAMCLK_GATE_D); + + /* BIOS does below anyway */ + REG_WRITE(DPIO_CFG, 0); + REG_WRITE(DPIO_CFG, DPIO_MODE_SELECT_0 | DPIO_CMN_RESET_N); + + temp = REG_READ(DPLL_A); + if ((temp & DPLL_SYNCLOCK_ENABLE) == 0) { + REG_WRITE(DPLL_A, temp | DPLL_SYNCLOCK_ENABLE); + REG_READ(DPLL_A); + } + + temp = REG_READ(DPLL_B); + if ((temp & DPLL_SYNCLOCK_ENABLE) == 0) { + REG_WRITE(DPLL_B, temp | DPLL_SYNCLOCK_ENABLE); + REG_READ(DPLL_B); + } + + udelay(500); + + REG_WRITE(DSPFW1, regs->cdv.saveDSPFW[0]); + REG_WRITE(DSPFW2, regs->cdv.saveDSPFW[1]); + REG_WRITE(DSPFW3, regs->cdv.saveDSPFW[2]); + REG_WRITE(DSPFW4, regs->cdv.saveDSPFW[3]); + REG_WRITE(DSPFW5, regs->cdv.saveDSPFW[4]); + REG_WRITE(DSPFW6, regs->cdv.saveDSPFW[5]); + + REG_WRITE(DSPARB, regs->cdv.saveDSPARB); + REG_WRITE(ADPA, regs->cdv.saveADPA); + + REG_WRITE(BLC_PWM_CTL2, regs->saveBLC_PWM_CTL2); + REG_WRITE(LVDS, regs->cdv.saveLVDS); + REG_WRITE(PFIT_CONTROL, regs->cdv.savePFIT_CONTROL); + REG_WRITE(PFIT_PGM_RATIOS, regs->cdv.savePFIT_PGM_RATIOS); + REG_WRITE(BLC_PWM_CTL, regs->saveBLC_PWM_CTL); + REG_WRITE(PP_ON_DELAYS, regs->cdv.savePP_ON_DELAYS); + REG_WRITE(PP_OFF_DELAYS, regs->cdv.savePP_OFF_DELAYS); + REG_WRITE(PP_CYCLE, regs->cdv.savePP_CYCLE); + REG_WRITE(PP_CONTROL, regs->cdv.savePP_CONTROL); + + REG_WRITE(VGACNTRL, regs->cdv.saveVGACNTRL); + + REG_WRITE(PSB_INT_ENABLE_R, regs->cdv.saveIER); + REG_WRITE(PSB_INT_MASK_R, regs->cdv.saveIMR); + + /* Fix arbitration bug */ + CDV_MSG_WRITE32(3, 0x30, 0x08027108); + + drm_mode_config_reset(dev); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) + connector->funcs->dpms(connector, DRM_MODE_DPMS_ON); + + /* Resume the modeset for every activated CRTC */ + drm_helper_resume_force_mode(dev); return 0; } static int cdv_power_down(struct drm_device *dev) { + struct drm_psb_private *dev_priv = dev->dev_private; + u32 pwr_cnt, pwr_mask, pwr_sts; + int tries = 5; + + pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD); + pwr_cnt &= ~PSB_PWRGT_GFX_MASK; + pwr_cnt |= PSB_PWRGT_GFX_OFF; + pwr_mask = PSB_PWRGT_GFX_MASK; + + outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD); + + while (tries--) { + pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS); + if ((pwr_sts & pwr_mask) == PSB_PWRGT_GFX_D3) + return 0; + udelay(10); + } return 0; } static int cdv_power_up(struct drm_device *dev) { + struct drm_psb_private *dev_priv = dev->dev_private; + u32 pwr_cnt, pwr_mask, pwr_sts; + int tries = 5; + + pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD); + pwr_cnt &= ~PSB_PWRGT_GFX_MASK; + pwr_cnt |= PSB_PWRGT_GFX_ON; + pwr_mask = PSB_PWRGT_GFX_MASK; + + outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD); + + while (tries--) { + pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS); + if ((pwr_sts & pwr_mask) == PSB_PWRGT_GFX_D0) + return 0; + udelay(10); + } return 0; } @@ -321,6 +452,8 @@ static int cdv_chip_setup(struct drm_device *dev) cdv_get_core_freq(dev); gma_intel_opregion_init(dev); psb_intel_init_bios(dev); + REG_WRITE(PORT_HOTPLUG_EN, 0); + REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT)); return 0; } diff --git a/drivers/gpu/drm/gma500/cdv_device.h b/drivers/gpu/drm/gma500/cdv_device.h index 2a88b7beb551..9561e17621b3 100644 --- a/drivers/gpu/drm/gma500/cdv_device.h +++ b/drivers/gpu/drm/gma500/cdv_device.h @@ -26,7 +26,7 @@ extern void cdv_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device * extern struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev, struct drm_crtc *crtc); -extern inline void cdv_intel_wait_for_vblank(struct drm_device *dev) +static inline void cdv_intel_wait_for_vblank(struct drm_device *dev) { /* Wait for 20ms, i.e. one cycle at 50hz. */ /* FIXME: msleep ?? */ diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c index c100f3e9c920..a71a6cd95bdd 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_crt.c +++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c @@ -32,6 +32,7 @@ #include "psb_intel_drv.h" #include "psb_intel_reg.h" #include "power.h" +#include "cdv_device.h" #include <linux/pm_runtime.h> diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 18d11525095e..be8455919b33 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -344,7 +344,7 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, /* * Returns whether any encoder on the specified pipe is of the specified type */ -bool cdv_intel_pipe_has_type(struct drm_crtc *crtc, int type) +static bool cdv_intel_pipe_has_type(struct drm_crtc *crtc, int type) { struct drm_device *dev = crtc->dev; struct drm_mode_config *mode_config = &dev->mode_config; @@ -476,7 +476,7 @@ static bool cdv_intel_find_best_PLL(struct drm_crtc *crtc, int target, return err != target; } -int cdv_intel_pipe_set_base(struct drm_crtc *crtc, +static int cdv_intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; @@ -569,7 +569,6 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; u32 temp; - bool enabled; /* XXX: When our outputs are all unaware of DPMS modes other than off * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. @@ -663,7 +662,6 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) udelay(150); break; } - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; /*Set FIFO Watermarks*/ REG_WRITE(DSPARB, 0x3F3E); } @@ -680,22 +678,6 @@ static void cdv_intel_crtc_commit(struct drm_crtc *crtc) crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); } -void cdv_intel_encoder_prepare(struct drm_encoder *encoder) -{ - struct drm_encoder_helper_funcs *encoder_funcs = - encoder->helper_private; - /* lvds has its own version of prepare see cdv_intel_lvds_prepare */ - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); -} - -void cdv_intel_encoder_commit(struct drm_encoder *encoder) -{ - struct drm_encoder_helper_funcs *encoder_funcs = - encoder->helper_private; - /* lvds has its own version of commit see cdv_intel_lvds_commit */ - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); -} - static bool cdv_intel_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -745,7 +727,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, int refclk; struct cdv_intel_clock_t clock; u32 dpll = 0, dspcntr, pipeconf; - bool ok, is_sdvo = false, is_dvo = false; + bool ok; bool is_crt = false, is_lvds = false, is_tv = false; bool is_hdmi = false; struct drm_mode_config *mode_config = &dev->mode_config; @@ -763,12 +745,6 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_LVDS: is_lvds = true; break; - case INTEL_OUTPUT_SDVO: - is_sdvo = true; - break; - case INTEL_OUTPUT_DVO: - is_dvo = true; - break; case INTEL_OUTPUT_TVOUT: is_tv = true; break; @@ -928,7 +904,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, } /** Loads the palette/gamma unit for the CRTC with the prepared values */ -void cdv_intel_crtc_load_lut(struct drm_crtc *crtc) +static void cdv_intel_crtc_load_lut(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_psb_private *dev_priv = @@ -968,7 +944,7 @@ void cdv_intel_crtc_load_lut(struct drm_crtc *crtc) gma_power_end(dev); } else { for (i = 0; i < 256; i++) { - dev_priv->save_palette_a[i] = + dev_priv->regs.psb.save_palette_a[i] = ((psb_intel_crtc->lut_r[i] + psb_intel_crtc->lut_adj[i]) << 16) | ((psb_intel_crtc->lut_g[i] + @@ -1338,18 +1314,20 @@ static int cdv_intel_crtc_clock_get(struct drm_device *dev, gma_power_end(dev); } else { dpll = (pipe == 0) ? - dev_priv->saveDPLL_A : dev_priv->saveDPLL_B; + dev_priv->regs.psb.saveDPLL_A : + dev_priv->regs.psb.saveDPLL_B; if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) fp = (pipe == 0) ? - dev_priv->saveFPA0 : - dev_priv->saveFPB0; + dev_priv->regs.psb.saveFPA0 : + dev_priv->regs.psb.saveFPB0; else fp = (pipe == 0) ? - dev_priv->saveFPA1 : - dev_priv->saveFPB1; + dev_priv->regs.psb.saveFPA1 : + dev_priv->regs.psb.saveFPB1; - is_lvds = (pipe == 1) && (dev_priv->saveLVDS & LVDS_PORT_EN); + is_lvds = (pipe == 1) && + (dev_priv->regs.psb.saveLVDS & LVDS_PORT_EN); } clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; @@ -1419,13 +1397,17 @@ struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev, gma_power_end(dev); } else { htot = (pipe == 0) ? - dev_priv->saveHTOTAL_A : dev_priv->saveHTOTAL_B; + dev_priv->regs.psb.saveHTOTAL_A : + dev_priv->regs.psb.saveHTOTAL_B; hsync = (pipe == 0) ? - dev_priv->saveHSYNC_A : dev_priv->saveHSYNC_B; + dev_priv->regs.psb.saveHSYNC_A : + dev_priv->regs.psb.saveHSYNC_B; vtot = (pipe == 0) ? - dev_priv->saveVTOTAL_A : dev_priv->saveVTOTAL_B; + dev_priv->regs.psb.saveVTOTAL_A : + dev_priv->regs.psb.saveVTOTAL_B; vsync = (pipe == 0) ? - dev_priv->saveVSYNC_A : dev_priv->saveVSYNC_B; + dev_priv->regs.psb.saveVSYNC_A : + dev_priv->regs.psb.saveVSYNC_B; } mode = kzalloc(sizeof(*mode), GFP_KERNEL); @@ -1475,34 +1457,3 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = { .set_config = cdv_crtc_set_config, .destroy = cdv_intel_crtc_destroy, }; - -/* - * Set the default value of cursor control and base register - * to zero. This is a workaround for h/w defect on oaktrail - */ -void cdv_intel_cursor_init(struct drm_device *dev, int pipe) -{ - uint32_t control; - uint32_t base; - - switch (pipe) { - case 0: - control = CURACNTR; - base = CURABASE; - break; - case 1: - control = CURBCNTR; - base = CURBBASE; - break; - case 2: - control = CURCCNTR; - base = CURCBASE; - break; - default: - return; - } - - REG_WRITE(control, 0); - REG_WRITE(base, 0); -} - diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index de25560e629d..8d5269555005 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -34,6 +34,7 @@ #include "psb_intel_drv.h" #include "psb_drv.h" #include "psb_intel_reg.h" +#include "cdv_device.h" #include <linux/pm_runtime.h> /* hdmi control bits */ diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 50e744be9852..8359c1a3f45f 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -78,13 +78,14 @@ static u32 cdv_intel_lvds_get_max_backlight(struct drm_device *dev) gma_power_end(dev); } else - retval = ((dev_priv->saveBLC_PWM_CTL & + retval = ((dev_priv->regs.saveBLC_PWM_CTL & BACKLIGHT_MODULATION_FREQ_MASK) >> BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; return retval; } +#if 0 /* * Set LVDS backlight level by I2C command */ @@ -165,6 +166,7 @@ void cdv_intel_lvds_set_brightness(struct drm_device *dev, int level) else cdv_lvds_pwm_set_brightness(dev, level); } +#endif /** * Sets the backlight level. @@ -184,9 +186,9 @@ static void cdv_intel_lvds_set_backlight(struct drm_device *dev, int level) (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); gma_power_end(dev); } else { - blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL & + blc_pwm_ctl = dev_priv->regs.saveBLC_PWM_CTL & ~BACKLIGHT_DUTY_CYCLE_MASK; - dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl | + dev_priv->regs.saveBLC_PWM_CTL = (blc_pwm_ctl | (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); } } @@ -242,7 +244,7 @@ static void cdv_intel_lvds_restore(struct drm_connector *connector) { } -int cdv_intel_lvds_mode_valid(struct drm_connector *connector, +static int cdv_intel_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct drm_device *dev = connector->dev; @@ -267,7 +269,7 @@ int cdv_intel_lvds_mode_valid(struct drm_connector *connector, return MODE_OK; } -bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder, +static bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { @@ -436,7 +438,7 @@ static int cdv_intel_lvds_get_modes(struct drm_connector *connector) * Unregister the DDC bus for this connector then free the driver private * structure. */ -void cdv_intel_lvds_destroy(struct drm_connector *connector) +static void cdv_intel_lvds_destroy(struct drm_connector *connector) { struct psb_intel_encoder *psb_intel_encoder = psb_intel_attached_encoder(connector); @@ -448,7 +450,7 @@ void cdv_intel_lvds_destroy(struct drm_connector *connector) kfree(connector); } -int cdv_intel_lvds_set_property(struct drm_connector *connector, +static int cdv_intel_lvds_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t value) { diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 830dfdd6bf15..8ea202f1ba50 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -111,39 +111,6 @@ static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info) return 0; } -void psbfb_suspend(struct drm_device *dev) -{ - struct drm_framebuffer *fb; - - console_lock(); - mutex_lock(&dev->mode_config.mutex); - list_for_each_entry(fb, &dev->mode_config.fb_list, head) { - struct psb_framebuffer *psbfb = to_psb_fb(fb); - struct fb_info *info = psbfb->fbdev; - fb_set_suspend(info, 1); - drm_fb_helper_blank(FB_BLANK_POWERDOWN, info); - } - mutex_unlock(&dev->mode_config.mutex); - console_unlock(); -} - -void psbfb_resume(struct drm_device *dev) -{ - struct drm_framebuffer *fb; - - console_lock(); - mutex_lock(&dev->mode_config.mutex); - list_for_each_entry(fb, &dev->mode_config.fb_list, head) { - struct psb_framebuffer *psbfb = to_psb_fb(fb); - struct fb_info *info = psbfb->fbdev; - fb_set_suspend(info, 0); - drm_fb_helper_blank(FB_BLANK_UNBLANK, info); - } - mutex_unlock(&dev->mode_config.mutex); - console_unlock(); - drm_helper_disable_unused_functions(dev); -} - static int psbfb_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct psb_framebuffer *psbfb = vma->vm_private_data; @@ -158,7 +125,7 @@ static int psbfb_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) unsigned long phys_addr = (unsigned long)dev_priv->stolen_base; page_num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; - address = (unsigned long)vmf->virtual_address; + address = (unsigned long)vmf->virtual_address - (vmf->pgoff << PAGE_SHIFT); vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); @@ -247,7 +214,6 @@ static struct fb_ops psbfb_roll_ops = { .fb_imageblit = cfb_imageblit, .fb_pan_display = psbfb_pan, .fb_mmap = psbfb_mmap, - .fb_sync = psbfb_sync, .fb_ioctl = psbfb_ioctl, }; @@ -391,6 +357,7 @@ static int psbfb_create(struct psb_fbdev *fbdev, mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; bpp = sizes->surface_bpp; + depth = sizes->surface_depth; /* No 24bit packed */ if (bpp == 24) @@ -403,7 +370,6 @@ static int psbfb_create(struct psb_fbdev *fbdev, * is ok with some fonts */ mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 4096 >> pitch_lines); - depth = sizes->surface_depth; size = mode_cmd.pitches[0] * mode_cmd.height; size = ALIGN(size, PAGE_SIZE); @@ -463,6 +429,7 @@ static int psbfb_create(struct psb_fbdev *fbdev, fbdev->psb_fb_helper.fb = fb; fbdev->psb_fb_helper.fbdev = info; + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); strcpy(info->fix.id, "psbfb"); info->flags = FBINFO_DEFAULT; @@ -500,18 +467,13 @@ static int psbfb_create(struct psb_fbdev *fbdev, info->apertures->ranges[0].size = dev_priv->gtt.stolen_size; } - drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(info, &fbdev->psb_fb_helper, sizes->fb_width, sizes->fb_height); info->fix.mmio_start = pci_resource_start(dev->pdev, 0); info->fix.mmio_len = pci_resource_len(dev->pdev, 0); - info->pixmap.size = 64 * 1024; - info->pixmap.buf_align = 8; - info->pixmap.access_align = 32; - info->pixmap.flags = FB_PIXMAP_SYSTEM; - info->pixmap.scan_align = 1; + /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ dev_info(dev->dev, "allocated %dx%d fb\n", psbfb->base.width, psbfb->base.height); @@ -560,11 +522,21 @@ static struct drm_framebuffer *psb_user_framebuffer_create static void psbfb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, int regno) { + struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); + + intel_crtc->lut_r[regno] = red >> 8; + intel_crtc->lut_g[regno] = green >> 8; + intel_crtc->lut_b[regno] = blue >> 8; } static void psbfb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, int regno) { + struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); + + *red = intel_crtc->lut_r[regno] << 8; + *green = intel_crtc->lut_g[regno] << 8; + *blue = intel_crtc->lut_b[regno] << 8; } static int psbfb_probe(struct drm_fb_helper *helper, @@ -589,7 +561,7 @@ struct drm_fb_helper_funcs psb_fb_helper_funcs = { .fb_probe = psbfb_probe, }; -int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev) +static int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev) { struct fb_info *info; struct psb_framebuffer *psbfb = &fbdev->pfb; @@ -631,7 +603,7 @@ int psb_fbdev_init(struct drm_device *dev) return 0; } -void psb_fbdev_fini(struct drm_device *dev) +static void psb_fbdev_fini(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; @@ -725,10 +697,7 @@ static int psb_create_backlight_property(struct drm_device *dev) if (dev_priv->backlight_property) return 0; - backlight = drm_property_create(dev, DRM_MODE_PROP_RANGE, - "backlight", 2); - backlight->values[0] = 0; - backlight->values[1] = 100; + backlight = drm_property_create_range(dev, 0, "backlight", 0, 100); dev_priv->backlight_property = backlight; diff --git a/drivers/gpu/drm/gma500/gem_glue.c b/drivers/gpu/drm/gma500/gem_glue.c index daac12120653..3c17634f6061 100644 --- a/drivers/gpu/drm/gma500/gem_glue.c +++ b/drivers/gpu/drm/gma500/gem_glue.c @@ -19,6 +19,7 @@ #include <drm/drmP.h> #include <drm/drm.h> +#include "gem_glue.h" void drm_gem_object_release_wrap(struct drm_gem_object *obj) { diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 5d5330f667f1..c6465b40090f 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -57,7 +57,7 @@ static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type) * Given a gtt_range object return the GTT offset of the page table * entries for this gtt_range */ -u32 *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r) +static u32 *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r) { struct drm_psb_private *dev_priv = dev->dev_private; unsigned long offset; @@ -378,7 +378,7 @@ void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt) kfree(gt); } -void psb_gtt_alloc(struct drm_device *dev) +static void psb_gtt_alloc(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; init_rwsem(&dev_priv->gtt.sem); @@ -446,10 +446,9 @@ int psb_gtt_init(struct drm_device *dev, int resume) pg->gtt_start = pci_resource_start(dev->pdev, PSB_GTT_RESOURCE); gtt_pages = pci_resource_len(dev->pdev, PSB_GTT_RESOURCE) >> PAGE_SHIFT; - /* Some CDV firmware doesn't report this currently. In which case the - system has 64 gtt pages */ + /* CDV doesn't report this. In which case the system has 64 gtt pages */ if (pg->gtt_start == 0 || gtt_pages == 0) { - dev_err(dev->dev, "GTT PCI BAR not initialized.\n"); + dev_dbg(dev->dev, "GTT PCI BAR not initialized.\n"); gtt_pages = 64; pg->gtt_start = dev_priv->pge_ctl; } @@ -461,10 +460,10 @@ int psb_gtt_init(struct drm_device *dev, int resume) if (pg->gatt_pages == 0 || pg->gatt_start == 0) { static struct resource fudge; /* Preferably peppermint */ - /* This can occur on CDV SDV systems. Fudge it in this case. + /* This can occur on CDV systems. Fudge it in this case. We really don't care what imaginary space is being allocated at this point */ - dev_err(dev->dev, "GATT PCI BAR not initialized.\n"); + dev_dbg(dev->dev, "GATT PCI BAR not initialized.\n"); pg->gatt_start = 0x40000000; pg->gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT; /* This is a little confusing but in fact the GTT is providing diff --git a/drivers/gpu/drm/gma500/intel_gmbus.c b/drivers/gpu/drm/gma500/intel_gmbus.c index 147584ac8d02..9db90527bf0f 100644 --- a/drivers/gpu/drm/gma500/intel_gmbus.c +++ b/drivers/gpu/drm/gma500/intel_gmbus.c @@ -395,7 +395,7 @@ int gma_intel_setup_gmbus(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; int ret, i; - dev_priv->gmbus = kcalloc(sizeof(struct intel_gmbus), GMBUS_NUM_PORTS, + dev_priv->gmbus = kcalloc(GMBUS_NUM_PORTS, sizeof(struct intel_gmbus), GFP_KERNEL); if (dev_priv->gmbus == NULL) return -ENOMEM; diff --git a/drivers/gpu/drm/gma500/mdfld_device.c b/drivers/gpu/drm/gma500/mdfld_device.c new file mode 100644 index 000000000000..af656787db0f --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_device.c @@ -0,0 +1,691 @@ +/************************************************************************** + * 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 pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct medfield_state *regs = &dev_priv->regs.mdfld; + int i; + + /* register */ + u32 dpll_reg = MRST_DPLL_A; + u32 fp_reg = MRST_FPA0; + u32 pipeconf_reg = PIPEACONF; + u32 htot_reg = HTOTAL_A; + u32 hblank_reg = HBLANK_A; + u32 hsync_reg = HSYNC_A; + u32 vtot_reg = VTOTAL_A; + u32 vblank_reg = VBLANK_A; + u32 vsync_reg = VSYNC_A; + u32 pipesrc_reg = PIPEASRC; + u32 dspstride_reg = DSPASTRIDE; + u32 dsplinoff_reg = DSPALINOFF; + u32 dsptileoff_reg = DSPATILEOFF; + u32 dspsize_reg = DSPASIZE; + u32 dsppos_reg = DSPAPOS; + u32 dspsurf_reg = DSPASURF; + u32 mipi_reg = MIPI; + u32 dspcntr_reg = DSPACNTR; + u32 dspstatus_reg = PIPEASTAT; + u32 palette_reg = PALETTE_A; + + /* pointer to values */ + u32 *dpll_val = ®s->saveDPLL_A; + u32 *fp_val = ®s->saveFPA0; + u32 *pipeconf_val = ®s->savePIPEACONF; + u32 *htot_val = ®s->saveHTOTAL_A; + u32 *hblank_val = ®s->saveHBLANK_A; + u32 *hsync_val = ®s->saveHSYNC_A; + u32 *vtot_val = ®s->saveVTOTAL_A; + u32 *vblank_val = ®s->saveVBLANK_A; + u32 *vsync_val = ®s->saveVSYNC_A; + u32 *pipesrc_val = ®s->savePIPEASRC; + u32 *dspstride_val = ®s->saveDSPASTRIDE; + u32 *dsplinoff_val = ®s->saveDSPALINOFF; + u32 *dsptileoff_val = ®s->saveDSPATILEOFF; + u32 *dspsize_val = ®s->saveDSPASIZE; + u32 *dsppos_val = ®s->saveDSPAPOS; + u32 *dspsurf_val = ®s->saveDSPASURF; + u32 *mipi_val = ®s->saveMIPI; + u32 *dspcntr_val = ®s->saveDSPACNTR; + u32 *dspstatus_val = ®s->saveDSPASTATUS; + u32 *palette_val = regs->save_palette_a; + + switch (pipe) { + case 0: + break; + case 1: + /* regester */ + dpll_reg = MDFLD_DPLL_B; + fp_reg = MDFLD_DPLL_DIV0; + pipeconf_reg = PIPEBCONF; + htot_reg = HTOTAL_B; + hblank_reg = HBLANK_B; + hsync_reg = HSYNC_B; + vtot_reg = VTOTAL_B; + vblank_reg = VBLANK_B; + vsync_reg = VSYNC_B; + pipesrc_reg = PIPEBSRC; + dspstride_reg = DSPBSTRIDE; + dsplinoff_reg = DSPBLINOFF; + dsptileoff_reg = DSPBTILEOFF; + dspsize_reg = DSPBSIZE; + dsppos_reg = DSPBPOS; + dspsurf_reg = DSPBSURF; + dspcntr_reg = DSPBCNTR; + dspstatus_reg = PIPEBSTAT; + palette_reg = PALETTE_B; + + /* values */ + dpll_val = ®s->saveDPLL_B; + fp_val = ®s->saveFPB0; + pipeconf_val = ®s->savePIPEBCONF; + htot_val = ®s->saveHTOTAL_B; + hblank_val = ®s->saveHBLANK_B; + hsync_val = ®s->saveHSYNC_B; + vtot_val = ®s->saveVTOTAL_B; + vblank_val = ®s->saveVBLANK_B; + vsync_val = ®s->saveVSYNC_B; + pipesrc_val = ®s->savePIPEBSRC; + dspstride_val = ®s->saveDSPBSTRIDE; + dsplinoff_val = ®s->saveDSPBLINOFF; + dsptileoff_val = ®s->saveDSPBTILEOFF; + dspsize_val = ®s->saveDSPBSIZE; + dsppos_val = ®s->saveDSPBPOS; + dspsurf_val = ®s->saveDSPBSURF; + dspcntr_val = ®s->saveDSPBCNTR; + dspstatus_val = ®s->saveDSPBSTATUS; + palette_val = regs->save_palette_b; + break; + case 2: + /* register */ + pipeconf_reg = PIPECCONF; + htot_reg = HTOTAL_C; + hblank_reg = HBLANK_C; + hsync_reg = HSYNC_C; + vtot_reg = VTOTAL_C; + vblank_reg = VBLANK_C; + vsync_reg = VSYNC_C; + pipesrc_reg = PIPECSRC; + dspstride_reg = DSPCSTRIDE; + dsplinoff_reg = DSPCLINOFF; + dsptileoff_reg = DSPCTILEOFF; + dspsize_reg = DSPCSIZE; + dsppos_reg = DSPCPOS; + dspsurf_reg = DSPCSURF; + mipi_reg = MIPI_C; + dspcntr_reg = DSPCCNTR; + dspstatus_reg = PIPECSTAT; + palette_reg = PALETTE_C; + + /* pointer to values */ + pipeconf_val = ®s->savePIPECCONF; + htot_val = ®s->saveHTOTAL_C; + hblank_val = ®s->saveHBLANK_C; + hsync_val = ®s->saveHSYNC_C; + vtot_val = ®s->saveVTOTAL_C; + vblank_val = ®s->saveVBLANK_C; + vsync_val = ®s->saveVSYNC_C; + pipesrc_val = ®s->savePIPECSRC; + dspstride_val = ®s->saveDSPCSTRIDE; + dsplinoff_val = ®s->saveDSPCLINOFF; + dsptileoff_val = ®s->saveDSPCTILEOFF; + dspsize_val = ®s->saveDSPCSIZE; + dsppos_val = ®s->saveDSPCPOS; + dspsurf_val = ®s->saveDSPCSURF; + mipi_val = ®s->saveMIPI_C; + dspcntr_val = ®s->saveDSPCCNTR; + dspstatus_val = ®s->saveDSPCSTATUS; + palette_val = regs->save_palette_c; + break; + default: + DRM_ERROR("%s, invalid pipe number.\n", __func__); + return -EINVAL; + } + + /* Pipe & plane A info */ + *dpll_val = PSB_RVDC32(dpll_reg); + *fp_val = PSB_RVDC32(fp_reg); + *pipeconf_val = PSB_RVDC32(pipeconf_reg); + *htot_val = PSB_RVDC32(htot_reg); + *hblank_val = PSB_RVDC32(hblank_reg); + *hsync_val = PSB_RVDC32(hsync_reg); + *vtot_val = PSB_RVDC32(vtot_reg); + *vblank_val = PSB_RVDC32(vblank_reg); + *vsync_val = PSB_RVDC32(vsync_reg); + *pipesrc_val = PSB_RVDC32(pipesrc_reg); + *dspstride_val = PSB_RVDC32(dspstride_reg); + *dsplinoff_val = PSB_RVDC32(dsplinoff_reg); + *dsptileoff_val = PSB_RVDC32(dsptileoff_reg); + *dspsize_val = PSB_RVDC32(dspsize_reg); + *dsppos_val = PSB_RVDC32(dsppos_reg); + *dspsurf_val = PSB_RVDC32(dspsurf_reg); + *dspcntr_val = PSB_RVDC32(dspcntr_reg); + *dspstatus_val = PSB_RVDC32(dspstatus_reg); + + /*save palette (gamma) */ + for (i = 0; i < 256; i++) + palette_val[i] = PSB_RVDC32(palette_reg + (i << 2)); + + if (pipe == 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 pipe) +{ + /* 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; + u32 i = 0; + u32 dpll = 0; + u32 timeout = 0; + + /* regester */ + u32 dpll_reg = MRST_DPLL_A; + u32 fp_reg = MRST_FPA0; + u32 pipeconf_reg = PIPEACONF; + u32 htot_reg = HTOTAL_A; + u32 hblank_reg = HBLANK_A; + u32 hsync_reg = HSYNC_A; + u32 vtot_reg = VTOTAL_A; + u32 vblank_reg = VBLANK_A; + u32 vsync_reg = VSYNC_A; + u32 pipesrc_reg = PIPEASRC; + u32 dspstride_reg = DSPASTRIDE; + u32 dsplinoff_reg = DSPALINOFF; + u32 dsptileoff_reg = DSPATILEOFF; + u32 dspsize_reg = DSPASIZE; + u32 dsppos_reg = DSPAPOS; + u32 dspsurf_reg = DSPASURF; + u32 dspstatus_reg = PIPEASTAT; + u32 mipi_reg = MIPI; + u32 dspcntr_reg = DSPACNTR; + u32 palette_reg = PALETTE_A; + + /* values */ + u32 dpll_val = regs->saveDPLL_A & ~DPLL_VCO_ENABLE; + u32 fp_val = regs->saveFPA0; + u32 pipeconf_val = regs->savePIPEACONF; + u32 htot_val = regs->saveHTOTAL_A; + u32 hblank_val = regs->saveHBLANK_A; + u32 hsync_val = regs->saveHSYNC_A; + u32 vtot_val = regs->saveVTOTAL_A; + u32 vblank_val = regs->saveVBLANK_A; + u32 vsync_val = regs->saveVSYNC_A; + u32 pipesrc_val = regs->savePIPEASRC; + u32 dspstride_val = regs->saveDSPASTRIDE; + u32 dsplinoff_val = regs->saveDSPALINOFF; + u32 dsptileoff_val = regs->saveDSPATILEOFF; + u32 dspsize_val = regs->saveDSPASIZE; + u32 dsppos_val = regs->saveDSPAPOS; + u32 dspsurf_val = regs->saveDSPASURF; + u32 dspstatus_val = regs->saveDSPASTATUS; + u32 mipi_val = regs->saveMIPI; + u32 dspcntr_val = regs->saveDSPACNTR; + u32 *palette_val = regs->save_palette_a; + + switch (pipe) { + case 0: + dsi_config = dev_priv->dsi_configs[0]; + break; + case 1: + /* regester */ + dpll_reg = MDFLD_DPLL_B; + fp_reg = MDFLD_DPLL_DIV0; + pipeconf_reg = PIPEBCONF; + htot_reg = HTOTAL_B; + hblank_reg = HBLANK_B; + hsync_reg = HSYNC_B; + vtot_reg = VTOTAL_B; + vblank_reg = VBLANK_B; + vsync_reg = VSYNC_B; + pipesrc_reg = PIPEBSRC; + dspstride_reg = DSPBSTRIDE; + dsplinoff_reg = DSPBLINOFF; + dsptileoff_reg = DSPBTILEOFF; + dspsize_reg = DSPBSIZE; + dsppos_reg = DSPBPOS; + dspsurf_reg = DSPBSURF; + dspcntr_reg = DSPBCNTR; + dspstatus_reg = PIPEBSTAT; + palette_reg = PALETTE_B; + + /* values */ + dpll_val = regs->saveDPLL_B & ~DPLL_VCO_ENABLE; + fp_val = regs->saveFPB0; + pipeconf_val = regs->savePIPEBCONF; + htot_val = regs->saveHTOTAL_B; + hblank_val = regs->saveHBLANK_B; + hsync_val = regs->saveHSYNC_B; + vtot_val = regs->saveVTOTAL_B; + vblank_val = regs->saveVBLANK_B; + vsync_val = regs->saveVSYNC_B; + pipesrc_val = regs->savePIPEBSRC; + dspstride_val = regs->saveDSPBSTRIDE; + dsplinoff_val = regs->saveDSPBLINOFF; + dsptileoff_val = regs->saveDSPBTILEOFF; + dspsize_val = regs->saveDSPBSIZE; + dsppos_val = regs->saveDSPBPOS; + dspsurf_val = regs->saveDSPBSURF; + dspcntr_val = regs->saveDSPBCNTR; + dspstatus_val = regs->saveDSPBSTATUS; + palette_val = regs->save_palette_b; + break; + case 2: + /* regester */ + pipeconf_reg = PIPECCONF; + htot_reg = HTOTAL_C; + hblank_reg = HBLANK_C; + hsync_reg = HSYNC_C; + vtot_reg = VTOTAL_C; + vblank_reg = VBLANK_C; + vsync_reg = VSYNC_C; + pipesrc_reg = PIPECSRC; + dspstride_reg = DSPCSTRIDE; + dsplinoff_reg = DSPCLINOFF; + dsptileoff_reg = DSPCTILEOFF; + dspsize_reg = DSPCSIZE; + dsppos_reg = DSPCPOS; + dspsurf_reg = DSPCSURF; + mipi_reg = MIPI_C; + dspcntr_reg = DSPCCNTR; + dspstatus_reg = PIPECSTAT; + palette_reg = PALETTE_C; + + /* values */ + pipeconf_val = regs->savePIPECCONF; + htot_val = regs->saveHTOTAL_C; + hblank_val = regs->saveHBLANK_C; + hsync_val = regs->saveHSYNC_C; + vtot_val = regs->saveVTOTAL_C; + vblank_val = regs->saveVBLANK_C; + vsync_val = regs->saveVSYNC_C; + pipesrc_val = regs->savePIPECSRC; + dspstride_val = regs->saveDSPCSTRIDE; + dsplinoff_val = regs->saveDSPCLINOFF; + dsptileoff_val = regs->saveDSPCTILEOFF; + dspsize_val = regs->saveDSPCSIZE; + dsppos_val = regs->saveDSPCPOS; + dspsurf_val = regs->saveDSPCSURF; + mipi_val = regs->saveMIPI_C; + dspcntr_val = regs->saveDSPCCNTR; + dspstatus_val = regs->saveDSPCSTATUS; + palette_val = regs->save_palette_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 (pipe == 1) { + PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, dpll_reg); + PSB_RVDC32(dpll_reg); + + PSB_WVDC32(fp_val, fp_reg); + } else { + + dpll = PSB_RVDC32(dpll_reg); + + 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, dpll_reg); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + } + + PSB_WVDC32(fp_val, fp_reg); + PSB_WVDC32(dpll_val, dpll_reg); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + dpll_val |= DPLL_VCO_ENABLE; + PSB_WVDC32(dpll_val, dpll_reg); + PSB_RVDC32(dpll_reg); + + /* wait for DSI PLL to lock */ + while (timeout < 20000 && + !(PSB_RVDC32(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { + udelay(150); + timeout++; + } + + if (timeout == 20000) { + DRM_ERROR("%s, can't lock DSIPLL.\n", + __func__); + return -EINVAL; + } + } + } + /* Restore mode */ + PSB_WVDC32(htot_val, htot_reg); + PSB_WVDC32(hblank_val, hblank_reg); + PSB_WVDC32(hsync_val, hsync_reg); + PSB_WVDC32(vtot_val, vtot_reg); + PSB_WVDC32(vblank_val, vblank_reg); + PSB_WVDC32(vsync_val, vsync_reg); + PSB_WVDC32(pipesrc_val, pipesrc_reg); + PSB_WVDC32(dspstatus_val, dspstatus_reg); + + /*set up the plane*/ + PSB_WVDC32(dspstride_val, dspstride_reg); + PSB_WVDC32(dsplinoff_val, dsplinoff_reg); + PSB_WVDC32(dsptileoff_val, dsptileoff_reg); + PSB_WVDC32(dspsize_val, dspsize_reg); + PSB_WVDC32(dsppos_val, dsppos_reg); + PSB_WVDC32(dspsurf_val, dspsurf_reg); + + if (pipe == 1) { + /* restore palette (gamma) */ + /*DRM_UDELAY(50000); */ + for (i = 0; i < 256; i++) + PSB_WVDC32(palette_val[i], palette_reg + (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(dspcntr_val & ~DISPLAY_PLANE_ENABLE, dspcntr_reg); + + 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, pipe); + + if (in_atomic() || in_interrupt()) + mdelay(20); + else + msleep(20); + + /*enable the plane*/ + PSB_WVDC32(dspcntr_val, dspcntr_reg); + + 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(pipeconf_val, pipeconf_reg); + + /* restore palette (gamma) */ + /*DRM_UDELAY(50000); */ + for (i = 0; i < 256; i++) + PSB_WVDC32(palette_val[i], palette_reg + (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; +} + +const struct psb_ops mdfld_chip_ops = { + .name = "mdfld", + .accel_2d = 0, + .pipes = 3, + .crtcs = 3, + .sgx_offset = MRST_SGX_OFFSET, + + .chip_setup = mid_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, + .power_down = mdfld_power_down, + .power_up = mdfld_power_up, +}; diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_dpi.c b/drivers/gpu/drm/gma500/mdfld_dsi_dpi.c new file mode 100644 index 000000000000..d52358b744a0 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_dpi.c @@ -0,0 +1,1017 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * jim liu <jim.liu@intel.com> + * Jackie Li<yaodong.li@intel.com> + */ + +#include "mdfld_dsi_dpi.h" +#include "mdfld_output.h" +#include "mdfld_dsi_pkg_sender.h" +#include "psb_drv.h" +#include "tc35876x-dsi-lvds.h" + +static void mdfld_dsi_dpi_shut_down(struct mdfld_dsi_dpi_output *output, + int pipe); + +static void mdfld_wait_for_HS_DATA_FIFO(struct drm_device *dev, u32 pipe) +{ + u32 gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe); + int timeout = 0; + + udelay(500); + + /* This will time out after approximately 2+ seconds */ + while ((timeout < 20000) && + (REG_READ(gen_fifo_stat_reg) & DSI_FIFO_GEN_HS_DATA_FULL)) { + udelay(100); + timeout++; + } + + if (timeout == 20000) + DRM_INFO("MIPI: HS Data FIFO was never cleared!\n"); +} + +static void mdfld_wait_for_HS_CTRL_FIFO(struct drm_device *dev, u32 pipe) +{ + u32 gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe); + int timeout = 0; + + udelay(500); + + /* This will time out after approximately 2+ seconds */ + while ((timeout < 20000) && (REG_READ(gen_fifo_stat_reg) + & DSI_FIFO_GEN_HS_CTRL_FULL)) { + udelay(100); + timeout++; + } + if (timeout == 20000) + DRM_INFO("MIPI: HS CMD FIFO was never cleared!\n"); +} + +static void mdfld_wait_for_DPI_CTRL_FIFO(struct drm_device *dev, u32 pipe) +{ + u32 gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe); + int timeout = 0; + + udelay(500); + + /* This will time out after approximately 2+ seconds */ + while ((timeout < 20000) && ((REG_READ(gen_fifo_stat_reg) & + DPI_FIFO_EMPTY) != DPI_FIFO_EMPTY)) { + udelay(100); + timeout++; + } + + if (timeout == 20000) + DRM_ERROR("MIPI: DPI FIFO was never cleared\n"); +} + +static void mdfld_wait_for_SPL_PKG_SENT(struct drm_device *dev, u32 pipe) +{ + u32 intr_stat_reg = MIPI_INTR_STAT_REG(pipe); + int timeout = 0; + + udelay(500); + + /* This will time out after approximately 2+ seconds */ + while ((timeout < 20000) && (!(REG_READ(intr_stat_reg) + & DSI_INTR_STATE_SPL_PKG_SENT))) { + udelay(100); + timeout++; + } + + if (timeout == 20000) + DRM_ERROR("MIPI: SPL_PKT_SENT_INTERRUPT was not sent successfully!\n"); +} + +/* For TC35876X */ + +static void dsi_set_device_ready_state(struct drm_device *dev, int state, + int pipe) +{ + REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), !!state, 0, 0); +} + +static void dsi_set_pipe_plane_enable_state(struct drm_device *dev, + int state, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + u32 pipeconf_reg = PIPEACONF; + u32 dspcntr_reg = DSPACNTR; + + u32 dspcntr = dev_priv->dspcntr[pipe]; + u32 mipi = MIPI_PORT_EN | PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX; + + if (pipe) { + pipeconf_reg = PIPECCONF; + dspcntr_reg = DSPCCNTR; + } else + mipi &= (~0x03); + + if (state) { + /*Set up pipe */ + REG_WRITE(pipeconf_reg, BIT(31)); + + if (REG_BIT_WAIT(pipeconf_reg, 1, 30)) + dev_err(&dev->pdev->dev, "%s: Pipe enable timeout\n", + __func__); + + /*Set up display plane */ + REG_WRITE(dspcntr_reg, dspcntr); + } else { + u32 dspbase_reg = pipe ? MDFLD_DSPCBASE : MRST_DSPABASE; + + /* Put DSI lanes to ULPS to disable pipe */ + REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), 2, 2, 1); + REG_READ(MIPI_DEVICE_READY_REG(pipe)); /* posted write? */ + + /* LP Hold */ + REG_FLD_MOD(MIPI_PORT_CONTROL(pipe), 0, 16, 16); + REG_READ(MIPI_PORT_CONTROL(pipe)); /* posted write? */ + + /* Disable display plane */ + REG_FLD_MOD(dspcntr_reg, 0, 31, 31); + + /* Flush the plane changes ??? posted write? */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_READ(dspbase_reg); + + /* Disable PIPE */ + REG_FLD_MOD(pipeconf_reg, 0, 31, 31); + + if (REG_BIT_WAIT(pipeconf_reg, 0, 30)) + dev_err(&dev->pdev->dev, "%s: Pipe disable timeout\n", + __func__); + + if (REG_BIT_WAIT(MIPI_GEN_FIFO_STAT_REG(pipe), 1, 28)) + dev_err(&dev->pdev->dev, "%s: FIFO not empty\n", + __func__); + } +} + +static void mdfld_dsi_configure_down(struct mdfld_dsi_encoder *dsi_encoder, + int pipe) +{ + struct mdfld_dsi_dpi_output *dpi_output = + MDFLD_DSI_DPI_OUTPUT(dsi_encoder); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_encoder_get_config(dsi_encoder); + struct drm_device *dev = dsi_config->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (!dev_priv->dpi_panel_on[pipe]) { + dev_err(dev->dev, "DPI panel is already off\n"); + return; + } + tc35876x_toshiba_bridge_panel_off(dev); + tc35876x_set_bridge_reset_state(dev, 1); + dsi_set_pipe_plane_enable_state(dev, 0, pipe); + mdfld_dsi_dpi_shut_down(dpi_output, pipe); + dsi_set_device_ready_state(dev, 0, pipe); +} + +static void mdfld_dsi_configure_up(struct mdfld_dsi_encoder *dsi_encoder, + int pipe) +{ + struct mdfld_dsi_dpi_output *dpi_output = + MDFLD_DSI_DPI_OUTPUT(dsi_encoder); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_encoder_get_config(dsi_encoder); + struct drm_device *dev = dsi_config->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (dev_priv->dpi_panel_on[pipe]) { + dev_err(dev->dev, "DPI panel is already on\n"); + return; + } + + /* For resume path sequence */ + mdfld_dsi_dpi_shut_down(dpi_output, pipe); + dsi_set_device_ready_state(dev, 0, pipe); + + dsi_set_device_ready_state(dev, 1, pipe); + tc35876x_set_bridge_reset_state(dev, 0); + tc35876x_configure_lvds_bridge(dev); + mdfld_dsi_dpi_turn_on(dpi_output, pipe); /* Send turn on command */ + dsi_set_pipe_plane_enable_state(dev, 1, pipe); +} +/* End for TC35876X */ + +/* ************************************************************************* *\ + * FUNCTION: mdfld_dsi_tpo_ic_init + * + * DESCRIPTION: This function is called only by mrst_dsi_mode_set and + * restore_display_registers. since this function does not + * acquire the mutex, it is important that the calling function + * does! +\* ************************************************************************* */ +static void mdfld_dsi_tpo_ic_init(struct mdfld_dsi_config *dsi_config, u32 pipe) +{ + struct drm_device *dev = dsi_config->dev; + u32 dcsChannelNumber = dsi_config->channel_num; + u32 gen_data_reg = MIPI_HS_GEN_DATA_REG(pipe); + u32 gen_ctrl_reg = MIPI_HS_GEN_CTRL_REG(pipe); + u32 gen_ctrl_val = GEN_LONG_WRITE; + + DRM_INFO("Enter mrst init TPO MIPI display.\n"); + + gen_ctrl_val |= dcsChannelNumber << DCS_CHANNEL_NUMBER_POS; + + /* Flip page order */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00008036); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x02 << WORD_COUNTS_POS)); + + /* 0xF0 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x005a5af0); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); + + /* Write protection key */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x005a5af1); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); + + /* 0xFC */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x005a5afc); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); + + /* 0xB7 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x770000b7); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000044); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x05 << WORD_COUNTS_POS)); + + /* 0xB6 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x000a0ab6); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); + + /* 0xF2 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x081010f2); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x4a070708); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x000000c5); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS)); + + /* 0xF8 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x024003f8); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x01030a04); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x0e020220); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000004); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x0d << WORD_COUNTS_POS)); + + /* 0xE2 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x398fc3e2); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x0000916f); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x06 << WORD_COUNTS_POS)); + + /* 0xB0 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x000000b0); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x02 << WORD_COUNTS_POS)); + + /* 0xF4 */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x240242f4); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x78ee2002); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x2a071050); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x507fee10); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x10300710); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x14 << WORD_COUNTS_POS)); + + /* 0xBA */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x19fe07ba); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x101c0a31); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000010); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS)); + + /* 0xBB */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x28ff07bb); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x24280a31); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000034); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS)); + + /* 0xFB */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x535d05fb); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1b1a2130); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x221e180e); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x131d2120); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x535d0508); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1c1a2131); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x231f160d); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x111b2220); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x535c2008); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1f1d2433); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x2c251a10); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x2c34372d); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000023); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x31 << WORD_COUNTS_POS)); + + /* 0xFA */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x525c0bfa); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1c1c232f); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x2623190e); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x18212625); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x545d0d0e); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1e1d2333); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x26231a10); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x1a222725); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x545d280f); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x21202635); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x31292013); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x31393d33); + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x00000029); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x31 << WORD_COUNTS_POS)); + + /* Set DM */ + mdfld_wait_for_HS_DATA_FIFO(dev, pipe); + REG_WRITE(gen_data_reg, 0x000100f7); + mdfld_wait_for_HS_CTRL_FIFO(dev, pipe); + REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS)); +} + +static u16 mdfld_dsi_dpi_to_byte_clock_count(int pixel_clock_count, + int num_lane, int bpp) +{ + return (u16)((pixel_clock_count * bpp) / (num_lane * 8)); +} + +/* + * Calculate the dpi time basing on a given drm mode @mode + * return 0 on success. + * FIXME: I was using proposed mode value for calculation, may need to + * use crtc mode values later + */ +int mdfld_dsi_dpi_timing_calculation(struct drm_display_mode *mode, + struct mdfld_dsi_dpi_timing *dpi_timing, + int num_lane, int bpp) +{ + int pclk_hsync, pclk_hfp, pclk_hbp, pclk_hactive; + int pclk_vsync, pclk_vfp, pclk_vbp; + + pclk_hactive = mode->hdisplay; + pclk_hfp = mode->hsync_start - mode->hdisplay; + pclk_hsync = mode->hsync_end - mode->hsync_start; + pclk_hbp = mode->htotal - mode->hsync_end; + + pclk_vfp = mode->vsync_start - mode->vdisplay; + pclk_vsync = mode->vsync_end - mode->vsync_start; + pclk_vbp = mode->vtotal - mode->vsync_end; + + /* + * byte clock counts were calculated by following formula + * bclock_count = pclk_count * bpp / num_lane / 8 + */ + dpi_timing->hsync_count = mdfld_dsi_dpi_to_byte_clock_count( + pclk_hsync, num_lane, bpp); + dpi_timing->hbp_count = mdfld_dsi_dpi_to_byte_clock_count( + pclk_hbp, num_lane, bpp); + dpi_timing->hfp_count = mdfld_dsi_dpi_to_byte_clock_count( + pclk_hfp, num_lane, bpp); + dpi_timing->hactive_count = mdfld_dsi_dpi_to_byte_clock_count( + pclk_hactive, num_lane, bpp); + dpi_timing->vsync_count = mdfld_dsi_dpi_to_byte_clock_count( + pclk_vsync, num_lane, bpp); + dpi_timing->vbp_count = mdfld_dsi_dpi_to_byte_clock_count( + pclk_vbp, num_lane, bpp); + dpi_timing->vfp_count = mdfld_dsi_dpi_to_byte_clock_count( + pclk_vfp, num_lane, bpp); + + return 0; +} + +void mdfld_dsi_dpi_controller_init(struct mdfld_dsi_config *dsi_config, + int pipe) +{ + struct drm_device *dev = dsi_config->dev; + int lane_count = dsi_config->lane_count; + struct mdfld_dsi_dpi_timing dpi_timing; + struct drm_display_mode *mode = dsi_config->mode; + u32 val; + + /*un-ready device*/ + REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), 0, 0, 0); + + /*init dsi adapter before kicking off*/ + REG_WRITE(MIPI_CTRL_REG(pipe), 0x00000018); + + /*enable all interrupts*/ + REG_WRITE(MIPI_INTR_EN_REG(pipe), 0xffffffff); + + /*set up func_prg*/ + val = lane_count; + val |= dsi_config->channel_num << DSI_DPI_VIRT_CHANNEL_OFFSET; + + switch (dsi_config->bpp) { + case 16: + val |= DSI_DPI_COLOR_FORMAT_RGB565; + break; + case 18: + val |= DSI_DPI_COLOR_FORMAT_RGB666; + break; + case 24: + val |= DSI_DPI_COLOR_FORMAT_RGB888; + break; + default: + DRM_ERROR("unsupported color format, bpp = %d\n", + dsi_config->bpp); + } + REG_WRITE(MIPI_DSI_FUNC_PRG_REG(pipe), val); + + REG_WRITE(MIPI_HS_TX_TIMEOUT_REG(pipe), + (mode->vtotal * mode->htotal * dsi_config->bpp / + (8 * lane_count)) & DSI_HS_TX_TIMEOUT_MASK); + REG_WRITE(MIPI_LP_RX_TIMEOUT_REG(pipe), + 0xffff & DSI_LP_RX_TIMEOUT_MASK); + + /*max value: 20 clock cycles of txclkesc*/ + REG_WRITE(MIPI_TURN_AROUND_TIMEOUT_REG(pipe), + 0x14 & DSI_TURN_AROUND_TIMEOUT_MASK); + + /*min 21 txclkesc, max: ffffh*/ + REG_WRITE(MIPI_DEVICE_RESET_TIMER_REG(pipe), + 0xffff & DSI_RESET_TIMER_MASK); + + REG_WRITE(MIPI_DPI_RESOLUTION_REG(pipe), + mode->vdisplay << 16 | mode->hdisplay); + + /*set DPI timing registers*/ + mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing, + dsi_config->lane_count, dsi_config->bpp); + + REG_WRITE(MIPI_HSYNC_COUNT_REG(pipe), + dpi_timing.hsync_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_HBP_COUNT_REG(pipe), + dpi_timing.hbp_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_HFP_COUNT_REG(pipe), + dpi_timing.hfp_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_HACTIVE_COUNT_REG(pipe), + dpi_timing.hactive_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_VSYNC_COUNT_REG(pipe), + dpi_timing.vsync_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_VBP_COUNT_REG(pipe), + dpi_timing.vbp_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_VFP_COUNT_REG(pipe), + dpi_timing.vfp_count & DSI_DPI_TIMING_MASK); + + REG_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe), 0x46); + + /*min: 7d0 max: 4e20*/ + REG_WRITE(MIPI_INIT_COUNT_REG(pipe), 0x000007d0); + + /*set up video mode*/ + val = dsi_config->video_mode | DSI_DPI_COMPLETE_LAST_LINE; + REG_WRITE(MIPI_VIDEO_MODE_FORMAT_REG(pipe), val); + + REG_WRITE(MIPI_EOT_DISABLE_REG(pipe), 0x00000000); + + REG_WRITE(MIPI_LP_BYTECLK_REG(pipe), 0x00000004); + + /*TODO: figure out how to setup these registers*/ + if (mdfld_get_panel_type(dev, pipe) == TC35876X) + REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x2A0c6008); + else + REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x150c3408); + + REG_WRITE(MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe), (0xa << 16) | 0x14); + + if (mdfld_get_panel_type(dev, pipe) == TC35876X) + tc35876x_set_bridge_reset_state(dev, 0); /*Pull High Reset */ + + /*set device ready*/ + REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), 1, 0, 0); +} + +void mdfld_dsi_dpi_turn_on(struct mdfld_dsi_dpi_output *output, int pipe) +{ + struct drm_device *dev = output->dev; + + /* clear special packet sent bit */ + if (REG_READ(MIPI_INTR_STAT_REG(pipe)) & DSI_INTR_STATE_SPL_PKG_SENT) + REG_WRITE(MIPI_INTR_STAT_REG(pipe), + DSI_INTR_STATE_SPL_PKG_SENT); + + /*send turn on package*/ + REG_WRITE(MIPI_DPI_CONTROL_REG(pipe), DSI_DPI_CTRL_HS_TURN_ON); + + /*wait for SPL_PKG_SENT interrupt*/ + mdfld_wait_for_SPL_PKG_SENT(dev, pipe); + + if (REG_READ(MIPI_INTR_STAT_REG(pipe)) & DSI_INTR_STATE_SPL_PKG_SENT) + REG_WRITE(MIPI_INTR_STAT_REG(pipe), + DSI_INTR_STATE_SPL_PKG_SENT); + + output->panel_on = 1; + + /* FIXME the following is disabled to WA the X slow start issue + for TMD panel + if (pipe == 2) + dev_priv->dpi_panel_on2 = true; + else if (pipe == 0) + dev_priv->dpi_panel_on = true; */ +} + +static void mdfld_dsi_dpi_shut_down(struct mdfld_dsi_dpi_output *output, + int pipe) +{ + struct drm_device *dev = output->dev; + + /*if output is on, or mode setting didn't happen, ignore this*/ + if ((!output->panel_on) || output->first_boot) { + output->first_boot = 0; + return; + } + + /* Wait for dpi fifo to empty */ + mdfld_wait_for_DPI_CTRL_FIFO(dev, pipe); + + /* Clear the special packet interrupt bit if set */ + if (REG_READ(MIPI_INTR_STAT_REG(pipe)) & DSI_INTR_STATE_SPL_PKG_SENT) + REG_WRITE(MIPI_INTR_STAT_REG(pipe), + DSI_INTR_STATE_SPL_PKG_SENT); + + if (REG_READ(MIPI_DPI_CONTROL_REG(pipe)) == DSI_DPI_CTRL_HS_SHUTDOWN) + goto shutdown_out; + + REG_WRITE(MIPI_DPI_CONTROL_REG(pipe), DSI_DPI_CTRL_HS_SHUTDOWN); + +shutdown_out: + output->panel_on = 0; + output->first_boot = 0; + + /* FIXME the following is disabled to WA the X slow start issue + for TMD panel + if (pipe == 2) + dev_priv->dpi_panel_on2 = false; + else if (pipe == 0) + dev_priv->dpi_panel_on = false; */ +} + +static void mdfld_dsi_dpi_set_power(struct drm_encoder *encoder, bool on) +{ + struct mdfld_dsi_encoder *dsi_encoder = mdfld_dsi_encoder(encoder); + struct mdfld_dsi_dpi_output *dpi_output = + MDFLD_DSI_DPI_OUTPUT(dsi_encoder); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_encoder_get_config(dsi_encoder); + int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder); + struct drm_device *dev = dsi_config->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + /*start up display island if it was shutdown*/ + if (!gma_power_begin(dev, true)) + return; + + if (on) { + if (mdfld_get_panel_type(dev, pipe) == TMD_VID) + mdfld_dsi_dpi_turn_on(dpi_output, pipe); + else if (mdfld_get_panel_type(dev, pipe) == TC35876X) + mdfld_dsi_configure_up(dsi_encoder, pipe); + else { + /*enable mipi port*/ + REG_WRITE(MIPI_PORT_CONTROL(pipe), + REG_READ(MIPI_PORT_CONTROL(pipe)) | BIT(31)); + REG_READ(MIPI_PORT_CONTROL(pipe)); + + mdfld_dsi_dpi_turn_on(dpi_output, pipe); + mdfld_dsi_tpo_ic_init(dsi_config, pipe); + } + dev_priv->dpi_panel_on[pipe] = true; + } else { + if (mdfld_get_panel_type(dev, pipe) == TMD_VID) + mdfld_dsi_dpi_shut_down(dpi_output, pipe); + else if (mdfld_get_panel_type(dev, pipe) == TC35876X) + mdfld_dsi_configure_down(dsi_encoder, pipe); + else { + mdfld_dsi_dpi_shut_down(dpi_output, pipe); + + /*disable mipi port*/ + REG_WRITE(MIPI_PORT_CONTROL(pipe), + REG_READ(MIPI_PORT_CONTROL(pipe)) & ~BIT(31)); + REG_READ(MIPI_PORT_CONTROL(pipe)); + } + dev_priv->dpi_panel_on[pipe] = false; + } + gma_power_end(dev); +} + +void mdfld_dsi_dpi_dpms(struct drm_encoder *encoder, int mode) +{ + mdfld_dsi_dpi_set_power(encoder, mode == DRM_MODE_DPMS_ON); +} + +bool mdfld_dsi_dpi_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct mdfld_dsi_encoder *dsi_encoder = mdfld_dsi_encoder(encoder); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_encoder_get_config(dsi_encoder); + struct drm_display_mode *fixed_mode = dsi_config->fixed_mode; + + if (fixed_mode) { + adjusted_mode->hdisplay = fixed_mode->hdisplay; + adjusted_mode->hsync_start = fixed_mode->hsync_start; + adjusted_mode->hsync_end = fixed_mode->hsync_end; + adjusted_mode->htotal = fixed_mode->htotal; + adjusted_mode->vdisplay = fixed_mode->vdisplay; + adjusted_mode->vsync_start = fixed_mode->vsync_start; + adjusted_mode->vsync_end = fixed_mode->vsync_end; + adjusted_mode->vtotal = fixed_mode->vtotal; + adjusted_mode->clock = fixed_mode->clock; + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); + } + return true; +} + +void mdfld_dsi_dpi_prepare(struct drm_encoder *encoder) +{ + mdfld_dsi_dpi_set_power(encoder, false); +} + +void mdfld_dsi_dpi_commit(struct drm_encoder *encoder) +{ + mdfld_dsi_dpi_set_power(encoder, true); +} + +/* For TC35876X */ +/* This functionality was implemented in FW in iCDK */ +/* But removed in DV0 and later. So need to add here. */ +static void mipi_set_properties(struct mdfld_dsi_config *dsi_config, int pipe) +{ + struct drm_device *dev = dsi_config->dev; + + REG_WRITE(MIPI_CTRL_REG(pipe), 0x00000018); + REG_WRITE(MIPI_INTR_EN_REG(pipe), 0xffffffff); + REG_WRITE(MIPI_HS_TX_TIMEOUT_REG(pipe), 0xffffff); + REG_WRITE(MIPI_LP_RX_TIMEOUT_REG(pipe), 0xffffff); + REG_WRITE(MIPI_TURN_AROUND_TIMEOUT_REG(pipe), 0x14); + REG_WRITE(MIPI_DEVICE_RESET_TIMER_REG(pipe), 0xff); + REG_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe), 0x25); + REG_WRITE(MIPI_INIT_COUNT_REG(pipe), 0xf0); + REG_WRITE(MIPI_EOT_DISABLE_REG(pipe), 0x00000000); + REG_WRITE(MIPI_LP_BYTECLK_REG(pipe), 0x00000004); + REG_WRITE(MIPI_DBI_BW_CTRL_REG(pipe), 0x00000820); + REG_WRITE(MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe), (0xa << 16) | 0x14); +} + +static void mdfld_mipi_set_video_timing(struct mdfld_dsi_config *dsi_config, + int pipe) +{ + struct drm_device *dev = dsi_config->dev; + struct mdfld_dsi_dpi_timing dpi_timing; + struct drm_display_mode *mode = dsi_config->mode; + + mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing, + dsi_config->lane_count, + dsi_config->bpp); + + REG_WRITE(MIPI_DPI_RESOLUTION_REG(pipe), + mode->vdisplay << 16 | mode->hdisplay); + REG_WRITE(MIPI_HSYNC_COUNT_REG(pipe), + dpi_timing.hsync_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_HBP_COUNT_REG(pipe), + dpi_timing.hbp_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_HFP_COUNT_REG(pipe), + dpi_timing.hfp_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_HACTIVE_COUNT_REG(pipe), + dpi_timing.hactive_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_VSYNC_COUNT_REG(pipe), + dpi_timing.vsync_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_VBP_COUNT_REG(pipe), + dpi_timing.vbp_count & DSI_DPI_TIMING_MASK); + REG_WRITE(MIPI_VFP_COUNT_REG(pipe), + dpi_timing.vfp_count & DSI_DPI_TIMING_MASK); +} + +static void mdfld_mipi_config(struct mdfld_dsi_config *dsi_config, int pipe) +{ + struct drm_device *dev = dsi_config->dev; + int lane_count = dsi_config->lane_count; + + if (pipe) { + REG_WRITE(MIPI_PORT_CONTROL(0), 0x00000002); + REG_WRITE(MIPI_PORT_CONTROL(2), 0x80000000); + } else { + REG_WRITE(MIPI_PORT_CONTROL(0), 0x80010000); + REG_WRITE(MIPI_PORT_CONTROL(2), 0x00); + } + + REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x150A600F); + REG_WRITE(MIPI_VIDEO_MODE_FORMAT_REG(pipe), 0x0000000F); + + /* lane_count = 3 */ + REG_WRITE(MIPI_DSI_FUNC_PRG_REG(pipe), 0x00000200 | lane_count); + + mdfld_mipi_set_video_timing(dsi_config, pipe); +} + +static void mdfld_set_pipe_timing(struct mdfld_dsi_config *dsi_config, int pipe) +{ + struct drm_device *dev = dsi_config->dev; + struct drm_display_mode *mode = dsi_config->mode; + + REG_WRITE(HTOTAL_A, ((mode->htotal - 1) << 16) | (mode->hdisplay - 1)); + REG_WRITE(HBLANK_A, ((mode->htotal - 1) << 16) | (mode->hdisplay - 1)); + REG_WRITE(HSYNC_A, + ((mode->hsync_end - 1) << 16) | (mode->hsync_start - 1)); + + REG_WRITE(VTOTAL_A, ((mode->vtotal - 1) << 16) | (mode->vdisplay - 1)); + REG_WRITE(VBLANK_A, ((mode->vtotal - 1) << 16) | (mode->vdisplay - 1)); + REG_WRITE(VSYNC_A, + ((mode->vsync_end - 1) << 16) | (mode->vsync_start - 1)); + + REG_WRITE(PIPEASRC, + ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); +} +/* End for TC35876X */ + +void mdfld_dsi_dpi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct mdfld_dsi_encoder *dsi_encoder = mdfld_dsi_encoder(encoder); + struct mdfld_dsi_dpi_output *dpi_output = + MDFLD_DSI_DPI_OUTPUT(dsi_encoder); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_encoder_get_config(dsi_encoder); + struct drm_device *dev = dsi_config->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder); + + u32 pipeconf_reg = PIPEACONF; + u32 dspcntr_reg = DSPACNTR; + + u32 pipeconf = dev_priv->pipeconf[pipe]; + u32 dspcntr = dev_priv->dspcntr[pipe]; + u32 mipi = MIPI_PORT_EN | PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX; + + if (pipe) { + pipeconf_reg = PIPECCONF; + dspcntr_reg = DSPCCNTR; + } else { + if (mdfld_get_panel_type(dev, pipe) == TC35876X) + mipi &= (~0x03); /* Use all four lanes */ + else + mipi |= 2; + } + + /*start up display island if it was shutdown*/ + if (!gma_power_begin(dev, true)) + return; + + if (mdfld_get_panel_type(dev, pipe) == TC35876X) { + /* + * The following logic is required to reset the bridge and + * configure. This also starts the DSI clock at 200MHz. + */ + tc35876x_set_bridge_reset_state(dev, 0); /*Pull High Reset */ + tc35876x_toshiba_bridge_panel_on(dev); + udelay(100); + /* Now start the DSI clock */ + REG_WRITE(MRST_DPLL_A, 0x00); + REG_WRITE(MRST_FPA0, 0xC1); + REG_WRITE(MRST_DPLL_A, 0x00800000); + udelay(500); + REG_WRITE(MRST_DPLL_A, 0x80800000); + + if (REG_BIT_WAIT(pipeconf_reg, 1, 29)) + dev_err(&dev->pdev->dev, "%s: DSI PLL lock timeout\n", + __func__); + + REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x2A0c6008); + + mipi_set_properties(dsi_config, pipe); + mdfld_mipi_config(dsi_config, pipe); + mdfld_set_pipe_timing(dsi_config, pipe); + + REG_WRITE(DSPABASE, 0x00); + REG_WRITE(DSPASTRIDE, (mode->hdisplay * 4)); + REG_WRITE(DSPASIZE, + ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); + + REG_WRITE(DSPACNTR, 0x98000000); + REG_WRITE(DSPASURF, 0x00); + + REG_WRITE(VGACNTRL, 0x80000000); + REG_WRITE(DEVICE_READY_REG, 0x00000001); + + REG_WRITE(MIPI_PORT_CONTROL(pipe), 0x80810000); + } else { + /*set up mipi port FIXME: do at init time */ + REG_WRITE(MIPI_PORT_CONTROL(pipe), mipi); + } + REG_READ(MIPI_PORT_CONTROL(pipe)); + + if (mdfld_get_panel_type(dev, pipe) == TMD_VID) { + /* NOP */ + } else if (mdfld_get_panel_type(dev, pipe) == TC35876X) { + /* set up DSI controller DPI interface */ + mdfld_dsi_dpi_controller_init(dsi_config, pipe); + + /* Configure MIPI Bridge and Panel */ + tc35876x_configure_lvds_bridge(dev); + dev_priv->dpi_panel_on[pipe] = true; + } else { + /*turn on DPI interface*/ + mdfld_dsi_dpi_turn_on(dpi_output, pipe); + } + + /*set up pipe*/ + REG_WRITE(pipeconf_reg, pipeconf); + REG_READ(pipeconf_reg); + + /*set up display plane*/ + REG_WRITE(dspcntr_reg, dspcntr); + REG_READ(dspcntr_reg); + + msleep(20); /* FIXME: this should wait for vblank */ + + if (mdfld_get_panel_type(dev, pipe) == TMD_VID) { + /* NOP */ + } else if (mdfld_get_panel_type(dev, pipe) == TC35876X) { + mdfld_dsi_dpi_turn_on(dpi_output, pipe); + } else { + /* init driver ic */ + mdfld_dsi_tpo_ic_init(dsi_config, pipe); + /*init backlight*/ + mdfld_dsi_brightness_init(dsi_config, pipe); + } + + gma_power_end(dev); +} + +/* + * Init DSI DPI encoder. + * Allocate an mdfld_dsi_encoder and attach it to given @dsi_connector + * return pointer of newly allocated DPI encoder, NULL on error + */ +struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev, + struct mdfld_dsi_connector *dsi_connector, + const struct panel_funcs *p_funcs) +{ + struct mdfld_dsi_dpi_output *dpi_output = NULL; + struct mdfld_dsi_config *dsi_config; + struct drm_connector *connector = NULL; + struct drm_encoder *encoder = NULL; + int pipe; + u32 data; + int ret; + + pipe = dsi_connector->pipe; + + if (mdfld_get_panel_type(dev, pipe) != TC35876X) { + dsi_config = mdfld_dsi_get_config(dsi_connector); + + /* panel hard-reset */ + if (p_funcs->reset) { + ret = p_funcs->reset(pipe); + if (ret) { + DRM_ERROR("Panel %d hard-reset failed\n", pipe); + return NULL; + } + } + + /* panel drvIC init */ + if (p_funcs->drv_ic_init) + p_funcs->drv_ic_init(dsi_config, pipe); + + /* panel power mode detect */ + ret = mdfld_dsi_get_power_mode(dsi_config, &data, false); + if (ret) { + DRM_ERROR("Panel %d get power mode failed\n", pipe); + dsi_connector->status = connector_status_disconnected; + } else { + DRM_INFO("pipe %d power mode 0x%x\n", pipe, data); + dsi_connector->status = connector_status_connected; + } + } + + dpi_output = kzalloc(sizeof(struct mdfld_dsi_dpi_output), GFP_KERNEL); + if (!dpi_output) { + DRM_ERROR("No memory\n"); + return NULL; + } + + if (dsi_connector->pipe) + dpi_output->panel_on = 0; + else + dpi_output->panel_on = 0; + + dpi_output->dev = dev; + if (mdfld_get_panel_type(dev, pipe) != TC35876X) + dpi_output->p_funcs = p_funcs; + dpi_output->first_boot = 1; + + /*get fixed mode*/ + dsi_config = mdfld_dsi_get_config(dsi_connector); + + /*create drm encoder object*/ + connector = &dsi_connector->base.base; + encoder = &dpi_output->base.base.base; + drm_encoder_init(dev, + encoder, + p_funcs->encoder_funcs, + DRM_MODE_ENCODER_LVDS); + drm_encoder_helper_add(encoder, + p_funcs->encoder_helper_funcs); + + /*attach to given connector*/ + drm_mode_connector_attach_encoder(connector, encoder); + + /*set possible crtcs and clones*/ + if (dsi_connector->pipe) { + encoder->possible_crtcs = (1 << 2); + encoder->possible_clones = (1 << 1); + } else { + encoder->possible_crtcs = (1 << 0); + encoder->possible_clones = (1 << 0); + } + + dsi_connector->base.encoder = &dpi_output->base.base; + + return &dpi_output->base; +} diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_dpi.h b/drivers/gpu/drm/gma500/mdfld_dsi_dpi.h new file mode 100644 index 000000000000..6f762478b959 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_dpi.h @@ -0,0 +1,79 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * jim liu <jim.liu@intel.com> + * Jackie Li<yaodong.li@intel.com> + */ + +#ifndef __MDFLD_DSI_DPI_H__ +#define __MDFLD_DSI_DPI_H__ + +#include "mdfld_dsi_output.h" +#include "mdfld_output.h" + +struct mdfld_dsi_dpi_timing { + u16 hsync_count; + u16 hbp_count; + u16 hfp_count; + u16 hactive_count; + u16 vsync_count; + u16 vbp_count; + u16 vfp_count; +}; + +struct mdfld_dsi_dpi_output { + struct mdfld_dsi_encoder base; + struct drm_device *dev; + + int panel_on; + int first_boot; + + const struct panel_funcs *p_funcs; +}; + +#define MDFLD_DSI_DPI_OUTPUT(dsi_encoder)\ + container_of(dsi_encoder, struct mdfld_dsi_dpi_output, base) + +/* Export functions */ +extern int mdfld_dsi_dpi_timing_calculation(struct drm_display_mode *mode, + struct mdfld_dsi_dpi_timing *dpi_timing, + int num_lane, int bpp); +extern struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev, + struct mdfld_dsi_connector *dsi_connector, + const struct panel_funcs *p_funcs); + +/* MDFLD DPI helper functions */ +extern void mdfld_dsi_dpi_dpms(struct drm_encoder *encoder, int mode); +extern bool mdfld_dsi_dpi_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +extern void mdfld_dsi_dpi_prepare(struct drm_encoder *encoder); +extern void mdfld_dsi_dpi_commit(struct drm_encoder *encoder); +extern void mdfld_dsi_dpi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +extern void mdfld_dsi_dpi_turn_on(struct mdfld_dsi_dpi_output *output, + int pipe); +extern void mdfld_dsi_dpi_controller_init(struct mdfld_dsi_config *dsi_config, + int pipe); +#endif /*__MDFLD_DSI_DPI_H__*/ diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c new file mode 100644 index 000000000000..5675d93b4205 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c @@ -0,0 +1,621 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * jim liu <jim.liu@intel.com> + * Jackie Li<yaodong.li@intel.com> + */ + +#include <linux/module.h> + +#include "mdfld_dsi_output.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_output.h" +#include "mdfld_dsi_pkg_sender.h" +#include "tc35876x-dsi-lvds.h" +#include <linux/pm_runtime.h> +#include <asm/intel_scu_ipc.h> + +/* get the LABC from command line. */ +static int LABC_control = 1; + +#ifdef MODULE +module_param(LABC_control, int, 0644); +#else + +static int __init parse_LABC_control(char *arg) +{ + /* LABC control can be passed in as a cmdline parameter */ + /* to enable this feature add LABC=1 to cmdline */ + /* to disable this feature add LABC=0 to cmdline */ + if (!arg) + return -EINVAL; + + if (!strcasecmp(arg, "0")) + LABC_control = 0; + else if (!strcasecmp(arg, "1")) + LABC_control = 1; + + return 0; +} +early_param("LABC", parse_LABC_control); +#endif + +/** + * Check and see if the generic control or data buffer is empty and ready. + */ +void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, u32 gen_fifo_stat_reg, + u32 fifo_stat) +{ + u32 GEN_BF_time_out_count; + + /* Check MIPI Adatper command registers */ + for (GEN_BF_time_out_count = 0; + GEN_BF_time_out_count < GEN_FB_TIME_OUT; + GEN_BF_time_out_count++) { + if ((REG_READ(gen_fifo_stat_reg) & fifo_stat) == fifo_stat) + break; + udelay(100); + } + + if (GEN_BF_time_out_count == GEN_FB_TIME_OUT) + DRM_ERROR("mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x.\n", + gen_fifo_stat_reg); +} + +/** + * Manage the DSI MIPI keyboard and display brightness. + * FIXME: this is exported to OSPM code. should work out an specific + * display interface to OSPM. + */ + +void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe) +{ + struct mdfld_dsi_pkg_sender *sender = + mdfld_dsi_get_pkg_sender(dsi_config); + struct drm_device *dev = sender->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 gen_ctrl_val; + + if (!sender) { + DRM_ERROR("No sender found\n"); + return; + } + + /* Set default display backlight value to 85% (0xd8)*/ + mdfld_dsi_send_mcs_short(sender, write_display_brightness, 0xd8, 1, + true); + + /* Set minimum brightness setting of CABC function to 20% (0x33)*/ + mdfld_dsi_send_mcs_short(sender, write_cabc_min_bright, 0x33, 1, true); + + /* Enable backlight or/and LABC */ + gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON | + BACKLIGHT_ON; + if (LABC_control == 1) + gen_ctrl_val |= DISPLAY_DIMMING_ON | DISPLAY_BRIGHTNESS_AUTO + | GAMMA_AUTO; + + if (LABC_control == 1) + gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON; + + dev_priv->mipi_ctrl_display = gen_ctrl_val; + + mdfld_dsi_send_mcs_short(sender, write_ctrl_display, (u8)gen_ctrl_val, + 1, true); + + mdfld_dsi_send_mcs_short(sender, write_ctrl_cabc, UI_IMAGE, 1, true); +} + +void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level) +{ + struct mdfld_dsi_pkg_sender *sender; + struct drm_psb_private *dev_priv; + struct mdfld_dsi_config *dsi_config; + u32 gen_ctrl_val = 0; + int p_type = TMD_VID; + + if (!dev || (pipe != 0 && pipe != 2)) { + DRM_ERROR("Invalid parameter\n"); + return; + } + + p_type = mdfld_get_panel_type(dev, 0); + + dev_priv = dev->dev_private; + + if (pipe) + dsi_config = dev_priv->dsi_configs[1]; + else + dsi_config = dev_priv->dsi_configs[0]; + + sender = mdfld_dsi_get_pkg_sender(dsi_config); + + if (!sender) { + DRM_ERROR("No sender found\n"); + return; + } + + gen_ctrl_val = (level * 0xff / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff; + + dev_dbg(sender->dev->dev, "pipe = %d, gen_ctrl_val = %d.\n", + pipe, gen_ctrl_val); + + if (p_type == TMD_VID) { + /* Set display backlight value */ + mdfld_dsi_send_mcs_short(sender, tmd_write_display_brightness, + (u8)gen_ctrl_val, 1, true); + } else { + /* Set display backlight value */ + mdfld_dsi_send_mcs_short(sender, write_display_brightness, + (u8)gen_ctrl_val, 1, true); + + /* Enable backlight control */ + if (level == 0) + gen_ctrl_val = 0; + else + gen_ctrl_val = dev_priv->mipi_ctrl_display; + + mdfld_dsi_send_mcs_short(sender, write_ctrl_display, + (u8)gen_ctrl_val, 1, true); + } +} + +static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config, + u8 dcs, u32 *data, bool hs) +{ + struct mdfld_dsi_pkg_sender *sender + = mdfld_dsi_get_pkg_sender(dsi_config); + + if (!sender || !data) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + return mdfld_dsi_read_mcs(sender, dcs, data, 1, hs); +} + +int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, u32 *mode, + bool hs) +{ + if (!dsi_config || !mode) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, hs); +} + +/* + * NOTE: this function was used by OSPM. + * TODO: will be removed later, should work out display interfaces for OSPM + */ +void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe) +{ + if (!dsi_config || ((pipe != 0) && (pipe != 2))) { + DRM_ERROR("Invalid parameters\n"); + return; + } + + mdfld_dsi_dpi_controller_init(dsi_config, pipe); +} + +static void mdfld_dsi_connector_save(struct drm_connector *connector) +{ +} + +static void mdfld_dsi_connector_restore(struct drm_connector *connector) +{ +} + +/* FIXME: start using the force parameter */ +static enum drm_connector_status +mdfld_dsi_connector_detect(struct drm_connector *connector, bool force) +{ + struct mdfld_dsi_connector *dsi_connector + = mdfld_dsi_connector(connector); + + dsi_connector->status = connector_status_connected; + + return dsi_connector->status; +} + +static int mdfld_dsi_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_encoder *encoder = connector->encoder; + + if (!strcmp(property->name, "scaling mode") && encoder) { + struct psb_intel_crtc *psb_crtc = + to_psb_intel_crtc(encoder->crtc); + bool centerechange; + uint64_t val; + + if (!psb_crtc) + goto set_prop_error; + + switch (value) { + case DRM_MODE_SCALE_FULLSCREEN: + break; + case DRM_MODE_SCALE_NO_SCALE: + break; + case DRM_MODE_SCALE_ASPECT: + break; + default: + goto set_prop_error; + } + + if (drm_connector_property_get_value(connector, property, &val)) + goto set_prop_error; + + if (val == value) + goto set_prop_done; + + if (drm_connector_property_set_value(connector, + property, value)) + goto set_prop_error; + + centerechange = (val == DRM_MODE_SCALE_NO_SCALE) || + (value == DRM_MODE_SCALE_NO_SCALE); + + if (psb_crtc->saved_mode.hdisplay != 0 && + psb_crtc->saved_mode.vdisplay != 0) { + if (centerechange) { + if (!drm_crtc_helper_set_mode(encoder->crtc, + &psb_crtc->saved_mode, + encoder->crtc->x, + encoder->crtc->y, + encoder->crtc->fb)) + goto set_prop_error; + } else { + struct drm_encoder_helper_funcs *funcs = + encoder->helper_private; + funcs->mode_set(encoder, + &psb_crtc->saved_mode, + &psb_crtc->saved_adjusted_mode); + } + } + } else if (!strcmp(property->name, "backlight") && encoder) { + if (drm_connector_property_set_value(connector, property, + value)) + goto set_prop_error; + else { +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct backlight_device *psb_bd; + + psb_bd = mdfld_get_backlight_device(); + if (psb_bd) { + psb_bd->props.brightness = value; + mdfld_set_brightness(psb_bd); + } +#endif + } + } +set_prop_done: + return 0; +set_prop_error: + return -1; +} + +static void mdfld_dsi_connector_destroy(struct drm_connector *connector) +{ + struct mdfld_dsi_connector *dsi_connector = + mdfld_dsi_connector(connector); + struct mdfld_dsi_pkg_sender *sender; + + if (!dsi_connector) + return; + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + sender = dsi_connector->pkg_sender; + mdfld_dsi_pkg_sender_destroy(sender); + kfree(dsi_connector); +} + +static int mdfld_dsi_connector_get_modes(struct drm_connector *connector) +{ + struct mdfld_dsi_connector *dsi_connector = + mdfld_dsi_connector(connector); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_get_config(dsi_connector); + struct drm_display_mode *fixed_mode = dsi_config->fixed_mode; + struct drm_display_mode *dup_mode = NULL; + struct drm_device *dev = connector->dev; + + connector->display_info.min_vfreq = 0; + connector->display_info.max_vfreq = 200; + connector->display_info.min_hfreq = 0; + connector->display_info.max_hfreq = 200; + + if (fixed_mode) { + dev_dbg(dev->dev, "fixed_mode %dx%d\n", + fixed_mode->hdisplay, fixed_mode->vdisplay); + dup_mode = drm_mode_duplicate(dev, fixed_mode); + drm_mode_probed_add(connector, dup_mode); + return 1; + } + DRM_ERROR("Didn't get any modes!\n"); + return 0; +} + +static int mdfld_dsi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct mdfld_dsi_connector *dsi_connector = + mdfld_dsi_connector(connector); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_get_config(dsi_connector); + struct drm_display_mode *fixed_mode = dsi_config->fixed_mode; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + return MODE_NO_DBLESCAN; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + return MODE_NO_INTERLACE; + + /** + * FIXME: current DC has no fitting unit, reject any mode setting + * request + * Will figure out a way to do up-scaling(pannel fitting) later. + **/ + if (fixed_mode) { + if (mode->hdisplay != fixed_mode->hdisplay) + return MODE_PANEL; + + if (mode->vdisplay != fixed_mode->vdisplay) + return MODE_PANEL; + } + + return MODE_OK; +} + +static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode) +{ + if (mode == connector->dpms) + return; + + /*first, execute dpms*/ + + drm_helper_connector_dpms(connector, mode); +} + +static struct drm_encoder *mdfld_dsi_connector_best_encoder( + struct drm_connector *connector) +{ + struct mdfld_dsi_connector *dsi_connector = + mdfld_dsi_connector(connector); + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_get_config(dsi_connector); + return &dsi_config->encoder->base.base; +} + +/*DSI connector funcs*/ +static const struct drm_connector_funcs mdfld_dsi_connector_funcs = { + .dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms, + .save = mdfld_dsi_connector_save, + .restore = mdfld_dsi_connector_restore, + .detect = mdfld_dsi_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = mdfld_dsi_connector_set_property, + .destroy = mdfld_dsi_connector_destroy, +}; + +/*DSI connector helper funcs*/ +static const struct drm_connector_helper_funcs + mdfld_dsi_connector_helper_funcs = { + .get_modes = mdfld_dsi_connector_get_modes, + .mode_valid = mdfld_dsi_connector_mode_valid, + .best_encoder = mdfld_dsi_connector_best_encoder, +}; + +static int mdfld_dsi_get_default_config(struct drm_device *dev, + struct mdfld_dsi_config *config, int pipe) +{ + if (!dev || !config) { + DRM_ERROR("Invalid parameters"); + return -EINVAL; + } + + config->bpp = 24; + if (mdfld_get_panel_type(dev, pipe) == TC35876X) + config->lane_count = 4; + else + config->lane_count = 2; + config->channel_num = 0; + + if (mdfld_get_panel_type(dev, pipe) == TMD_VID) + config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE; + else if (mdfld_get_panel_type(dev, pipe) == TC35876X) + config->video_mode = + MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS; + else + config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE; + + return 0; +} + +int mdfld_dsi_panel_reset(int pipe) +{ + unsigned gpio; + int ret = 0; + + switch (pipe) { + case 0: + gpio = 128; + break; + case 2: + gpio = 34; + break; + default: + DRM_ERROR("Invalid output\n"); + return -EINVAL; + } + + ret = gpio_request(gpio, "gfx"); + if (ret) { + DRM_ERROR("gpio_rqueset failed\n"); + return ret; + } + + ret = gpio_direction_output(gpio, 1); + if (ret) { + DRM_ERROR("gpio_direction_output failed\n"); + goto gpio_error; + } + + gpio_get_value(128); + +gpio_error: + if (gpio_is_valid(gpio)) + gpio_free(gpio); + + return ret; +} + +/* + * MIPI output init + * @dev drm device + * @pipe pipe number. 0 or 2 + * @config + * + * Do the initialization of a MIPI output, including create DRM mode objects + * initialization of DSI output on @pipe + */ +void mdfld_dsi_output_init(struct drm_device *dev, + int pipe, + const struct panel_funcs *p_vid_funcs) +{ + struct mdfld_dsi_config *dsi_config; + struct mdfld_dsi_connector *dsi_connector; + struct drm_connector *connector; + struct mdfld_dsi_encoder *encoder; + struct drm_psb_private *dev_priv = dev->dev_private; + struct panel_info dsi_panel_info; + u32 width_mm, height_mm; + + dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe); + + if (!dev || ((pipe != 0) && (pipe != 2))) { + DRM_ERROR("Invalid parameter\n"); + return; + } + + /*create a new connetor*/ + dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL); + if (!dsi_connector) { + DRM_ERROR("No memory"); + return; + } + + dsi_connector->pipe = pipe; + + dsi_config = kzalloc(sizeof(struct mdfld_dsi_config), + GFP_KERNEL); + if (!dsi_config) { + DRM_ERROR("cannot allocate memory for DSI config\n"); + goto dsi_init_err0; + } + mdfld_dsi_get_default_config(dev, dsi_config, pipe); + + dsi_connector->private = dsi_config; + + dsi_config->changed = 1; + dsi_config->dev = dev; + + dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev); + if (p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info)) + goto dsi_init_err0; + + width_mm = dsi_panel_info.width_mm; + height_mm = dsi_panel_info.height_mm; + + dsi_config->mode = dsi_config->fixed_mode; + dsi_config->connector = dsi_connector; + + if (!dsi_config->fixed_mode) { + DRM_ERROR("No pannel fixed mode was found\n"); + goto dsi_init_err0; + } + + if (pipe && dev_priv->dsi_configs[0]) { + dsi_config->dvr_ic_inited = 0; + dev_priv->dsi_configs[1] = dsi_config; + } else if (pipe == 0) { + dsi_config->dvr_ic_inited = 1; + dev_priv->dsi_configs[0] = dsi_config; + } else { + DRM_ERROR("Trying to init MIPI1 before MIPI0\n"); + goto dsi_init_err0; + } + + + connector = &dsi_connector->base.base; + drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs); + + connector->display_info.subpixel_order = SubPixelHorizontalRGB; + connector->display_info.width_mm = width_mm; + connector->display_info.height_mm = height_mm; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + /*attach properties*/ + drm_connector_attach_property(connector, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + drm_connector_attach_property(connector, + dev_priv->backlight_property, + MDFLD_DSI_BRIGHTNESS_MAX_LEVEL); + + /*init DSI package sender on this output*/ + if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) { + DRM_ERROR("Package Sender initialization failed on pipe %d\n", + pipe); + goto dsi_init_err0; + } + + encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs); + if (!encoder) { + DRM_ERROR("Create DPI encoder failed\n"); + goto dsi_init_err1; + } + encoder->private = dsi_config; + dsi_config->encoder = encoder; + encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI : + INTEL_OUTPUT_MIPI2; + drm_sysfs_connector_add(connector); + return; + + /*TODO: add code to destroy outputs on error*/ +dsi_init_err1: + /*destroy sender*/ + mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender); + + drm_connector_cleanup(connector); + + kfree(dsi_config->fixed_mode); + kfree(dsi_config); +dsi_init_err0: + kfree(dsi_connector); +} diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.h b/drivers/gpu/drm/gma500/mdfld_dsi_output.h new file mode 100644 index 000000000000..21071cef92a4 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.h @@ -0,0 +1,378 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * jim liu <jim.liu@intel.com> + * Jackie Li<yaodong.li@intel.com> + */ + +#ifndef __MDFLD_DSI_OUTPUT_H__ +#define __MDFLD_DSI_OUTPUT_H__ + +#include <linux/backlight.h> +#include <linux/version.h> +#include <drm/drmP.h> +#include <drm/drm.h> +#include <drm/drm_crtc.h> +#include <drm/drm_edid.h> + +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "mdfld_output.h" + +#include <asm/mrst.h> + +#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) +#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) +#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) +#define FLD_MOD(orig, val, start, end) \ + (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) + +#define REG_FLD_MOD(reg, val, start, end) \ + REG_WRITE(reg, FLD_MOD(REG_READ(reg), val, start, end)) + +static inline int REGISTER_FLD_WAIT(struct drm_device *dev, u32 reg, + u32 val, int start, int end) +{ + int t = 100000; + + while (FLD_GET(REG_READ(reg), start, end) != val) { + if (--t == 0) + return 1; + } + + return 0; +} + +#define REG_FLD_WAIT(reg, val, start, end) \ + REGISTER_FLD_WAIT(dev, reg, val, start, end) + +#define REG_BIT_WAIT(reg, val, bitnum) \ + REGISTER_FLD_WAIT(dev, reg, val, bitnum, bitnum) + +#define MDFLD_DSI_BRIGHTNESS_MAX_LEVEL 100 + +#ifdef DEBUG +#define CHECK_PIPE(pipe) ({ \ + const typeof(pipe) __pipe = (pipe); \ + BUG_ON(__pipe != 0 && __pipe != 2); \ + __pipe; }) +#else +#define CHECK_PIPE(pipe) (pipe) +#endif + +/* + * Actual MIPIA->MIPIC reg offset is 0x800, value 0x400 is valid for 0 and 2 + */ +#define REG_OFFSET(pipe) (CHECK_PIPE(pipe) * 0x400) + +/* mdfld DSI controller registers */ +#define MIPI_DEVICE_READY_REG(pipe) (0xb000 + REG_OFFSET(pipe)) +#define MIPI_INTR_STAT_REG(pipe) (0xb004 + REG_OFFSET(pipe)) +#define MIPI_INTR_EN_REG(pipe) (0xb008 + REG_OFFSET(pipe)) +#define MIPI_DSI_FUNC_PRG_REG(pipe) (0xb00c + REG_OFFSET(pipe)) +#define MIPI_HS_TX_TIMEOUT_REG(pipe) (0xb010 + REG_OFFSET(pipe)) +#define MIPI_LP_RX_TIMEOUT_REG(pipe) (0xb014 + REG_OFFSET(pipe)) +#define MIPI_TURN_AROUND_TIMEOUT_REG(pipe) (0xb018 + REG_OFFSET(pipe)) +#define MIPI_DEVICE_RESET_TIMER_REG(pipe) (0xb01c + REG_OFFSET(pipe)) +#define MIPI_DPI_RESOLUTION_REG(pipe) (0xb020 + REG_OFFSET(pipe)) +#define MIPI_DBI_FIFO_THROTTLE_REG(pipe) (0xb024 + REG_OFFSET(pipe)) +#define MIPI_HSYNC_COUNT_REG(pipe) (0xb028 + REG_OFFSET(pipe)) +#define MIPI_HBP_COUNT_REG(pipe) (0xb02c + REG_OFFSET(pipe)) +#define MIPI_HFP_COUNT_REG(pipe) (0xb030 + REG_OFFSET(pipe)) +#define MIPI_HACTIVE_COUNT_REG(pipe) (0xb034 + REG_OFFSET(pipe)) +#define MIPI_VSYNC_COUNT_REG(pipe) (0xb038 + REG_OFFSET(pipe)) +#define MIPI_VBP_COUNT_REG(pipe) (0xb03c + REG_OFFSET(pipe)) +#define MIPI_VFP_COUNT_REG(pipe) (0xb040 + REG_OFFSET(pipe)) +#define MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe) (0xb044 + REG_OFFSET(pipe)) +#define MIPI_DPI_CONTROL_REG(pipe) (0xb048 + REG_OFFSET(pipe)) +#define MIPI_DPI_DATA_REG(pipe) (0xb04c + REG_OFFSET(pipe)) +#define MIPI_INIT_COUNT_REG(pipe) (0xb050 + REG_OFFSET(pipe)) +#define MIPI_MAX_RETURN_PACK_SIZE_REG(pipe) (0xb054 + REG_OFFSET(pipe)) +#define MIPI_VIDEO_MODE_FORMAT_REG(pipe) (0xb058 + REG_OFFSET(pipe)) +#define MIPI_EOT_DISABLE_REG(pipe) (0xb05c + REG_OFFSET(pipe)) +#define MIPI_LP_BYTECLK_REG(pipe) (0xb060 + REG_OFFSET(pipe)) +#define MIPI_LP_GEN_DATA_REG(pipe) (0xb064 + REG_OFFSET(pipe)) +#define MIPI_HS_GEN_DATA_REG(pipe) (0xb068 + REG_OFFSET(pipe)) +#define MIPI_LP_GEN_CTRL_REG(pipe) (0xb06c + REG_OFFSET(pipe)) +#define MIPI_HS_GEN_CTRL_REG(pipe) (0xb070 + REG_OFFSET(pipe)) +#define MIPI_GEN_FIFO_STAT_REG(pipe) (0xb074 + REG_OFFSET(pipe)) +#define MIPI_HS_LS_DBI_ENABLE_REG(pipe) (0xb078 + REG_OFFSET(pipe)) +#define MIPI_DPHY_PARAM_REG(pipe) (0xb080 + REG_OFFSET(pipe)) +#define MIPI_DBI_BW_CTRL_REG(pipe) (0xb084 + REG_OFFSET(pipe)) +#define MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe) (0xb088 + REG_OFFSET(pipe)) + +#define MIPI_CTRL_REG(pipe) (0xb104 + REG_OFFSET(pipe)) +#define MIPI_DATA_ADD_REG(pipe) (0xb108 + REG_OFFSET(pipe)) +#define MIPI_DATA_LEN_REG(pipe) (0xb10c + REG_OFFSET(pipe)) +#define MIPI_CMD_ADD_REG(pipe) (0xb110 + REG_OFFSET(pipe)) +#define MIPI_CMD_LEN_REG(pipe) (0xb114 + REG_OFFSET(pipe)) + +/* non-uniform reg offset */ +#define MIPI_PORT_CONTROL(pipe) (CHECK_PIPE(pipe) ? MIPI_C : MIPI) + +#define DSI_DEVICE_READY (0x1) +#define DSI_POWER_STATE_ULPS_ENTER (0x2 << 1) +#define DSI_POWER_STATE_ULPS_EXIT (0x1 << 1) +#define DSI_POWER_STATE_ULPS_OFFSET (0x1) + + +#define DSI_ONE_DATA_LANE (0x1) +#define DSI_TWO_DATA_LANE (0x2) +#define DSI_THREE_DATA_LANE (0X3) +#define DSI_FOUR_DATA_LANE (0x4) +#define DSI_DPI_VIRT_CHANNEL_OFFSET (0x3) +#define DSI_DBI_VIRT_CHANNEL_OFFSET (0x5) +#define DSI_DPI_COLOR_FORMAT_RGB565 (0x01 << 7) +#define DSI_DPI_COLOR_FORMAT_RGB666 (0x02 << 7) +#define DSI_DPI_COLOR_FORMAT_RGB666_UNPACK (0x03 << 7) +#define DSI_DPI_COLOR_FORMAT_RGB888 (0x04 << 7) +#define DSI_DBI_COLOR_FORMAT_OPTION2 (0x05 << 13) + +#define DSI_INTR_STATE_RXSOTERROR BIT(0) + +#define DSI_INTR_STATE_SPL_PKG_SENT BIT(30) +#define DSI_INTR_STATE_TE BIT(31) + +#define DSI_HS_TX_TIMEOUT_MASK (0xffffff) + +#define DSI_LP_RX_TIMEOUT_MASK (0xffffff) + +#define DSI_TURN_AROUND_TIMEOUT_MASK (0x3f) + +#define DSI_RESET_TIMER_MASK (0xffff) + +#define DSI_DBI_FIFO_WM_HALF (0x0) +#define DSI_DBI_FIFO_WM_QUARTER (0x1) +#define DSI_DBI_FIFO_WM_LOW (0x2) + +#define DSI_DPI_TIMING_MASK (0xffff) + +#define DSI_INIT_TIMER_MASK (0xffff) + +#define DSI_DBI_RETURN_PACK_SIZE_MASK (0x3ff) + +#define DSI_LP_BYTECLK_MASK (0x0ffff) + +#define DSI_HS_CTRL_GEN_SHORT_W0 (0x03) +#define DSI_HS_CTRL_GEN_SHORT_W1 (0x13) +#define DSI_HS_CTRL_GEN_SHORT_W2 (0x23) +#define DSI_HS_CTRL_GEN_R0 (0x04) +#define DSI_HS_CTRL_GEN_R1 (0x14) +#define DSI_HS_CTRL_GEN_R2 (0x24) +#define DSI_HS_CTRL_GEN_LONG_W (0x29) +#define DSI_HS_CTRL_MCS_SHORT_W0 (0x05) +#define DSI_HS_CTRL_MCS_SHORT_W1 (0x15) +#define DSI_HS_CTRL_MCS_R0 (0x06) +#define DSI_HS_CTRL_MCS_LONG_W (0x39) +#define DSI_HS_CTRL_VC_OFFSET (0x06) +#define DSI_HS_CTRL_WC_OFFSET (0x08) + +#define DSI_FIFO_GEN_HS_DATA_FULL BIT(0) +#define DSI_FIFO_GEN_HS_DATA_HALF_EMPTY BIT(1) +#define DSI_FIFO_GEN_HS_DATA_EMPTY BIT(2) +#define DSI_FIFO_GEN_LP_DATA_FULL BIT(8) +#define DSI_FIFO_GEN_LP_DATA_HALF_EMPTY BIT(9) +#define DSI_FIFO_GEN_LP_DATA_EMPTY BIT(10) +#define DSI_FIFO_GEN_HS_CTRL_FULL BIT(16) +#define DSI_FIFO_GEN_HS_CTRL_HALF_EMPTY BIT(17) +#define DSI_FIFO_GEN_HS_CTRL_EMPTY BIT(18) +#define DSI_FIFO_GEN_LP_CTRL_FULL BIT(24) +#define DSI_FIFO_GEN_LP_CTRL_HALF_EMPTY BIT(25) +#define DSI_FIFO_GEN_LP_CTRL_EMPTY BIT(26) +#define DSI_FIFO_DBI_EMPTY BIT(27) +#define DSI_FIFO_DPI_EMPTY BIT(28) + +#define DSI_DBI_HS_LP_SWITCH_MASK (0x1) + +#define DSI_HS_LP_SWITCH_COUNTER_OFFSET (0x0) +#define DSI_LP_HS_SWITCH_COUNTER_OFFSET (0x16) + +#define DSI_DPI_CTRL_HS_SHUTDOWN (0x00000001) +#define DSI_DPI_CTRL_HS_TURN_ON (0x00000002) + +/*dsi power modes*/ +#define DSI_POWER_MODE_DISPLAY_ON BIT(2) +#define DSI_POWER_MODE_NORMAL_ON BIT(3) +#define DSI_POWER_MODE_SLEEP_OUT BIT(4) +#define DSI_POWER_MODE_PARTIAL_ON BIT(5) +#define DSI_POWER_MODE_IDLE_ON BIT(6) + +enum { + MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE = 1, + MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS = 2, + MDFLD_DSI_VIDEO_BURST_MODE = 3, +}; + +#define DSI_DPI_COMPLETE_LAST_LINE BIT(2) +#define DSI_DPI_DISABLE_BTA BIT(3) + +struct mdfld_dsi_connector { + struct psb_intel_connector base; + + int pipe; + void *private; + void *pkg_sender; + + /* Connection status */ + enum drm_connector_status status; +}; + +struct mdfld_dsi_encoder { + struct psb_intel_encoder base; + void *private; +}; + +/* + * DSI config, consists of one DSI connector, two DSI encoders. + * DRM will pick up on DSI encoder basing on differents configs. + */ +struct mdfld_dsi_config { + struct drm_device *dev; + struct drm_display_mode *fixed_mode; + struct drm_display_mode *mode; + + struct mdfld_dsi_connector *connector; + struct mdfld_dsi_encoder *encoder; + + int changed; + + int bpp; + int lane_count; + /*Virtual channel number for this encoder*/ + int channel_num; + /*video mode configure*/ + int video_mode; + + int dvr_ic_inited; +}; + +static inline struct mdfld_dsi_connector *mdfld_dsi_connector( + struct drm_connector *connector) +{ + struct psb_intel_connector *psb_connector; + + psb_connector = to_psb_intel_connector(connector); + + return container_of(psb_connector, struct mdfld_dsi_connector, base); +} + +static inline struct mdfld_dsi_encoder *mdfld_dsi_encoder( + struct drm_encoder *encoder) +{ + struct psb_intel_encoder *psb_encoder; + + psb_encoder = to_psb_intel_encoder(encoder); + + return container_of(psb_encoder, struct mdfld_dsi_encoder, base); +} + +static inline struct mdfld_dsi_config * + mdfld_dsi_get_config(struct mdfld_dsi_connector *connector) +{ + if (!connector) + return NULL; + return (struct mdfld_dsi_config *)connector->private; +} + +static inline void *mdfld_dsi_get_pkg_sender(struct mdfld_dsi_config *config) +{ + struct mdfld_dsi_connector *dsi_connector; + + if (!config) + return NULL; + + dsi_connector = config->connector; + + if (!dsi_connector) + return NULL; + + return dsi_connector->pkg_sender; +} + +static inline struct mdfld_dsi_config * + mdfld_dsi_encoder_get_config(struct mdfld_dsi_encoder *encoder) +{ + if (!encoder) + return NULL; + return (struct mdfld_dsi_config *)encoder->private; +} + +static inline struct mdfld_dsi_connector * + mdfld_dsi_encoder_get_connector(struct mdfld_dsi_encoder *encoder) +{ + struct mdfld_dsi_config *config; + + if (!encoder) + return NULL; + + config = mdfld_dsi_encoder_get_config(encoder); + if (!config) + return NULL; + + return config->connector; +} + +static inline void *mdfld_dsi_encoder_get_pkg_sender( + struct mdfld_dsi_encoder *encoder) +{ + struct mdfld_dsi_config *dsi_config; + + dsi_config = mdfld_dsi_encoder_get_config(encoder); + if (!dsi_config) + return NULL; + + return mdfld_dsi_get_pkg_sender(dsi_config); +} + +static inline int mdfld_dsi_encoder_get_pipe(struct mdfld_dsi_encoder *encoder) +{ + struct mdfld_dsi_connector *connector; + + if (!encoder) + return -1; + + connector = mdfld_dsi_encoder_get_connector(encoder); + if (!connector) + return -1; + return connector->pipe; +} + +/* Export functions */ +extern void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, + u32 gen_fifo_stat_reg, u32 fifo_stat); +extern void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, + int pipe); +extern void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, + int level); +extern void mdfld_dsi_output_init(struct drm_device *dev, + int pipe, + const struct panel_funcs *p_vid_funcs); +extern void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, + int pipe); + +extern int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, + u32 *mode, bool hs); +extern int mdfld_dsi_panel_reset(int pipe); + +#endif /*__MDFLD_DSI_OUTPUT_H__*/ diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c new file mode 100644 index 000000000000..baa0e14165e0 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c @@ -0,0 +1,694 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Jackie Li<yaodong.li@intel.com> + */ + +#include <linux/freezer.h> + +#include "mdfld_dsi_output.h" +#include "mdfld_dsi_pkg_sender.h" +#include "mdfld_dsi_dpi.h" + +#define MDFLD_DSI_READ_MAX_COUNT 5000 + +enum data_type { + DSI_DT_GENERIC_SHORT_WRITE_0 = 0x03, + DSI_DT_GENERIC_SHORT_WRITE_1 = 0x13, + DSI_DT_GENERIC_SHORT_WRITE_2 = 0x23, + DSI_DT_GENERIC_READ_0 = 0x04, + DSI_DT_GENERIC_READ_1 = 0x14, + DSI_DT_GENERIC_READ_2 = 0x24, + DSI_DT_GENERIC_LONG_WRITE = 0x29, + DSI_DT_DCS_SHORT_WRITE_0 = 0x05, + DSI_DT_DCS_SHORT_WRITE_1 = 0x15, + DSI_DT_DCS_READ = 0x06, + DSI_DT_DCS_LONG_WRITE = 0x39, +}; + +enum { + MDFLD_DSI_PANEL_MODE_SLEEP = 0x1, +}; + +enum { + MDFLD_DSI_PKG_SENDER_FREE = 0x0, + MDFLD_DSI_PKG_SENDER_BUSY = 0x1, +}; + +static const char *const dsi_errors[] = { + "RX SOT Error", + "RX SOT Sync Error", + "RX EOT Sync Error", + "RX Escape Mode Entry Error", + "RX LP TX Sync Error", + "RX HS Receive Timeout Error", + "RX False Control Error", + "RX ECC Single Bit Error", + "RX ECC Multibit Error", + "RX Checksum Error", + "RX DSI Data Type Not Recognised", + "RX DSI VC ID Invalid", + "TX False Control Error", + "TX ECC Single Bit Error", + "TX ECC Multibit Error", + "TX Checksum Error", + "TX DSI Data Type Not Recognised", + "TX DSI VC ID invalid", + "High Contention", + "Low contention", + "DPI FIFO Under run", + "HS TX Timeout", + "LP RX Timeout", + "Turn Around ACK Timeout", + "ACK With No Error", + "RX Invalid TX Length", + "RX Prot Violation", + "HS Generic Write FIFO Full", + "LP Generic Write FIFO Full", + "Generic Read Data Avail" + "Special Packet Sent", + "Tearing Effect", +}; + +static inline int wait_for_gen_fifo_empty(struct mdfld_dsi_pkg_sender *sender, + u32 mask) +{ + struct drm_device *dev = sender->dev; + u32 gen_fifo_stat_reg = sender->mipi_gen_fifo_stat_reg; + int retry = 0xffff; + + while (retry--) { + if ((mask & REG_READ(gen_fifo_stat_reg)) == mask) + return 0; + udelay(100); + } + DRM_ERROR("fifo is NOT empty 0x%08x\n", REG_READ(gen_fifo_stat_reg)); + return -EIO; +} + +static int wait_for_all_fifos_empty(struct mdfld_dsi_pkg_sender *sender) +{ + return wait_for_gen_fifo_empty(sender, (BIT(2) | BIT(10) | BIT(18) | + BIT(26) | BIT(27) | BIT(28))); +} + +static int wait_for_lp_fifos_empty(struct mdfld_dsi_pkg_sender *sender) +{ + return wait_for_gen_fifo_empty(sender, (BIT(10) | BIT(26))); +} + +static int wait_for_hs_fifos_empty(struct mdfld_dsi_pkg_sender *sender) +{ + return wait_for_gen_fifo_empty(sender, (BIT(2) | BIT(18))); +} + +static int handle_dsi_error(struct mdfld_dsi_pkg_sender *sender, u32 mask) +{ + u32 intr_stat_reg = sender->mipi_intr_stat_reg; + struct drm_device *dev = sender->dev; + + dev_dbg(sender->dev->dev, "Handling error 0x%08x\n", mask); + + switch (mask) { + case BIT(0): + case BIT(1): + case BIT(2): + case BIT(3): + case BIT(4): + case BIT(5): + case BIT(6): + case BIT(7): + case BIT(8): + case BIT(9): + case BIT(10): + case BIT(11): + case BIT(12): + case BIT(13): + dev_dbg(sender->dev->dev, "No Action required\n"); + break; + case BIT(14): + /*wait for all fifo empty*/ + /*wait_for_all_fifos_empty(sender)*/; + break; + case BIT(15): + dev_dbg(sender->dev->dev, "No Action required\n"); + break; + case BIT(16): + break; + case BIT(17): + break; + case BIT(18): + case BIT(19): + dev_dbg(sender->dev->dev, "High/Low contention detected\n"); + /*wait for contention recovery time*/ + /*mdelay(10);*/ + /*wait for all fifo empty*/ + if (0) + wait_for_all_fifos_empty(sender); + break; + case BIT(20): + dev_dbg(sender->dev->dev, "No Action required\n"); + break; + case BIT(21): + /*wait for all fifo empty*/ + /*wait_for_all_fifos_empty(sender);*/ + break; + case BIT(22): + break; + case BIT(23): + case BIT(24): + case BIT(25): + case BIT(26): + case BIT(27): + dev_dbg(sender->dev->dev, "HS Gen fifo full\n"); + REG_WRITE(intr_stat_reg, mask); + wait_for_hs_fifos_empty(sender); + break; + case BIT(28): + dev_dbg(sender->dev->dev, "LP Gen fifo full\n"); + REG_WRITE(intr_stat_reg, mask); + wait_for_lp_fifos_empty(sender); + break; + case BIT(29): + case BIT(30): + case BIT(31): + dev_dbg(sender->dev->dev, "No Action required\n"); + break; + } + + if (mask & REG_READ(intr_stat_reg)) + dev_dbg(sender->dev->dev, + "Cannot clean interrupt 0x%08x\n", mask); + return 0; +} + +static int dsi_error_handler(struct mdfld_dsi_pkg_sender *sender) +{ + struct drm_device *dev = sender->dev; + u32 intr_stat_reg = sender->mipi_intr_stat_reg; + u32 mask; + u32 intr_stat; + int i; + int err = 0; + + intr_stat = REG_READ(intr_stat_reg); + + for (i = 0; i < 32; i++) { + mask = (0x00000001UL) << i; + if (intr_stat & mask) { + dev_dbg(sender->dev->dev, "[DSI]: %s\n", dsi_errors[i]); + err = handle_dsi_error(sender, mask); + if (err) + DRM_ERROR("Cannot handle error\n"); + } + } + return err; +} + +static int send_short_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type, + u8 cmd, u8 param, bool hs) +{ + struct drm_device *dev = sender->dev; + u32 ctrl_reg; + u32 val; + u8 virtual_channel = 0; + + if (hs) { + ctrl_reg = sender->mipi_hs_gen_ctrl_reg; + + /* FIXME: wait_for_hs_fifos_empty(sender); */ + } else { + ctrl_reg = sender->mipi_lp_gen_ctrl_reg; + + /* FIXME: wait_for_lp_fifos_empty(sender); */ + } + + val = FLD_VAL(param, 23, 16) | FLD_VAL(cmd, 15, 8) | + FLD_VAL(virtual_channel, 7, 6) | FLD_VAL(data_type, 5, 0); + + REG_WRITE(ctrl_reg, val); + + return 0; +} + +static int send_long_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type, + u8 *data, int len, bool hs) +{ + struct drm_device *dev = sender->dev; + u32 ctrl_reg; + u32 data_reg; + u32 val; + u8 *p; + u8 b1, b2, b3, b4; + u8 virtual_channel = 0; + int i; + + if (hs) { + ctrl_reg = sender->mipi_hs_gen_ctrl_reg; + data_reg = sender->mipi_hs_gen_data_reg; + + /* FIXME: wait_for_hs_fifos_empty(sender); */ + } else { + ctrl_reg = sender->mipi_lp_gen_ctrl_reg; + data_reg = sender->mipi_lp_gen_data_reg; + + /* FIXME: wait_for_lp_fifos_empty(sender); */ + } + + p = data; + for (i = 0; i < len / 4; i++) { + b1 = *p++; + b2 = *p++; + b3 = *p++; + b4 = *p++; + + REG_WRITE(data_reg, b4 << 24 | b3 << 16 | b2 << 8 | b1); + } + + i = len % 4; + if (i) { + b1 = 0; b2 = 0; b3 = 0; + + switch (i) { + case 3: + b1 = *p++; + b2 = *p++; + b3 = *p++; + break; + case 2: + b1 = *p++; + b2 = *p++; + break; + case 1: + b1 = *p++; + break; + } + + REG_WRITE(data_reg, b3 << 16 | b2 << 8 | b1); + } + + val = FLD_VAL(len, 23, 8) | FLD_VAL(virtual_channel, 7, 6) | + FLD_VAL(data_type, 5, 0); + + REG_WRITE(ctrl_reg, val); + + return 0; +} + +static int send_pkg_prepare(struct mdfld_dsi_pkg_sender *sender, u8 data_type, + u8 *data, u16 len) +{ + u8 cmd; + + switch (data_type) { + case DSI_DT_DCS_SHORT_WRITE_0: + case DSI_DT_DCS_SHORT_WRITE_1: + case DSI_DT_DCS_LONG_WRITE: + cmd = *data; + break; + default: + return 0; + } + + /*this prevents other package sending while doing msleep*/ + sender->status = MDFLD_DSI_PKG_SENDER_BUSY; + + /*wait for 120 milliseconds in case exit_sleep_mode just be sent*/ + if (unlikely(cmd == DCS_ENTER_SLEEP_MODE)) { + /*TODO: replace it with msleep later*/ + mdelay(120); + } + + if (unlikely(cmd == DCS_EXIT_SLEEP_MODE)) { + /*TODO: replace it with msleep later*/ + mdelay(120); + } + return 0; +} + +static int send_pkg_done(struct mdfld_dsi_pkg_sender *sender, u8 data_type, + u8 *data, u16 len) +{ + u8 cmd; + + switch (data_type) { + case DSI_DT_DCS_SHORT_WRITE_0: + case DSI_DT_DCS_SHORT_WRITE_1: + case DSI_DT_DCS_LONG_WRITE: + cmd = *data; + break; + default: + return 0; + } + + /*update panel status*/ + if (unlikely(cmd == DCS_ENTER_SLEEP_MODE)) { + sender->panel_mode |= MDFLD_DSI_PANEL_MODE_SLEEP; + /*TODO: replace it with msleep later*/ + mdelay(120); + } else if (unlikely(cmd == DCS_EXIT_SLEEP_MODE)) { + sender->panel_mode &= ~MDFLD_DSI_PANEL_MODE_SLEEP; + /*TODO: replace it with msleep later*/ + mdelay(120); + } else if (unlikely(cmd == DCS_SOFT_RESET)) { + /*TODO: replace it with msleep later*/ + mdelay(5); + } + + sender->status = MDFLD_DSI_PKG_SENDER_FREE; + + return 0; +} + +static int send_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type, + u8 *data, u16 len, bool hs) +{ + int ret; + + /*handle DSI error*/ + ret = dsi_error_handler(sender); + if (ret) { + DRM_ERROR("Error handling failed\n"); + return -EAGAIN; + } + + /* send pkg */ + if (sender->status == MDFLD_DSI_PKG_SENDER_BUSY) { + DRM_ERROR("sender is busy\n"); + return -EAGAIN; + } + + ret = send_pkg_prepare(sender, data_type, data, len); + if (ret) { + DRM_ERROR("send_pkg_prepare error\n"); + return ret; + } + + switch (data_type) { + case DSI_DT_GENERIC_SHORT_WRITE_0: + case DSI_DT_GENERIC_SHORT_WRITE_1: + case DSI_DT_GENERIC_SHORT_WRITE_2: + case DSI_DT_GENERIC_READ_0: + case DSI_DT_GENERIC_READ_1: + case DSI_DT_GENERIC_READ_2: + case DSI_DT_DCS_SHORT_WRITE_0: + case DSI_DT_DCS_SHORT_WRITE_1: + case DSI_DT_DCS_READ: + ret = send_short_pkg(sender, data_type, data[0], data[1], hs); + break; + case DSI_DT_GENERIC_LONG_WRITE: + case DSI_DT_DCS_LONG_WRITE: + ret = send_long_pkg(sender, data_type, data, len, hs); + break; + } + + send_pkg_done(sender, data_type, data, len); + + /*FIXME: should I query complete and fifo empty here?*/ + + return ret; +} + +int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender, u8 *data, + u32 len, bool hs) +{ + unsigned long flags; + + if (!sender || !data || !len) { + DRM_ERROR("Invalid parameters\n"); + return -EINVAL; + } + + spin_lock_irqsave(&sender->lock, flags); + send_pkg(sender, DSI_DT_DCS_LONG_WRITE, data, len, hs); + spin_unlock_irqrestore(&sender->lock, flags); + + return 0; +} + +int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender, u8 cmd, + u8 param, u8 param_num, bool hs) +{ + u8 data[2]; + unsigned long flags; + u8 data_type; + + if (!sender) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + data[0] = cmd; + + if (param_num) { + data_type = DSI_DT_DCS_SHORT_WRITE_1; + data[1] = param; + } else { + data_type = DSI_DT_DCS_SHORT_WRITE_0; + data[1] = 0; + } + + spin_lock_irqsave(&sender->lock, flags); + send_pkg(sender, data_type, data, sizeof(data), hs); + spin_unlock_irqrestore(&sender->lock, flags); + + return 0; +} + +int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender, u8 param0, + u8 param1, u8 param_num, bool hs) +{ + u8 data[2]; + unsigned long flags; + u8 data_type; + + if (!sender || param_num > 2) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + switch (param_num) { + case 0: + data_type = DSI_DT_GENERIC_SHORT_WRITE_0; + data[0] = 0; + data[1] = 0; + break; + case 1: + data_type = DSI_DT_GENERIC_SHORT_WRITE_1; + data[0] = param0; + data[1] = 0; + break; + case 2: + data_type = DSI_DT_GENERIC_SHORT_WRITE_2; + data[0] = param0; + data[1] = param1; + break; + } + + spin_lock_irqsave(&sender->lock, flags); + send_pkg(sender, data_type, data, sizeof(data), hs); + spin_unlock_irqrestore(&sender->lock, flags); + + return 0; +} + +int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender, u8 *data, + u32 len, bool hs) +{ + unsigned long flags; + + if (!sender || !data || !len) { + DRM_ERROR("Invalid parameters\n"); + return -EINVAL; + } + + spin_lock_irqsave(&sender->lock, flags); + send_pkg(sender, DSI_DT_GENERIC_LONG_WRITE, data, len, hs); + spin_unlock_irqrestore(&sender->lock, flags); + + return 0; +} + +static int __read_panel_data(struct mdfld_dsi_pkg_sender *sender, u8 data_type, + u8 *data, u16 len, u32 *data_out, u16 len_out, bool hs) +{ + unsigned long flags; + struct drm_device *dev = sender->dev; + int i; + u32 gen_data_reg; + int retry = MDFLD_DSI_READ_MAX_COUNT; + + if (!sender || !data_out || !len_out) { + DRM_ERROR("Invalid parameters\n"); + return -EINVAL; + } + + /** + * do reading. + * 0) send out generic read request + * 1) polling read data avail interrupt + * 2) read data + */ + spin_lock_irqsave(&sender->lock, flags); + + REG_WRITE(sender->mipi_intr_stat_reg, BIT(29)); + + if ((REG_READ(sender->mipi_intr_stat_reg) & BIT(29))) + DRM_ERROR("Can NOT clean read data valid interrupt\n"); + + /*send out read request*/ + send_pkg(sender, data_type, data, len, hs); + + /*polling read data avail interrupt*/ + while (retry && !(REG_READ(sender->mipi_intr_stat_reg) & BIT(29))) { + udelay(100); + retry--; + } + + if (!retry) { + spin_unlock_irqrestore(&sender->lock, flags); + return -ETIMEDOUT; + } + + REG_WRITE(sender->mipi_intr_stat_reg, BIT(29)); + + /*read data*/ + if (hs) + gen_data_reg = sender->mipi_hs_gen_data_reg; + else + gen_data_reg = sender->mipi_lp_gen_data_reg; + + for (i = 0; i < len_out; i++) + *(data_out + i) = REG_READ(gen_data_reg); + + spin_unlock_irqrestore(&sender->lock, flags); + + return 0; +} + +int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender, u8 cmd, + u32 *data, u16 len, bool hs) +{ + if (!sender || !data || !len) { + DRM_ERROR("Invalid parameters\n"); + return -EINVAL; + } + + return __read_panel_data(sender, DSI_DT_DCS_READ, &cmd, 1, + data, len, hs); +} + +int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector, + int pipe) +{ + struct mdfld_dsi_pkg_sender *pkg_sender; + struct mdfld_dsi_config *dsi_config = + mdfld_dsi_get_config(dsi_connector); + struct drm_device *dev = dsi_config->dev; + u32 mipi_val = 0; + + if (!dsi_connector) { + DRM_ERROR("Invalid parameter\n"); + return -EINVAL; + } + + pkg_sender = dsi_connector->pkg_sender; + + if (!pkg_sender || IS_ERR(pkg_sender)) { + pkg_sender = kzalloc(sizeof(struct mdfld_dsi_pkg_sender), + GFP_KERNEL); + if (!pkg_sender) { + DRM_ERROR("Create DSI pkg sender failed\n"); + return -ENOMEM; + } + dsi_connector->pkg_sender = (void *)pkg_sender; + } + + pkg_sender->dev = dev; + pkg_sender->dsi_connector = dsi_connector; + pkg_sender->pipe = pipe; + pkg_sender->pkg_num = 0; + pkg_sender->panel_mode = 0; + pkg_sender->status = MDFLD_DSI_PKG_SENDER_FREE; + + /*init regs*/ + if (pipe == 0) { + pkg_sender->dpll_reg = MRST_DPLL_A; + pkg_sender->dspcntr_reg = DSPACNTR; + pkg_sender->pipeconf_reg = PIPEACONF; + pkg_sender->dsplinoff_reg = DSPALINOFF; + pkg_sender->dspsurf_reg = DSPASURF; + pkg_sender->pipestat_reg = PIPEASTAT; + } else if (pipe == 2) { + pkg_sender->dpll_reg = MRST_DPLL_A; + pkg_sender->dspcntr_reg = DSPCCNTR; + pkg_sender->pipeconf_reg = PIPECCONF; + pkg_sender->dsplinoff_reg = DSPCLINOFF; + pkg_sender->dspsurf_reg = DSPCSURF; + pkg_sender->pipestat_reg = PIPECSTAT; + } + + pkg_sender->mipi_intr_stat_reg = MIPI_INTR_STAT_REG(pipe); + pkg_sender->mipi_lp_gen_data_reg = MIPI_LP_GEN_DATA_REG(pipe); + pkg_sender->mipi_hs_gen_data_reg = MIPI_HS_GEN_DATA_REG(pipe); + pkg_sender->mipi_lp_gen_ctrl_reg = MIPI_LP_GEN_CTRL_REG(pipe); + pkg_sender->mipi_hs_gen_ctrl_reg = MIPI_HS_GEN_CTRL_REG(pipe); + pkg_sender->mipi_gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe); + pkg_sender->mipi_data_addr_reg = MIPI_DATA_ADD_REG(pipe); + pkg_sender->mipi_data_len_reg = MIPI_DATA_LEN_REG(pipe); + pkg_sender->mipi_cmd_addr_reg = MIPI_CMD_ADD_REG(pipe); + pkg_sender->mipi_cmd_len_reg = MIPI_CMD_LEN_REG(pipe); + + /*init lock*/ + spin_lock_init(&pkg_sender->lock); + + if (mdfld_get_panel_type(dev, pipe) != TC35876X) { + /** + * For video mode, don't enable DPI timing output here, + * will init the DPI timing output during mode setting. + */ + mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX; + + if (pipe == 0) + mipi_val |= 0x2; + + REG_WRITE(MIPI_PORT_CONTROL(pipe), mipi_val); + REG_READ(MIPI_PORT_CONTROL(pipe)); + + /* do dsi controller init */ + mdfld_dsi_controller_init(dsi_config, pipe); + } + + return 0; +} + +void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender) +{ + if (!sender || IS_ERR(sender)) + return; + + /*free*/ + kfree(sender); +} + + diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.h b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.h new file mode 100644 index 000000000000..459cd7ea8b81 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.h @@ -0,0 +1,92 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Jackie Li<yaodong.li@intel.com> + */ +#ifndef __MDFLD_DSI_PKG_SENDER_H__ +#define __MDFLD_DSI_PKG_SENDER_H__ + +#include <linux/kthread.h> + +#define MDFLD_MAX_DCS_PARAM 8 + +struct mdfld_dsi_pkg_sender { + struct drm_device *dev; + struct mdfld_dsi_connector *dsi_connector; + u32 status; + u32 panel_mode; + + int pipe; + + spinlock_t lock; + + u32 pkg_num; + + /* Registers */ + u32 dpll_reg; + u32 dspcntr_reg; + u32 pipeconf_reg; + u32 pipestat_reg; + u32 dsplinoff_reg; + u32 dspsurf_reg; + + u32 mipi_intr_stat_reg; + u32 mipi_lp_gen_data_reg; + u32 mipi_hs_gen_data_reg; + u32 mipi_lp_gen_ctrl_reg; + u32 mipi_hs_gen_ctrl_reg; + u32 mipi_gen_fifo_stat_reg; + u32 mipi_data_addr_reg; + u32 mipi_data_len_reg; + u32 mipi_cmd_addr_reg; + u32 mipi_cmd_len_reg; +}; + +/* DCS definitions */ +#define DCS_SOFT_RESET 0x01 +#define DCS_ENTER_SLEEP_MODE 0x10 +#define DCS_EXIT_SLEEP_MODE 0x11 +#define DCS_SET_DISPLAY_OFF 0x28 +#define DCS_SET_DISPLAY_ON 0x29 +#define DCS_SET_COLUMN_ADDRESS 0x2a +#define DCS_SET_PAGE_ADDRESS 0x2b +#define DCS_WRITE_MEM_START 0x2c +#define DCS_SET_TEAR_OFF 0x34 +#define DCS_SET_TEAR_ON 0x35 + +extern int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector, + int pipe); +extern void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender); +int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender, u8 cmd, + u8 param, u8 param_num, bool hs); +int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender, u8 *data, + u32 len, bool hs); +int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender, u8 param0, + u8 param1, u8 param_num, bool hs); +int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender, u8 *data, + u32 len, bool hs); +/* Read interfaces */ +int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender, u8 cmd, + u32 *data, u16 len, bool hs); + +#endif diff --git a/drivers/gpu/drm/gma500/mdfld_intel_display.c b/drivers/gpu/drm/gma500/mdfld_intel_display.c new file mode 100644 index 000000000000..a35a2921bdf7 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_intel_display.c @@ -0,0 +1,1180 @@ +/* + * Copyright © 2006-2007 Intel Corporation + * + * 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. + * + * Authors: + * Eric Anholt <eric@anholt.net> + */ + +#include <linux/i2c.h> +#include <linux/pm_runtime.h> + +#include <drm/drmP.h> +#include "psb_intel_reg.h" +#include "psb_intel_display.h" +#include "framebuffer.h" +#include "mdfld_output.h" +#include "mdfld_dsi_output.h" + +/* Hardcoded currently */ +static int ksel = KSEL_CRYSTAL_19; + +struct psb_intel_range_t { + int min, max; +}; + +struct mrst_limit_t { + struct psb_intel_range_t dot, m, p1; +}; + +struct mrst_clock_t { + /* derived values */ + int dot; + int m; + int p1; +}; + +#define COUNT_MAX 0x10000000 + +void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe) +{ + int count, temp; + u32 pipeconf_reg = PIPEACONF; + + switch (pipe) { + case 0: + break; + case 1: + pipeconf_reg = PIPEBCONF; + break; + case 2: + pipeconf_reg = PIPECCONF; + break; + default: + DRM_ERROR("Illegal Pipe Number.\n"); + return; + } + + /* FIXME JLIU7_PO */ + psb_intel_wait_for_vblank(dev); + return; + + /* Wait for for the pipe disable to take effect. */ + for (count = 0; count < COUNT_MAX; count++) { + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_PIPE_STATE) == 0) + break; + } +} + +void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe) +{ + int count, temp; + u32 pipeconf_reg = PIPEACONF; + + switch (pipe) { + case 0: + break; + case 1: + pipeconf_reg = PIPEBCONF; + break; + case 2: + pipeconf_reg = PIPECCONF; + break; + default: + DRM_ERROR("Illegal Pipe Number.\n"); + return; + } + + /* FIXME JLIU7_PO */ + psb_intel_wait_for_vblank(dev); + return; + + /* Wait for for the pipe enable to take effect. */ + for (count = 0; count < COUNT_MAX; count++) { + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_PIPE_STATE) == 1) + break; + } +} + +static void psb_intel_crtc_prepare(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); +} + +static void psb_intel_crtc_commit(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +} + +static bool psb_intel_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +/** + * Return the pipe currently connected to the panel fitter, + * or -1 if the panel fitter is not present or not in use + */ +static int psb_intel_panel_fitter_pipe(struct drm_device *dev) +{ + u32 pfit_control; + + pfit_control = REG_READ(PFIT_CONTROL); + + /* See if the panel fitter is in use */ + if ((pfit_control & PFIT_ENABLE) == 0) + return -1; + + /* 965 can place panel fitter on either pipe */ + return (pfit_control >> 29) & 0x3; +} + +static struct drm_device globle_dev; + +void mdfld__intel_plane_set_alpha(int enable) +{ + struct drm_device *dev = &globle_dev; + int dspcntr_reg = DSPACNTR; + u32 dspcntr; + + dspcntr = REG_READ(dspcntr_reg); + + if (enable) { + dspcntr &= ~DISPPLANE_32BPP_NO_ALPHA; + dspcntr |= DISPPLANE_32BPP; + } else { + dspcntr &= ~DISPPLANE_32BPP; + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + } + + REG_WRITE(dspcntr_reg, dspcntr); +} + +static int check_fb(struct drm_framebuffer *fb) +{ + if (!fb) + return 0; + + switch (fb->bits_per_pixel) { + case 8: + case 16: + case 24: + case 32: + return 0; + default: + DRM_ERROR("Unknown color depth\n"); + return -EINVAL; + } +} + +static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + /* struct drm_i915_master_private *master_priv; */ + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb); + int pipe = psb_intel_crtc->pipe; + unsigned long start, offset; + int dsplinoff = DSPALINOFF; + int dspsurf = DSPASURF; + int dspstride = DSPASTRIDE; + int dspcntr_reg = DSPACNTR; + u32 dspcntr; + int ret; + + memcpy(&globle_dev, dev, sizeof(struct drm_device)); + + dev_dbg(dev->dev, "pipe = 0x%x.\n", pipe); + + /* no fb bound */ + if (!crtc->fb) { + dev_dbg(dev->dev, "No FB bound\n"); + return 0; + } + + ret = check_fb(crtc->fb); + if (ret) + return ret; + + switch (pipe) { + case 0: + dsplinoff = DSPALINOFF; + break; + case 1: + dsplinoff = DSPBLINOFF; + dspsurf = DSPBSURF; + dspstride = DSPBSTRIDE; + dspcntr_reg = DSPBCNTR; + break; + case 2: + dsplinoff = DSPCLINOFF; + dspsurf = DSPCSURF; + dspstride = DSPCSTRIDE; + dspcntr_reg = DSPCCNTR; + break; + default: + DRM_ERROR("Illegal Pipe Number.\n"); + return -EINVAL; + } + + if (!gma_power_begin(dev, true)) + return 0; + + start = psbfb->gtt->offset; + offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); + + REG_WRITE(dspstride, crtc->fb->pitches[0]); + dspcntr = REG_READ(dspcntr_reg); + dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + + switch (crtc->fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (crtc->fb->depth == 15) + dspcntr |= DISPPLANE_15_16BPP; + else + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + break; + } + REG_WRITE(dspcntr_reg, dspcntr); + + dev_dbg(dev->dev, "Writing base %08lX %08lX %d %d\n", + start, offset, x, y); + REG_WRITE(dsplinoff, offset); + REG_READ(dsplinoff); + REG_WRITE(dspsurf, start); + REG_READ(dspsurf); + + gma_power_end(dev); + + return 0; +} + +/* + * Disable the pipe, plane and pll. + * + */ +void mdfld_disable_crtc(struct drm_device *dev, int pipe) +{ + int dpll_reg = MRST_DPLL_A; + int dspcntr_reg = DSPACNTR; + int dspbase_reg = MRST_DSPABASE; + int pipeconf_reg = PIPEACONF; + u32 temp; + + dev_dbg(dev->dev, "pipe = %d\n", pipe); + + + switch (pipe) { + case 0: + break; + case 1: + dpll_reg = MDFLD_DPLL_B; + dspcntr_reg = DSPBCNTR; + dspbase_reg = DSPBSURF; + pipeconf_reg = PIPEBCONF; + break; + case 2: + dpll_reg = MRST_DPLL_A; + dspcntr_reg = DSPCCNTR; + dspbase_reg = MDFLD_DSPCBASE; + pipeconf_reg = PIPECCONF; + break; + default: + DRM_ERROR("Illegal Pipe Number.\n"); + return; + } + + if (pipe != 1) + mdfld_dsi_gen_fifo_ready(dev, MIPI_GEN_FIFO_STAT_REG(pipe), + HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); + + /* Disable display plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(dspcntr_reg, + temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_READ(dspbase_reg); + } + + /* FIXME_JLIU7 MDFLD_PO revisit */ + + /* Next, disable display pipes */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + temp &= ~PIPEACONF_ENABLE; + temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF; + REG_WRITE(pipeconf_reg, temp); + REG_READ(pipeconf_reg); + + /* Wait for for the pipe disable to take effect. */ + mdfldWaitForPipeDisable(dev, pipe); + } + + temp = REG_READ(dpll_reg); + if (temp & DPLL_VCO_ENABLE) { + if ((pipe != 1 && + !((REG_READ(PIPEACONF) | REG_READ(PIPECCONF)) + & PIPEACONF_ENABLE)) || pipe == 1) { + temp &= ~(DPLL_VCO_ENABLE); + REG_WRITE(dpll_reg, temp); + REG_READ(dpll_reg); + /* Wait for the clocks to turn off. */ + /* FIXME_MDFLD PO may need more delay */ + udelay(500); + + if (!(temp & MDFLD_PWR_GATE_EN)) { + /* gating power of DPLL */ + REG_WRITE(dpll_reg, temp | MDFLD_PWR_GATE_EN); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(5000); + } + } + } + +} + +/** + * Sets the power management mode of the pipe and plane. + * + * This code should probably grow support for turning the cursor off and back + * on appropriately at the same time as we're turning the pipe off/on. + */ +static void mdfld_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + int pipe = psb_intel_crtc->pipe; + int dpll_reg = MRST_DPLL_A; + int dspcntr_reg = DSPACNTR; + int dspbase_reg = MRST_DSPABASE; + int pipeconf_reg = PIPEACONF; + u32 pipestat_reg = PIPEASTAT; + u32 pipeconf = dev_priv->pipeconf[pipe]; + u32 temp; + int timeout = 0; + + dev_dbg(dev->dev, "mode = %d, pipe = %d\n", mode, pipe); + +/* FIXME_JLIU7 MDFLD_PO replaced w/ the following function */ +/* mdfld_dbi_dpms (struct drm_device *dev, int pipe, bool enabled) */ + + switch (pipe) { + case 0: + break; + case 1: + dpll_reg = DPLL_B; + dspcntr_reg = DSPBCNTR; + dspbase_reg = MRST_DSPBBASE; + pipeconf_reg = PIPEBCONF; + dpll_reg = MDFLD_DPLL_B; + break; + case 2: + dpll_reg = MRST_DPLL_A; + dspcntr_reg = DSPCCNTR; + dspbase_reg = MDFLD_DSPCBASE; + pipeconf_reg = PIPECCONF; + pipestat_reg = PIPECSTAT; + break; + default: + DRM_ERROR("Illegal Pipe Number.\n"); + return; + } + + if (!gma_power_begin(dev, true)) + return; + + /* XXX: When our outputs are all unaware of DPMS modes other than off + * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. + */ + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + /* Enable the DPLL */ + temp = REG_READ(dpll_reg); + + if ((temp & DPLL_VCO_ENABLE) == 0) { + /* When ungating power of DPLL, needs to wait 0.5us + before enable the VCO */ + if (temp & MDFLD_PWR_GATE_EN) { + temp &= ~MDFLD_PWR_GATE_EN; + REG_WRITE(dpll_reg, temp); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + } + + REG_WRITE(dpll_reg, temp); + REG_READ(dpll_reg); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); + REG_READ(dpll_reg); + + /** + * wait for DSI PLL to lock + * NOTE: only need to poll status of pipe 0 and pipe 1, + * since both MIPI pipes share the same PLL. + */ + while ((pipe != 2) && (timeout < 20000) && + !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { + udelay(150); + timeout++; + } + } + + /* Enable the plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + REG_WRITE(dspcntr_reg, + temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + } + + /* Enable the pipe */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) == 0) { + REG_WRITE(pipeconf_reg, pipeconf); + + /* Wait for for the pipe enable to take effect. */ + mdfldWaitForPipeEnable(dev, pipe); + } + + /*workaround for sighting 3741701 Random X blank display*/ + /*perform w/a in video mode only on pipe A or C*/ + if (pipe == 0 || pipe == 2) { + REG_WRITE(pipestat_reg, REG_READ(pipestat_reg)); + msleep(100); + if (PIPE_VBLANK_STATUS & REG_READ(pipestat_reg)) + dev_dbg(dev->dev, "OK"); + else { + dev_dbg(dev->dev, "STUCK!!!!"); + /*shutdown controller*/ + temp = REG_READ(dspcntr_reg); + REG_WRITE(dspcntr_reg, + temp & ~DISPLAY_PLANE_ENABLE); + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + /*mdfld_dsi_dpi_shut_down(dev, pipe);*/ + REG_WRITE(0xb048, 1); + msleep(100); + temp = REG_READ(pipeconf_reg); + temp &= ~PIPEACONF_ENABLE; + REG_WRITE(pipeconf_reg, temp); + msleep(100); /*wait for pipe disable*/ + REG_WRITE(MIPI_DEVICE_READY_REG(pipe), 0); + msleep(100); + REG_WRITE(0xb004, REG_READ(0xb004)); + /* try to bring the controller back up again*/ + REG_WRITE(MIPI_DEVICE_READY_REG(pipe), 1); + temp = REG_READ(dspcntr_reg); + REG_WRITE(dspcntr_reg, + temp | DISPLAY_PLANE_ENABLE); + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + /*mdfld_dsi_dpi_turn_on(dev, pipe);*/ + REG_WRITE(0xb048, 2); + msleep(100); + temp = REG_READ(pipeconf_reg); + temp |= PIPEACONF_ENABLE; + REG_WRITE(pipeconf_reg, temp); + } + } + + psb_intel_crtc_load_lut(crtc); + + /* Give the overlay scaler a chance to enable + if it's on this pipe */ + /* psb_intel_crtc_dpms_video(crtc, true); TODO */ + + break; + case DRM_MODE_DPMS_OFF: + /* Give the overlay scaler a chance to disable + * if it's on this pipe */ + /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ + if (pipe != 1) + mdfld_dsi_gen_fifo_ready(dev, + MIPI_GEN_FIFO_STAT_REG(pipe), + HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY); + + /* Disable the VGA plane that we never use */ + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* Disable display plane */ + temp = REG_READ(dspcntr_reg); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(dspcntr_reg, + temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(dspbase_reg, REG_READ(dspbase_reg)); + REG_READ(dspbase_reg); + } + + /* Next, disable display pipes */ + temp = REG_READ(pipeconf_reg); + if ((temp & PIPEACONF_ENABLE) != 0) { + temp &= ~PIPEACONF_ENABLE; + temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF; + REG_WRITE(pipeconf_reg, temp); + REG_READ(pipeconf_reg); + + /* Wait for for the pipe disable to take effect. */ + mdfldWaitForPipeDisable(dev, pipe); + } + + temp = REG_READ(dpll_reg); + if (temp & DPLL_VCO_ENABLE) { + if ((pipe != 1 && !((REG_READ(PIPEACONF) + | REG_READ(PIPECCONF)) & PIPEACONF_ENABLE)) + || pipe == 1) { + temp &= ~(DPLL_VCO_ENABLE); + REG_WRITE(dpll_reg, temp); + REG_READ(dpll_reg); + /* Wait for the clocks to turn off. */ + /* FIXME_MDFLD PO may need more delay */ + udelay(500); + } + } + break; + } + gma_power_end(dev); +} + + +#define MDFLD_LIMT_DPLL_19 0 +#define MDFLD_LIMT_DPLL_25 1 +#define MDFLD_LIMT_DPLL_83 2 +#define MDFLD_LIMT_DPLL_100 3 +#define MDFLD_LIMT_DSIPLL_19 4 +#define MDFLD_LIMT_DSIPLL_25 5 +#define MDFLD_LIMT_DSIPLL_83 6 +#define MDFLD_LIMT_DSIPLL_100 7 + +#define MDFLD_DOT_MIN 19750 +#define MDFLD_DOT_MAX 120000 +#define MDFLD_DPLL_M_MIN_19 113 +#define MDFLD_DPLL_M_MAX_19 155 +#define MDFLD_DPLL_P1_MIN_19 2 +#define MDFLD_DPLL_P1_MAX_19 10 +#define MDFLD_DPLL_M_MIN_25 101 +#define MDFLD_DPLL_M_MAX_25 130 +#define MDFLD_DPLL_P1_MIN_25 2 +#define MDFLD_DPLL_P1_MAX_25 10 +#define MDFLD_DPLL_M_MIN_83 64 +#define MDFLD_DPLL_M_MAX_83 64 +#define MDFLD_DPLL_P1_MIN_83 2 +#define MDFLD_DPLL_P1_MAX_83 2 +#define MDFLD_DPLL_M_MIN_100 64 +#define MDFLD_DPLL_M_MAX_100 64 +#define MDFLD_DPLL_P1_MIN_100 2 +#define MDFLD_DPLL_P1_MAX_100 2 +#define MDFLD_DSIPLL_M_MIN_19 131 +#define MDFLD_DSIPLL_M_MAX_19 175 +#define MDFLD_DSIPLL_P1_MIN_19 3 +#define MDFLD_DSIPLL_P1_MAX_19 8 +#define MDFLD_DSIPLL_M_MIN_25 97 +#define MDFLD_DSIPLL_M_MAX_25 140 +#define MDFLD_DSIPLL_P1_MIN_25 3 +#define MDFLD_DSIPLL_P1_MAX_25 9 +#define MDFLD_DSIPLL_M_MIN_83 33 +#define MDFLD_DSIPLL_M_MAX_83 92 +#define MDFLD_DSIPLL_P1_MIN_83 2 +#define MDFLD_DSIPLL_P1_MAX_83 3 +#define MDFLD_DSIPLL_M_MIN_100 97 +#define MDFLD_DSIPLL_M_MAX_100 140 +#define MDFLD_DSIPLL_P1_MIN_100 3 +#define MDFLD_DSIPLL_P1_MAX_100 9 + +static const struct mrst_limit_t mdfld_limits[] = { + { /* MDFLD_LIMT_DPLL_19 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DPLL_M_MIN_19, .max = MDFLD_DPLL_M_MAX_19}, + .p1 = {.min = MDFLD_DPLL_P1_MIN_19, .max = MDFLD_DPLL_P1_MAX_19}, + }, + { /* MDFLD_LIMT_DPLL_25 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DPLL_M_MIN_25, .max = MDFLD_DPLL_M_MAX_25}, + .p1 = {.min = MDFLD_DPLL_P1_MIN_25, .max = MDFLD_DPLL_P1_MAX_25}, + }, + { /* MDFLD_LIMT_DPLL_83 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DPLL_M_MIN_83, .max = MDFLD_DPLL_M_MAX_83}, + .p1 = {.min = MDFLD_DPLL_P1_MIN_83, .max = MDFLD_DPLL_P1_MAX_83}, + }, + { /* MDFLD_LIMT_DPLL_100 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DPLL_M_MIN_100, .max = MDFLD_DPLL_M_MAX_100}, + .p1 = {.min = MDFLD_DPLL_P1_MIN_100, .max = MDFLD_DPLL_P1_MAX_100}, + }, + { /* MDFLD_LIMT_DSIPLL_19 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DSIPLL_M_MIN_19, .max = MDFLD_DSIPLL_M_MAX_19}, + .p1 = {.min = MDFLD_DSIPLL_P1_MIN_19, .max = MDFLD_DSIPLL_P1_MAX_19}, + }, + { /* MDFLD_LIMT_DSIPLL_25 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DSIPLL_M_MIN_25, .max = MDFLD_DSIPLL_M_MAX_25}, + .p1 = {.min = MDFLD_DSIPLL_P1_MIN_25, .max = MDFLD_DSIPLL_P1_MAX_25}, + }, + { /* MDFLD_LIMT_DSIPLL_83 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DSIPLL_M_MIN_83, .max = MDFLD_DSIPLL_M_MAX_83}, + .p1 = {.min = MDFLD_DSIPLL_P1_MIN_83, .max = MDFLD_DSIPLL_P1_MAX_83}, + }, + { /* MDFLD_LIMT_DSIPLL_100 */ + .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX}, + .m = {.min = MDFLD_DSIPLL_M_MIN_100, .max = MDFLD_DSIPLL_M_MAX_100}, + .p1 = {.min = MDFLD_DSIPLL_P1_MIN_100, .max = MDFLD_DSIPLL_P1_MAX_100}, + }, +}; + +#define MDFLD_M_MIN 21 +#define MDFLD_M_MAX 180 +static const u32 mdfld_m_converts[] = { +/* M configuration table from 9-bit LFSR table */ + 224, 368, 440, 220, 366, 439, 219, 365, 182, 347, /* 21 - 30 */ + 173, 342, 171, 85, 298, 149, 74, 37, 18, 265, /* 31 - 40 */ + 388, 194, 353, 432, 216, 108, 310, 155, 333, 166, /* 41 - 50 */ + 83, 41, 276, 138, 325, 162, 337, 168, 340, 170, /* 51 - 60 */ + 341, 426, 469, 234, 373, 442, 221, 110, 311, 411, /* 61 - 70 */ + 461, 486, 243, 377, 188, 350, 175, 343, 427, 213, /* 71 - 80 */ + 106, 53, 282, 397, 354, 227, 113, 56, 284, 142, /* 81 - 90 */ + 71, 35, 273, 136, 324, 418, 465, 488, 500, 506, /* 91 - 100 */ + 253, 126, 63, 287, 399, 455, 483, 241, 376, 444, /* 101 - 110 */ + 478, 495, 503, 251, 381, 446, 479, 239, 375, 443, /* 111 - 120 */ + 477, 238, 119, 315, 157, 78, 295, 147, 329, 420, /* 121 - 130 */ + 210, 105, 308, 154, 77, 38, 275, 137, 68, 290, /* 131 - 140 */ + 145, 328, 164, 82, 297, 404, 458, 485, 498, 249, /* 141 - 150 */ + 380, 190, 351, 431, 471, 235, 117, 314, 413, 206, /* 151 - 160 */ + 103, 51, 25, 12, 262, 387, 193, 96, 48, 280, /* 161 - 170 */ + 396, 198, 99, 305, 152, 76, 294, 403, 457, 228, /* 171 - 180 */ +}; + +static const struct mrst_limit_t *mdfld_limit(struct drm_crtc *crtc) +{ + const struct mrst_limit_t *limit = NULL; + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI) + || psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI2)) { + if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) + limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_19]; + else if (ksel == KSEL_BYPASS_25) + limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_25]; + else if ((ksel == KSEL_BYPASS_83_100) && + (dev_priv->core_freq == 166)) + limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_83]; + else if ((ksel == KSEL_BYPASS_83_100) && + (dev_priv->core_freq == 100 || + dev_priv->core_freq == 200)) + limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_100]; + } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) { + if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) + limit = &mdfld_limits[MDFLD_LIMT_DPLL_19]; + else if (ksel == KSEL_BYPASS_25) + limit = &mdfld_limits[MDFLD_LIMT_DPLL_25]; + else if ((ksel == KSEL_BYPASS_83_100) && + (dev_priv->core_freq == 166)) + limit = &mdfld_limits[MDFLD_LIMT_DPLL_83]; + else if ((ksel == KSEL_BYPASS_83_100) && + (dev_priv->core_freq == 100 || + dev_priv->core_freq == 200)) + limit = &mdfld_limits[MDFLD_LIMT_DPLL_100]; + } else { + limit = NULL; + dev_dbg(dev->dev, "mdfld_limit Wrong display type.\n"); + } + + return limit; +} + +/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ +static void mdfld_clock(int refclk, struct mrst_clock_t *clock) +{ + clock->dot = (refclk * clock->m) / clock->p1; +} + +/** + * Returns a set of divisors for the desired target clock with the given refclk, + * or FALSE. Divisor values are the actual divisors for + */ +static bool +mdfldFindBestPLL(struct drm_crtc *crtc, int target, int refclk, + struct mrst_clock_t *best_clock) +{ + struct mrst_clock_t clock; + const struct mrst_limit_t *limit = mdfld_limit(crtc); + int err = target; + + memset(best_clock, 0, sizeof(*best_clock)); + + for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) { + for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max; + clock.p1++) { + int this_err; + + mdfld_clock(refclk, &clock); + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + return err != target; +} + +static int mdfld_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, + struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct drm_psb_private *dev_priv = dev->dev_private; + int pipe = psb_intel_crtc->pipe; + int fp_reg = MRST_FPA0; + int dpll_reg = MRST_DPLL_A; + int dspcntr_reg = DSPACNTR; + int pipeconf_reg = PIPEACONF; + int htot_reg = HTOTAL_A; + int hblank_reg = HBLANK_A; + int hsync_reg = HSYNC_A; + int vtot_reg = VTOTAL_A; + int vblank_reg = VBLANK_A; + int vsync_reg = VSYNC_A; + int dspsize_reg = DSPASIZE; + int dsppos_reg = DSPAPOS; + int pipesrc_reg = PIPEASRC; + u32 *pipeconf = &dev_priv->pipeconf[pipe]; + u32 *dspcntr = &dev_priv->dspcntr[pipe]; + int refclk = 0; + int clk_n = 0, clk_p2 = 0, clk_byte = 1, clk = 0, m_conv = 0, + clk_tmp = 0; + struct mrst_clock_t clock; + bool ok; + u32 dpll = 0, fp = 0; + bool is_mipi = false, is_mipi2 = false, is_hdmi = false; + struct drm_mode_config *mode_config = &dev->mode_config; + struct psb_intel_encoder *psb_intel_encoder = NULL; + uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; + struct drm_encoder *encoder; + struct drm_connector *connector; + int timeout = 0; + int ret; + + dev_dbg(dev->dev, "pipe = 0x%x\n", pipe); + +#if 0 + if (pipe == 1) { + if (!gma_power_begin(dev, true)) + return 0; + android_hdmi_crtc_mode_set(crtc, mode, adjusted_mode, + x, y, old_fb); + goto mrst_crtc_mode_set_exit; + } +#endif + + switch (pipe) { + case 0: + break; + case 1: + fp_reg = FPB0; + dpll_reg = DPLL_B; + dspcntr_reg = DSPBCNTR; + pipeconf_reg = PIPEBCONF; + htot_reg = HTOTAL_B; + hblank_reg = HBLANK_B; + hsync_reg = HSYNC_B; + vtot_reg = VTOTAL_B; + vblank_reg = VBLANK_B; + vsync_reg = VSYNC_B; + dspsize_reg = DSPBSIZE; + dsppos_reg = DSPBPOS; + pipesrc_reg = PIPEBSRC; + fp_reg = MDFLD_DPLL_DIV0; + dpll_reg = MDFLD_DPLL_B; + break; + case 2: + dpll_reg = MRST_DPLL_A; + dspcntr_reg = DSPCCNTR; + pipeconf_reg = PIPECCONF; + htot_reg = HTOTAL_C; + hblank_reg = HBLANK_C; + hsync_reg = HSYNC_C; + vtot_reg = VTOTAL_C; + vblank_reg = VBLANK_C; + vsync_reg = VSYNC_C; + dspsize_reg = DSPCSIZE; + dsppos_reg = DSPCPOS; + pipesrc_reg = PIPECSRC; + break; + default: + DRM_ERROR("Illegal Pipe Number.\n"); + return 0; + } + + ret = check_fb(crtc->fb); + if (ret) + return ret; + + dev_dbg(dev->dev, "adjusted_hdisplay = %d\n", + adjusted_mode->hdisplay); + dev_dbg(dev->dev, "adjusted_vdisplay = %d\n", + adjusted_mode->vdisplay); + dev_dbg(dev->dev, "adjusted_hsync_start = %d\n", + adjusted_mode->hsync_start); + dev_dbg(dev->dev, "adjusted_hsync_end = %d\n", + adjusted_mode->hsync_end); + dev_dbg(dev->dev, "adjusted_htotal = %d\n", + adjusted_mode->htotal); + dev_dbg(dev->dev, "adjusted_vsync_start = %d\n", + adjusted_mode->vsync_start); + dev_dbg(dev->dev, "adjusted_vsync_end = %d\n", + adjusted_mode->vsync_end); + dev_dbg(dev->dev, "adjusted_vtotal = %d\n", + adjusted_mode->vtotal); + dev_dbg(dev->dev, "adjusted_clock = %d\n", + adjusted_mode->clock); + dev_dbg(dev->dev, "hdisplay = %d\n", + mode->hdisplay); + dev_dbg(dev->dev, "vdisplay = %d\n", + mode->vdisplay); + + if (!gma_power_begin(dev, true)) + return 0; + + memcpy(&psb_intel_crtc->saved_mode, mode, + sizeof(struct drm_display_mode)); + memcpy(&psb_intel_crtc->saved_adjusted_mode, adjusted_mode, + sizeof(struct drm_display_mode)); + + list_for_each_entry(connector, &mode_config->connector_list, head) { + if (!connector) + continue; + + encoder = connector->encoder; + + if (!encoder) + continue; + + if (encoder->crtc != crtc) + continue; + + psb_intel_encoder = psb_intel_attached_encoder(connector); + + switch (psb_intel_encoder->type) { + case INTEL_OUTPUT_MIPI: + is_mipi = true; + break; + case INTEL_OUTPUT_MIPI2: + is_mipi2 = true; + break; + case INTEL_OUTPUT_HDMI: + is_hdmi = true; + break; + } + } + + /* Disable the VGA plane that we never use */ + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* Disable the panel fitter if it was on our pipe */ + if (psb_intel_panel_fitter_pipe(dev) == pipe) + REG_WRITE(PFIT_CONTROL, 0); + + /* pipesrc and dspsize control the size that is scaled from, + * which should always be the user's requested size. + */ + if (pipe == 1) { + /* FIXME: To make HDMI display with 864x480 (TPO), 480x864 + * (PYR) or 480x854 (TMD), set the sprite width/height and + * souce image size registers with the adjusted mode for + * pipe B. + */ + + /* + * The defined sprite rectangle must always be completely + * contained within the displayable area of the screen image + * (frame buffer). + */ + REG_WRITE(dspsize_reg, ((min(mode->crtc_vdisplay, adjusted_mode->crtc_vdisplay) - 1) << 16) + | (min(mode->crtc_hdisplay, adjusted_mode->crtc_hdisplay) - 1)); + /* Set the CRTC with encoder mode. */ + REG_WRITE(pipesrc_reg, ((mode->crtc_hdisplay - 1) << 16) + | (mode->crtc_vdisplay - 1)); + } else { + REG_WRITE(dspsize_reg, + ((mode->crtc_vdisplay - 1) << 16) | + (mode->crtc_hdisplay - 1)); + REG_WRITE(pipesrc_reg, + ((mode->crtc_hdisplay - 1) << 16) | + (mode->crtc_vdisplay - 1)); + } + + REG_WRITE(dsppos_reg, 0); + + if (psb_intel_encoder) + drm_connector_property_get_value(connector, + dev->mode_config.scaling_mode_property, &scalingType); + + if (scalingType == DRM_MODE_SCALE_NO_SCALE) { + /* Medfield doesn't have register support for centering so we + * need to mess with the h/vblank and h/vsync start and ends + * to get centering + */ + int offsetX = 0, offsetY = 0; + + offsetX = (adjusted_mode->crtc_hdisplay - + mode->crtc_hdisplay) / 2; + offsetY = (adjusted_mode->crtc_vdisplay - + mode->crtc_vdisplay) / 2; + + REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - + offsetX - 1) | + ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16)); + REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - + offsetX - 1) | + ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16)); + REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - + offsetY - 1) | + ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16)); + REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - + offsetY - 1) | + ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16)); + } else { + REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16)); + REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16)); + REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16)); + REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + ((adjusted_mode->crtc_vsync_end - 1) << 16)); + } + + /* Flush the plane changes */ + { + struct drm_crtc_helper_funcs *crtc_funcs = + crtc->helper_private; + crtc_funcs->mode_set_base(crtc, x, y, old_fb); + } + + /* setup pipeconf */ + *pipeconf = PIPEACONF_ENABLE; /* FIXME_JLIU7 REG_READ(pipeconf_reg); */ + + /* Set up the display plane register */ + *dspcntr = REG_READ(dspcntr_reg); + *dspcntr |= pipe << DISPPLANE_SEL_PIPE_POS; + *dspcntr |= DISPLAY_PLANE_ENABLE; + + if (is_mipi2) + goto mrst_crtc_mode_set_exit; + clk = adjusted_mode->clock; + + if (is_hdmi) { + if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) { + refclk = 19200; + + if (is_mipi || is_mipi2) + clk_n = 1, clk_p2 = 8; + else if (is_hdmi) + clk_n = 1, clk_p2 = 10; + } else if (ksel == KSEL_BYPASS_25) { + refclk = 25000; + + if (is_mipi || is_mipi2) + clk_n = 1, clk_p2 = 8; + else if (is_hdmi) + clk_n = 1, clk_p2 = 10; + } else if ((ksel == KSEL_BYPASS_83_100) && + dev_priv->core_freq == 166) { + refclk = 83000; + + if (is_mipi || is_mipi2) + clk_n = 4, clk_p2 = 8; + else if (is_hdmi) + clk_n = 4, clk_p2 = 10; + } else if ((ksel == KSEL_BYPASS_83_100) && + (dev_priv->core_freq == 100 || + dev_priv->core_freq == 200)) { + refclk = 100000; + if (is_mipi || is_mipi2) + clk_n = 4, clk_p2 = 8; + else if (is_hdmi) + clk_n = 4, clk_p2 = 10; + } + + if (is_mipi) + clk_byte = dev_priv->bpp / 8; + else if (is_mipi2) + clk_byte = dev_priv->bpp2 / 8; + + clk_tmp = clk * clk_n * clk_p2 * clk_byte; + + dev_dbg(dev->dev, "clk = %d, clk_n = %d, clk_p2 = %d.\n", + clk, clk_n, clk_p2); + dev_dbg(dev->dev, "adjusted_mode->clock = %d, clk_tmp = %d.\n", + adjusted_mode->clock, clk_tmp); + + ok = mdfldFindBestPLL(crtc, clk_tmp, refclk, &clock); + + if (!ok) { + DRM_ERROR + ("mdfldFindBestPLL fail in mdfld_crtc_mode_set.\n"); + } else { + m_conv = mdfld_m_converts[(clock.m - MDFLD_M_MIN)]; + + dev_dbg(dev->dev, "dot clock = %d," + "m = %d, p1 = %d, m_conv = %d.\n", + clock.dot, clock.m, + clock.p1, m_conv); + } + + dpll = REG_READ(dpll_reg); + + if (dpll & DPLL_VCO_ENABLE) { + dpll &= ~DPLL_VCO_ENABLE; + REG_WRITE(dpll_reg, dpll); + REG_READ(dpll_reg); + + /* FIXME jliu7 check the DPLL lock bit PIPEACONF[29] */ + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + /* reset M1, N1 & P1 */ + REG_WRITE(fp_reg, 0); + dpll &= ~MDFLD_P1_MASK; + REG_WRITE(dpll_reg, dpll); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + } + + /* 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; + REG_WRITE(dpll_reg, dpll); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + } + dpll = 0; + +#if 0 /* FIXME revisit later */ + if (ksel == KSEL_CRYSTAL_19 || ksel == KSEL_BYPASS_19 || + ksel == KSEL_BYPASS_25) + dpll &= ~MDFLD_INPUT_REF_SEL; + else if (ksel == KSEL_BYPASS_83_100) + dpll |= MDFLD_INPUT_REF_SEL; +#endif /* FIXME revisit later */ + + if (is_hdmi) + dpll |= MDFLD_VCO_SEL; + + fp = (clk_n / 2) << 16; + fp |= m_conv; + + /* compute bitmask from p1 value */ + dpll |= (1 << (clock.p1 - 2)) << 17; + +#if 0 /* 1080p30 & 720p */ + dpll = 0x00050000; + fp = 0x000001be; +#endif +#if 0 /* 480p */ + dpll = 0x02010000; + fp = 0x000000d2; +#endif + } else { +#if 0 /*DBI_TPO_480x864*/ + dpll = 0x00020000; + fp = 0x00000156; +#endif /* DBI_TPO_480x864 */ /* get from spec. */ + + dpll = 0x00800000; + fp = 0x000000c1; + } + + REG_WRITE(fp_reg, fp); + REG_WRITE(dpll_reg, dpll); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + dpll |= DPLL_VCO_ENABLE; + REG_WRITE(dpll_reg, dpll); + REG_READ(dpll_reg); + + /* wait for DSI PLL to lock */ + while (timeout < 20000 && + !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) { + udelay(150); + timeout++; + } + + if (is_mipi) + goto mrst_crtc_mode_set_exit; + + dev_dbg(dev->dev, "is_mipi = 0x%x\n", is_mipi); + + REG_WRITE(pipeconf_reg, *pipeconf); + REG_READ(pipeconf_reg); + + /* Wait for for the pipe enable to take effect. */ + REG_WRITE(dspcntr_reg, *dspcntr); + psb_intel_wait_for_vblank(dev); + +mrst_crtc_mode_set_exit: + + gma_power_end(dev); + + return 0; +} + +const struct drm_crtc_helper_funcs mdfld_helper_funcs = { + .dpms = mdfld_crtc_dpms, + .mode_fixup = psb_intel_crtc_mode_fixup, + .mode_set = mdfld_crtc_mode_set, + .mode_set_base = mdfld__intel_pipe_set_base, + .prepare = psb_intel_crtc_prepare, + .commit = psb_intel_crtc_commit, +}; + diff --git a/drivers/gpu/drm/gma500/mdfld_output.c b/drivers/gpu/drm/gma500/mdfld_output.c new file mode 100644 index 000000000000..c95966bb0c96 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_output.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicensen + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Thomas Eaton <thomas.g.eaton@intel.com> + * Scott Rowe <scott.m.rowe@intel.com> +*/ + +#include "mdfld_output.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_dsi_output.h" + +#include "tc35876x-dsi-lvds.h" + +int mdfld_get_panel_type(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + return dev_priv->mdfld_panel_id; +} + +static void mdfld_init_panel(struct drm_device *dev, int mipi_pipe, + int p_type) +{ + switch (p_type) { + case TPO_VID: + mdfld_dsi_output_init(dev, mipi_pipe, &mdfld_tpo_vid_funcs); + break; + case TC35876X: + tc35876x_init(dev); + mdfld_dsi_output_init(dev, mipi_pipe, &mdfld_tc35876x_funcs); + break; + case TMD_VID: + mdfld_dsi_output_init(dev, mipi_pipe, &mdfld_tmd_vid_funcs); + break; + case HDMI: +/* if (dev_priv->mdfld_hdmi_present) + mdfld_hdmi_init(dev, &dev_priv->mode_dev); */ + break; + } +} + + +int mdfld_output_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + /* FIXME: hardcoded for now */ + dev_priv->mdfld_panel_id = TC35876X; + /* MIPI panel 1 */ + mdfld_init_panel(dev, 0, dev_priv->mdfld_panel_id); + /* HDMI panel */ + mdfld_init_panel(dev, 1, HDMI); + return 0; +} + diff --git a/drivers/gpu/drm/gma500/mdfld_output.h b/drivers/gpu/drm/gma500/mdfld_output.h new file mode 100644 index 000000000000..ab2b27c0f037 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_output.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicensen + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Thomas Eaton <thomas.g.eaton@intel.com> + * Scott Rowe <scott.m.rowe@intel.com> +*/ + +#ifndef MDFLD_OUTPUT_H +#define MDFLD_OUTPUT_H + +#include "psb_drv.h" + +#define TPO_PANEL_WIDTH 84 +#define TPO_PANEL_HEIGHT 46 +#define TMD_PANEL_WIDTH 39 +#define TMD_PANEL_HEIGHT 71 + +struct mdfld_dsi_config; + +enum panel_type { + TPO_VID, + TMD_VID, + HDMI, + TC35876X, +}; + +struct panel_info { + u32 width_mm; + u32 height_mm; + /* Other info */ +}; + +struct panel_funcs { + const struct drm_encoder_funcs *encoder_funcs; + const struct drm_encoder_helper_funcs *encoder_helper_funcs; + struct drm_display_mode * (*get_config_mode)(struct drm_device *); + int (*get_panel_info)(struct drm_device *, int, struct panel_info *); + int (*reset)(int pipe); + void (*drv_ic_init)(struct mdfld_dsi_config *dsi_config, int pipe); +}; + +int mdfld_output_init(struct drm_device *dev); + +struct backlight_device *mdfld_get_backlight_device(void); +int mdfld_set_brightness(struct backlight_device *bd); + +int mdfld_get_panel_type(struct drm_device *dev, int pipe); + +extern const struct drm_crtc_helper_funcs mdfld_helper_funcs; + +extern const struct panel_funcs mdfld_tmd_vid_funcs; +extern const struct panel_funcs mdfld_tpo_vid_funcs; + +extern void mdfld_disable_crtc(struct drm_device *dev, int pipe); +extern void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe); +extern void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe); +#endif diff --git a/drivers/gpu/drm/gma500/mdfld_tmd_vid.c b/drivers/gpu/drm/gma500/mdfld_tmd_vid.c new file mode 100644 index 000000000000..dc0c6c3d3d29 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_tmd_vid.c @@ -0,0 +1,201 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Jim Liu <jim.liu@intel.com> + * Jackie Li<yaodong.li@intel.com> + * Gideon Eaton <eaton. + * Scott Rowe <scott.m.rowe@intel.com> + */ + +#include "mdfld_dsi_dpi.h" +#include "mdfld_dsi_pkg_sender.h" + +static struct drm_display_mode *tmd_vid_get_config_mode(struct drm_device *dev) +{ + struct drm_display_mode *mode; + struct drm_psb_private *dev_priv = dev->dev_private; + struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD; + bool use_gct = false; /*Disable GCT for now*/ + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) + return NULL; + + if (use_gct) { + mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; + mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; + mode->hsync_start = mode->hdisplay + \ + ((ti->hsync_offset_hi << 8) | \ + ti->hsync_offset_lo); + mode->hsync_end = mode->hsync_start + \ + ((ti->hsync_pulse_width_hi << 8) | \ + ti->hsync_pulse_width_lo); + mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \ + ti->hblank_lo); + mode->vsync_start = \ + mode->vdisplay + ((ti->vsync_offset_hi << 8) | \ + ti->vsync_offset_lo); + mode->vsync_end = \ + mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \ + ti->vsync_pulse_width_lo); + mode->vtotal = mode->vdisplay + \ + ((ti->vblank_hi << 8) | ti->vblank_lo); + mode->clock = ti->pixel_clock * 10; + + dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay); + dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay); + dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start); + dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end); + dev_dbg(dev->dev, "htotal is %d\n", mode->htotal); + dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start); + dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end); + dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal); + dev_dbg(dev->dev, "clock is %d\n", mode->clock); + } else { + mode->hdisplay = 480; + mode->vdisplay = 854; + mode->hsync_start = 487; + mode->hsync_end = 490; + mode->htotal = 499; + mode->vsync_start = 861; + mode->vsync_end = 865; + mode->vtotal = 873; + mode->clock = 33264; + } + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + + return mode; +} + +static int tmd_vid_get_panel_info(struct drm_device *dev, + int pipe, + struct panel_info *pi) +{ + if (!dev || !pi) + return -EINVAL; + + pi->width_mm = TMD_PANEL_WIDTH; + pi->height_mm = TMD_PANEL_HEIGHT; + + return 0; +} + +/* ************************************************************************* *\ + * FUNCTION: mdfld_init_TMD_MIPI + * + * DESCRIPTION: This function is called only by mrst_dsi_mode_set and + * restore_display_registers. since this function does not + * acquire the mutex, it is important that the calling function + * does! +\* ************************************************************************* */ + +/* FIXME: make the below data u8 instead of u32; note byte order! */ +static u32 tmd_cmd_mcap_off[] = {0x000000b2}; +static u32 tmd_cmd_enable_lane_switch[] = {0x000101ef}; +static u32 tmd_cmd_set_lane_num[] = {0x006360ef}; +static u32 tmd_cmd_pushing_clock0[] = {0x00cc2fef}; +static u32 tmd_cmd_pushing_clock1[] = {0x00dd6eef}; +static u32 tmd_cmd_set_mode[] = {0x000000b3}; +static u32 tmd_cmd_set_sync_pulse_mode[] = {0x000961ef}; +static u32 tmd_cmd_set_column[] = {0x0100002a, 0x000000df}; +static u32 tmd_cmd_set_page[] = {0x0300002b, 0x00000055}; +static u32 tmd_cmd_set_video_mode[] = {0x00000153}; +/*no auto_bl,need add in furture*/ +static u32 tmd_cmd_enable_backlight[] = {0x00005ab4}; +static u32 tmd_cmd_set_backlight_dimming[] = {0x00000ebd}; + +static void mdfld_dsi_tmd_drv_ic_init(struct mdfld_dsi_config *dsi_config, + int pipe) +{ + struct mdfld_dsi_pkg_sender *sender + = mdfld_dsi_get_pkg_sender(dsi_config); + + DRM_INFO("Enter mdfld init TMD MIPI display.\n"); + + if (!sender) { + DRM_ERROR("Cannot get sender\n"); + return; + } + + if (dsi_config->dvr_ic_inited) + return; + + msleep(3); + + /* FIXME: make the below data u8 instead of u32; note byte order! */ + + mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_mcap_off, + sizeof(tmd_cmd_mcap_off), false); + mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_enable_lane_switch, + sizeof(tmd_cmd_enable_lane_switch), false); + mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_lane_num, + sizeof(tmd_cmd_set_lane_num), false); + mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_pushing_clock0, + sizeof(tmd_cmd_pushing_clock0), false); + mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_pushing_clock1, + sizeof(tmd_cmd_pushing_clock1), false); + mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_mode, + sizeof(tmd_cmd_set_mode), false); + mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_sync_pulse_mode, + sizeof(tmd_cmd_set_sync_pulse_mode), false); + mdfld_dsi_send_mcs_long(sender, (u8 *) tmd_cmd_set_column, + sizeof(tmd_cmd_set_column), false); + mdfld_dsi_send_mcs_long(sender, (u8 *) tmd_cmd_set_page, + sizeof(tmd_cmd_set_page), false); + mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_video_mode, + sizeof(tmd_cmd_set_video_mode), false); + mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_enable_backlight, + sizeof(tmd_cmd_enable_backlight), false); + mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_backlight_dimming, + sizeof(tmd_cmd_set_backlight_dimming), false); + + dsi_config->dvr_ic_inited = 1; +} + +/*TPO DPI encoder helper funcs*/ +static const struct drm_encoder_helper_funcs + mdfld_tpo_dpi_encoder_helper_funcs = { + .dpms = mdfld_dsi_dpi_dpms, + .mode_fixup = mdfld_dsi_dpi_mode_fixup, + .prepare = mdfld_dsi_dpi_prepare, + .mode_set = mdfld_dsi_dpi_mode_set, + .commit = mdfld_dsi_dpi_commit, +}; + +/*TPO DPI encoder funcs*/ +static const struct drm_encoder_funcs mdfld_tpo_dpi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +const struct panel_funcs mdfld_tmd_vid_funcs = { + .encoder_funcs = &mdfld_tpo_dpi_encoder_funcs, + .encoder_helper_funcs = &mdfld_tpo_dpi_encoder_helper_funcs, + .get_config_mode = &tmd_vid_get_config_mode, + .get_panel_info = tmd_vid_get_panel_info, + .reset = mdfld_dsi_panel_reset, + .drv_ic_init = mdfld_dsi_tmd_drv_ic_init, +}; diff --git a/drivers/gpu/drm/gma500/mdfld_tpo_vid.c b/drivers/gpu/drm/gma500/mdfld_tpo_vid.c new file mode 100644 index 000000000000..d8d4170725b2 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_tpo_vid.c @@ -0,0 +1,124 @@ +/* + * Copyright © 2010 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * jim liu <jim.liu@intel.com> + * Jackie Li<yaodong.li@intel.com> + */ + +#include "mdfld_dsi_dpi.h" + +static struct drm_display_mode *tpo_vid_get_config_mode(struct drm_device *dev) +{ + struct drm_display_mode *mode; + struct drm_psb_private *dev_priv = dev->dev_private; + struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD; + bool use_gct = false; + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) + return NULL; + + if (use_gct) { + mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo; + mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo; + mode->hsync_start = mode->hdisplay + + ((ti->hsync_offset_hi << 8) | + ti->hsync_offset_lo); + mode->hsync_end = mode->hsync_start + + ((ti->hsync_pulse_width_hi << 8) | + ti->hsync_pulse_width_lo); + mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | + ti->hblank_lo); + mode->vsync_start = + mode->vdisplay + ((ti->vsync_offset_hi << 8) | + ti->vsync_offset_lo); + mode->vsync_end = + mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | + ti->vsync_pulse_width_lo); + mode->vtotal = mode->vdisplay + + ((ti->vblank_hi << 8) | ti->vblank_lo); + mode->clock = ti->pixel_clock * 10; + + dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay); + dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay); + dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start); + dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end); + dev_dbg(dev->dev, "htotal is %d\n", mode->htotal); + dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start); + dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end); + dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal); + dev_dbg(dev->dev, "clock is %d\n", mode->clock); + } else { + mode->hdisplay = 864; + mode->vdisplay = 480; + mode->hsync_start = 873; + mode->hsync_end = 876; + mode->htotal = 887; + mode->vsync_start = 487; + mode->vsync_end = 490; + mode->vtotal = 499; + mode->clock = 33264; + } + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + + return mode; +} + +static int tpo_vid_get_panel_info(struct drm_device *dev, + int pipe, + struct panel_info *pi) +{ + if (!dev || !pi) + return -EINVAL; + + pi->width_mm = TPO_PANEL_WIDTH; + pi->height_mm = TPO_PANEL_HEIGHT; + + return 0; +} + +/*TPO DPI encoder helper funcs*/ +static const struct drm_encoder_helper_funcs + mdfld_tpo_dpi_encoder_helper_funcs = { + .dpms = mdfld_dsi_dpi_dpms, + .mode_fixup = mdfld_dsi_dpi_mode_fixup, + .prepare = mdfld_dsi_dpi_prepare, + .mode_set = mdfld_dsi_dpi_mode_set, + .commit = mdfld_dsi_dpi_commit, +}; + +/*TPO DPI encoder funcs*/ +static const struct drm_encoder_funcs mdfld_tpo_dpi_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +const struct panel_funcs mdfld_tpo_vid_funcs = { + .encoder_funcs = &mdfld_tpo_dpi_encoder_funcs, + .encoder_helper_funcs = &mdfld_tpo_dpi_encoder_helper_funcs, + .get_config_mode = &tpo_vid_get_config_mode, + .get_panel_info = tpo_vid_get_panel_info, +}; diff --git a/drivers/gpu/drm/gma500/mmu.c b/drivers/gpu/drm/gma500/mmu.c index c904d73b1de3..49bac41beefb 100644 --- a/drivers/gpu/drm/gma500/mmu.c +++ b/drivers/gpu/drm/gma500/mmu.c @@ -125,14 +125,14 @@ static void psb_page_clflush(struct psb_mmu_driver *driver, struct page* page) int i; uint8_t *clf; - clf = kmap_atomic(page, KM_USER0); + clf = kmap_atomic(page); mb(); for (i = 0; i < clflush_count; ++i) { psb_clflush(clf); clf += clflush_add; } mb(); - kunmap_atomic(clf, KM_USER0); + kunmap_atomic(clf); } static void psb_pages_clflush(struct psb_mmu_driver *driver, @@ -270,7 +270,7 @@ out_err1: return NULL; } -void psb_mmu_free_pt(struct psb_mmu_pt *pt) +static void psb_mmu_free_pt(struct psb_mmu_pt *pt) { __free_page(pt->p); kfree(pt); @@ -325,7 +325,7 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd) spin_lock(lock); - v = kmap_atomic(pt->p, KM_USER0); + v = kmap_atomic(pt->p); clf = (uint8_t *) v; ptes = (uint32_t *) v; for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i) @@ -341,7 +341,7 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd) mb(); } - kunmap_atomic(v, KM_USER0); + kunmap_atomic(v); spin_unlock(lock); pt->count = 0; @@ -351,7 +351,7 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd) return pt; } -struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd, +static struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd, unsigned long addr) { uint32_t index = psb_mmu_pd_index(addr); @@ -376,18 +376,18 @@ struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd, continue; } - v = kmap_atomic(pd->p, KM_USER0); + v = kmap_atomic(pd->p); pd->tables[index] = pt; v[index] = (page_to_pfn(pt->p) << 12) | pd->pd_mask; pt->index = index; - kunmap_atomic((void *) v, KM_USER0); + kunmap_atomic((void *) v); if (pd->hw_context != -1) { psb_mmu_clflush(pd->driver, (void *) &v[index]); atomic_set(&pd->driver->needs_tlbflush, 1); } } - pt->v = kmap_atomic(pt->p, KM_USER0); + pt->v = kmap_atomic(pt->p); return pt; } @@ -404,7 +404,7 @@ static struct psb_mmu_pt *psb_mmu_pt_map_lock(struct psb_mmu_pd *pd, spin_unlock(lock); return NULL; } - pt->v = kmap_atomic(pt->p, KM_USER0); + pt->v = kmap_atomic(pt->p); return pt; } @@ -413,9 +413,9 @@ static void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt) struct psb_mmu_pd *pd = pt->pd; uint32_t *v; - kunmap_atomic(pt->v, KM_USER0); + kunmap_atomic(pt->v); if (pt->count == 0) { - v = kmap_atomic(pd->p, KM_USER0); + v = kmap_atomic(pd->p); v[pt->index] = pd->invalid_pde; pd->tables[pt->index] = NULL; @@ -424,7 +424,7 @@ static void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt) (void *) &v[pt->index]); atomic_set(&pd->driver->needs_tlbflush, 1); } - kunmap_atomic(pt->v, KM_USER0); + kunmap_atomic(pt->v); spin_unlock(&pd->driver->lock); psb_mmu_free_pt(pt); return; @@ -457,7 +457,7 @@ void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd, down_read(&driver->sem); spin_lock(&driver->lock); - v = kmap_atomic(pd->p, KM_USER0); + v = kmap_atomic(pd->p); v += start; while (gtt_pages--) { @@ -467,7 +467,7 @@ void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd, /*ttm_tt_cache_flush(&pd->p, num_pages);*/ psb_pages_clflush(pd->driver, &pd->p, num_pages); - kunmap_atomic(v, KM_USER0); + kunmap_atomic(v); spin_unlock(&driver->lock); if (pd->hw_context != -1) @@ -488,15 +488,6 @@ struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver *driver) return pd; } -/* Returns the physical address of the PD shared by sgx/msvdx */ -uint32_t psb_get_default_pd_addr(struct psb_mmu_driver *driver) -{ - struct psb_mmu_pd *pd; - - pd = psb_mmu_get_default_pd(driver); - return page_to_pfn(pd->p) << PAGE_SHIFT; -} - void psb_mmu_driver_takedown(struct psb_mmu_driver *driver) { psb_mmu_free_pagedir(driver->default_pd); @@ -830,9 +821,9 @@ int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual, uint32_t *v; spin_lock(lock); - v = kmap_atomic(pd->p, KM_USER0); + v = kmap_atomic(pd->p); tmp = v[psb_mmu_pd_index(virtual)]; - kunmap_atomic(v, KM_USER0); + kunmap_atomic(v); spin_unlock(lock); if (tmp != pd->invalid_pde || !(tmp & PSB_PTE_VALID) || diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c index 9d12a3ee1600..a39b0d0d680f 100644 --- a/drivers/gpu/drm/gma500/oaktrail_crtc.c +++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c @@ -115,7 +115,7 @@ static void oaktrail_clock(int refclk, struct oaktrail_clock_t *clock) clock->dot = (refclk * clock->m) / (14 * clock->p1); } -void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock) +static void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock) { pr_debug("%s: dotclock = %d, m = %d, p1 = %d.\n", prefix, clock->dot, clock->m, clock->p1); @@ -169,7 +169,6 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode) int dspbase_reg = (pipe == 0) ? MRST_DSPABASE : DSPBBASE; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; u32 temp; - bool enabled; if (!gma_power_begin(dev, true)) return; @@ -253,8 +252,6 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode) break; } - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; - /*Set FIFO Watermarks*/ REG_WRITE(DSPARB, 0x3FFF); REG_WRITE(DSPFW1, 0x3F88080A); @@ -310,7 +307,7 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, struct oaktrail_clock_t clock; u32 dpll = 0, fp = 0, dspcntr, pipeconf; bool ok, is_sdvo = false; - bool is_crt = false, is_lvds = false, is_tv = false; + bool is_lvds = false; bool is_mipi = false; struct drm_mode_config *mode_config = &dev->mode_config; struct psb_intel_encoder *psb_intel_encoder = NULL; @@ -340,12 +337,6 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_SDVO: is_sdvo = true; break; - case INTEL_OUTPUT_TVOUT: - is_tv = true; - break; - case INTEL_OUTPUT_ANALOG: - is_crt = true; - break; case INTEL_OUTPUT_MIPI: is_mipi = true; break; @@ -428,9 +419,6 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, else dspcntr |= DISPPLANE_SEL_PIPE_B; - dev_priv->dspcntr = dspcntr |= DISPLAY_PLANE_ENABLE; - dev_priv->pipeconf = pipeconf |= PIPEACONF_ENABLE; - if (is_mipi) goto oaktrail_crtc_mode_set_exit; @@ -517,7 +505,7 @@ static bool oaktrail_crtc_mode_fixup(struct drm_crtc *crtc, return true; } -int oaktrail_pipe_set_base(struct drm_crtc *crtc, +static int oaktrail_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c index 63aea2f010d9..41d1924ea31e 100644 --- a/drivers/gpu/drm/gma500/oaktrail_device.c +++ b/drivers/gpu/drm/gma500/oaktrail_device.c @@ -141,7 +141,7 @@ static const struct backlight_ops oaktrail_ops = { .update_status = oaktrail_set_brightness, }; -int oaktrail_backlight_init(struct drm_device *dev) +static int oaktrail_backlight_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; int ret; @@ -176,10 +176,6 @@ int oaktrail_backlight_init(struct drm_device *dev) * for power management */ -static void oaktrail_init_pm(struct drm_device *dev) -{ -} - /** * oaktrail_save_display_registers - save registers lost on suspend * @dev: our DRM device @@ -190,81 +186,82 @@ static void oaktrail_init_pm(struct drm_device *dev) static int oaktrail_save_display_registers(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_save_area *regs = &dev_priv->regs; int i; u32 pp_stat; /* Display arbitration control + watermarks */ - dev_priv->saveDSPARB = PSB_RVDC32(DSPARB); - dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1); - dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2); - dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3); - dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4); - dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5); - dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6); - dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); + regs->psb.saveDSPARB = PSB_RVDC32(DSPARB); + regs->psb.saveDSPFW1 = PSB_RVDC32(DSPFW1); + regs->psb.saveDSPFW2 = PSB_RVDC32(DSPFW2); + regs->psb.saveDSPFW3 = PSB_RVDC32(DSPFW3); + regs->psb.saveDSPFW4 = PSB_RVDC32(DSPFW4); + regs->psb.saveDSPFW5 = PSB_RVDC32(DSPFW5); + regs->psb.saveDSPFW6 = PSB_RVDC32(DSPFW6); + regs->psb.saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); /* Pipe & plane A info */ - dev_priv->savePIPEACONF = PSB_RVDC32(PIPEACONF); - dev_priv->savePIPEASRC = PSB_RVDC32(PIPEASRC); - dev_priv->saveFPA0 = PSB_RVDC32(MRST_FPA0); - dev_priv->saveFPA1 = PSB_RVDC32(MRST_FPA1); - dev_priv->saveDPLL_A = PSB_RVDC32(MRST_DPLL_A); - dev_priv->saveHTOTAL_A = PSB_RVDC32(HTOTAL_A); - dev_priv->saveHBLANK_A = PSB_RVDC32(HBLANK_A); - dev_priv->saveHSYNC_A = PSB_RVDC32(HSYNC_A); - dev_priv->saveVTOTAL_A = PSB_RVDC32(VTOTAL_A); - dev_priv->saveVBLANK_A = PSB_RVDC32(VBLANK_A); - dev_priv->saveVSYNC_A = PSB_RVDC32(VSYNC_A); - dev_priv->saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); - dev_priv->saveDSPACNTR = PSB_RVDC32(DSPACNTR); - dev_priv->saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE); - dev_priv->saveDSPAADDR = PSB_RVDC32(DSPABASE); - dev_priv->saveDSPASURF = PSB_RVDC32(DSPASURF); - dev_priv->saveDSPALINOFF = PSB_RVDC32(DSPALINOFF); - dev_priv->saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF); + regs->psb.savePIPEACONF = PSB_RVDC32(PIPEACONF); + regs->psb.savePIPEASRC = PSB_RVDC32(PIPEASRC); + regs->psb.saveFPA0 = PSB_RVDC32(MRST_FPA0); + regs->psb.saveFPA1 = PSB_RVDC32(MRST_FPA1); + regs->psb.saveDPLL_A = PSB_RVDC32(MRST_DPLL_A); + regs->psb.saveHTOTAL_A = PSB_RVDC32(HTOTAL_A); + regs->psb.saveHBLANK_A = PSB_RVDC32(HBLANK_A); + regs->psb.saveHSYNC_A = PSB_RVDC32(HSYNC_A); + regs->psb.saveVTOTAL_A = PSB_RVDC32(VTOTAL_A); + regs->psb.saveVBLANK_A = PSB_RVDC32(VBLANK_A); + regs->psb.saveVSYNC_A = PSB_RVDC32(VSYNC_A); + regs->psb.saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); + regs->psb.saveDSPACNTR = PSB_RVDC32(DSPACNTR); + regs->psb.saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE); + regs->psb.saveDSPAADDR = PSB_RVDC32(DSPABASE); + regs->psb.saveDSPASURF = PSB_RVDC32(DSPASURF); + regs->psb.saveDSPALINOFF = PSB_RVDC32(DSPALINOFF); + regs->psb.saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF); /* Save cursor regs */ - dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); - dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); - dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); + regs->psb.saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR); + regs->psb.saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE); + regs->psb.saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS); /* Save palette (gamma) */ for (i = 0; i < 256; i++) - dev_priv->save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2)); + regs->psb.save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2)); if (dev_priv->hdmi_priv) oaktrail_hdmi_save(dev); /* Save performance state */ - dev_priv->savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE); + regs->psb.savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE); /* LVDS state */ - dev_priv->savePP_CONTROL = PSB_RVDC32(PP_CONTROL); - dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); - dev_priv->savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS); - dev_priv->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL); - dev_priv->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2); - dev_priv->saveLVDS = PSB_RVDC32(LVDS); - dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); - dev_priv->savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON); - dev_priv->savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF); - dev_priv->savePP_DIVISOR = PSB_RVDC32(PP_CYCLE); + regs->psb.savePP_CONTROL = PSB_RVDC32(PP_CONTROL); + regs->psb.savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); + regs->psb.savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS); + regs->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL); + regs->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2); + regs->psb.saveLVDS = PSB_RVDC32(LVDS); + regs->psb.savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); + regs->psb.savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON); + regs->psb.savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF); + regs->psb.savePP_DIVISOR = PSB_RVDC32(PP_CYCLE); /* HW overlay */ - dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD); - dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); - dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); - dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); - dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); - dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); - dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); + regs->psb.saveOV_OVADD = PSB_RVDC32(OV_OVADD); + regs->psb.saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0); + regs->psb.saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1); + regs->psb.saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2); + regs->psb.saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3); + regs->psb.saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4); + regs->psb.saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5); /* DPST registers */ - dev_priv->saveHISTOGRAM_INT_CONTROL_REG = + regs->psb.saveHISTOGRAM_INT_CONTROL_REG = PSB_RVDC32(HISTOGRAM_INT_CONTROL); - dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG = + regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG = PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL); - dev_priv->savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC); + regs->psb.savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC); if (dev_priv->iLVDS_enable) { /* Shut down the panel */ @@ -302,79 +299,80 @@ static int oaktrail_save_display_registers(struct drm_device *dev) static int oaktrail_restore_display_registers(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_save_area *regs = &dev_priv->regs; u32 pp_stat; int i; /* Display arbitration + watermarks */ - PSB_WVDC32(dev_priv->saveDSPARB, DSPARB); - PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1); - PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2); - PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3); - PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4); - PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5); - PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6); - PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT); + PSB_WVDC32(regs->psb.saveDSPARB, DSPARB); + PSB_WVDC32(regs->psb.saveDSPFW1, DSPFW1); + PSB_WVDC32(regs->psb.saveDSPFW2, DSPFW2); + PSB_WVDC32(regs->psb.saveDSPFW3, DSPFW3); + PSB_WVDC32(regs->psb.saveDSPFW4, DSPFW4); + PSB_WVDC32(regs->psb.saveDSPFW5, DSPFW5); + PSB_WVDC32(regs->psb.saveDSPFW6, DSPFW6); + PSB_WVDC32(regs->psb.saveCHICKENBIT, DSPCHICKENBIT); /* Make sure VGA plane is off. it initializes to on after reset!*/ PSB_WVDC32(0x80000000, VGACNTRL); /* set the plls */ - PSB_WVDC32(dev_priv->saveFPA0, MRST_FPA0); - PSB_WVDC32(dev_priv->saveFPA1, MRST_FPA1); + PSB_WVDC32(regs->psb.saveFPA0, MRST_FPA0); + PSB_WVDC32(regs->psb.saveFPA1, MRST_FPA1); /* Actually enable it */ - PSB_WVDC32(dev_priv->saveDPLL_A, MRST_DPLL_A); + PSB_WVDC32(regs->psb.saveDPLL_A, MRST_DPLL_A); DRM_UDELAY(150); /* Restore mode */ - PSB_WVDC32(dev_priv->saveHTOTAL_A, HTOTAL_A); - PSB_WVDC32(dev_priv->saveHBLANK_A, HBLANK_A); - PSB_WVDC32(dev_priv->saveHSYNC_A, HSYNC_A); - PSB_WVDC32(dev_priv->saveVTOTAL_A, VTOTAL_A); - PSB_WVDC32(dev_priv->saveVBLANK_A, VBLANK_A); - PSB_WVDC32(dev_priv->saveVSYNC_A, VSYNC_A); - PSB_WVDC32(dev_priv->savePIPEASRC, PIPEASRC); - PSB_WVDC32(dev_priv->saveBCLRPAT_A, BCLRPAT_A); + PSB_WVDC32(regs->psb.saveHTOTAL_A, HTOTAL_A); + PSB_WVDC32(regs->psb.saveHBLANK_A, HBLANK_A); + PSB_WVDC32(regs->psb.saveHSYNC_A, HSYNC_A); + PSB_WVDC32(regs->psb.saveVTOTAL_A, VTOTAL_A); + PSB_WVDC32(regs->psb.saveVBLANK_A, VBLANK_A); + PSB_WVDC32(regs->psb.saveVSYNC_A, VSYNC_A); + PSB_WVDC32(regs->psb.savePIPEASRC, PIPEASRC); + PSB_WVDC32(regs->psb.saveBCLRPAT_A, BCLRPAT_A); /* Restore performance mode*/ - PSB_WVDC32(dev_priv->savePERF_MODE, MRST_PERF_MODE); + PSB_WVDC32(regs->psb.savePERF_MODE, MRST_PERF_MODE); /* Enable the pipe*/ if (dev_priv->iLVDS_enable) - PSB_WVDC32(dev_priv->savePIPEACONF, PIPEACONF); + PSB_WVDC32(regs->psb.savePIPEACONF, PIPEACONF); /* Set up the plane*/ - PSB_WVDC32(dev_priv->saveDSPALINOFF, DSPALINOFF); - PSB_WVDC32(dev_priv->saveDSPASTRIDE, DSPASTRIDE); - PSB_WVDC32(dev_priv->saveDSPATILEOFF, DSPATILEOFF); + PSB_WVDC32(regs->psb.saveDSPALINOFF, DSPALINOFF); + PSB_WVDC32(regs->psb.saveDSPASTRIDE, DSPASTRIDE); + PSB_WVDC32(regs->psb.saveDSPATILEOFF, DSPATILEOFF); /* Enable the plane */ - PSB_WVDC32(dev_priv->saveDSPACNTR, DSPACNTR); - PSB_WVDC32(dev_priv->saveDSPASURF, DSPASURF); + PSB_WVDC32(regs->psb.saveDSPACNTR, DSPACNTR); + PSB_WVDC32(regs->psb.saveDSPASURF, DSPASURF); /* Enable Cursor A */ - PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR); - PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS); - PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE); + PSB_WVDC32(regs->psb.saveDSPACURSOR_CTRL, CURACNTR); + PSB_WVDC32(regs->psb.saveDSPACURSOR_POS, CURAPOS); + PSB_WVDC32(regs->psb.saveDSPACURSOR_BASE, CURABASE); /* Restore palette (gamma) */ for (i = 0; i < 256; i++) - PSB_WVDC32(dev_priv->save_palette_a[i], PALETTE_A + (i << 2)); + PSB_WVDC32(regs->psb.save_palette_a[i], PALETTE_A + (i << 2)); if (dev_priv->hdmi_priv) oaktrail_hdmi_restore(dev); if (dev_priv->iLVDS_enable) { - PSB_WVDC32(dev_priv->saveBLC_PWM_CTL2, BLC_PWM_CTL2); - PSB_WVDC32(dev_priv->saveLVDS, LVDS); /*port 61180h*/ - PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL); - PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); - PSB_WVDC32(dev_priv->savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS); - PSB_WVDC32(dev_priv->saveBLC_PWM_CTL, BLC_PWM_CTL); - PSB_WVDC32(dev_priv->savePP_ON_DELAYS, LVDSPP_ON); - PSB_WVDC32(dev_priv->savePP_OFF_DELAYS, LVDSPP_OFF); - PSB_WVDC32(dev_priv->savePP_DIVISOR, PP_CYCLE); - PSB_WVDC32(dev_priv->savePP_CONTROL, PP_CONTROL); + PSB_WVDC32(regs->saveBLC_PWM_CTL2, BLC_PWM_CTL2); + PSB_WVDC32(regs->psb.saveLVDS, LVDS); /*port 61180h*/ + PSB_WVDC32(regs->psb.savePFIT_CONTROL, PFIT_CONTROL); + PSB_WVDC32(regs->psb.savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); + PSB_WVDC32(regs->psb.savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS); + PSB_WVDC32(regs->saveBLC_PWM_CTL, BLC_PWM_CTL); + PSB_WVDC32(regs->psb.savePP_ON_DELAYS, LVDSPP_ON); + PSB_WVDC32(regs->psb.savePP_OFF_DELAYS, LVDSPP_OFF); + PSB_WVDC32(regs->psb.savePP_DIVISOR, PP_CYCLE); + PSB_WVDC32(regs->psb.savePP_CONTROL, PP_CONTROL); } /* Wait for cycle delay */ @@ -388,20 +386,20 @@ static int oaktrail_restore_display_registers(struct drm_device *dev) } while (pp_stat & 0x10000000); /* Restore HW overlay */ - PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD); - PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0); - PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1); - PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2); - PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3); - PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4); - PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5); + PSB_WVDC32(regs->psb.saveOV_OVADD, OV_OVADD); + PSB_WVDC32(regs->psb.saveOV_OGAMC0, OV_OGAMC0); + PSB_WVDC32(regs->psb.saveOV_OGAMC1, OV_OGAMC1); + PSB_WVDC32(regs->psb.saveOV_OGAMC2, OV_OGAMC2); + PSB_WVDC32(regs->psb.saveOV_OGAMC3, OV_OGAMC3); + PSB_WVDC32(regs->psb.saveOV_OGAMC4, OV_OGAMC4); + PSB_WVDC32(regs->psb.saveOV_OGAMC5, OV_OGAMC5); /* DPST registers */ - PSB_WVDC32(dev_priv->saveHISTOGRAM_INT_CONTROL_REG, + PSB_WVDC32(regs->psb.saveHISTOGRAM_INT_CONTROL_REG, HISTOGRAM_INT_CONTROL); - PSB_WVDC32(dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG, + PSB_WVDC32(regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG, HISTOGRAM_LOGIC_CONTROL); - PSB_WVDC32(dev_priv->savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC); + PSB_WVDC32(regs->psb.savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC); return 0; } @@ -502,7 +500,6 @@ const struct psb_ops oaktrail_chip_ops = { .backlight_init = oaktrail_backlight_init, #endif - .init_pm = oaktrail_init_pm, .save_regs = oaktrail_save_display_registers, .restore_regs = oaktrail_restore_display_registers, .power_down = oaktrail_power_down, diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c index 025d30970cc0..f8b367b45f66 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c @@ -125,59 +125,6 @@ static const struct oaktrail_hdmi_limit oaktrail_hdmi_limit = { .nf = { .min = NF_MIN, .max = NF_MAX }, }; -static void wait_for_vblank(struct drm_device *dev) -{ - /* FIXME: Can we do this as a sleep ? */ - /* Wait for 20ms, i.e. one cycle at 50hz. */ - mdelay(20); -} - -static void scu_busy_loop(void *scu_base) -{ - u32 status = 0; - u32 loop_count = 0; - - status = readl(scu_base + 0x04); - while (status & 1) { - udelay(1); /* scu processing time is in few u secods */ - status = readl(scu_base + 0x04); - loop_count++; - /* break if scu doesn't reset busy bit after huge retry */ - if (loop_count > 1000) { - DRM_DEBUG_KMS("SCU IPC timed out"); - return; - } - } -} - -static void oaktrail_hdmi_reset(struct drm_device *dev) -{ - void *base; - /* FIXME: at least make these defines */ - unsigned int scu_ipc_mmio = 0xff11c000; - int scu_len = 1024; - - base = ioremap((resource_size_t)scu_ipc_mmio, scu_len); - if (base == NULL) { - DRM_ERROR("failed to map SCU mmio\n"); - return; - } - - /* scu ipc: assert hdmi controller reset */ - writel(0xff11d118, base + 0x0c); - writel(0x7fffffdf, base + 0x80); - writel(0x42005, base + 0x0); - scu_busy_loop(base); - - /* scu ipc: de-assert hdmi controller reset */ - writel(0xff11d118, base + 0x0c); - writel(0x7fffffff, base + 0x80); - writel(0x42005, base + 0x0); - scu_busy_loop(base); - - iounmap(base); -} - static void oaktrail_hdmi_audio_enable(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; @@ -208,104 +155,6 @@ static void oaktrail_hdmi_audio_disable(struct drm_device *dev) HDMI_READ(HDMI_HCR); } -void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode) -{ - struct drm_device *dev = crtc->dev; - u32 temp; - - switch (mode) { - case DRM_MODE_DPMS_OFF: - /* Disable VGACNTRL */ - REG_WRITE(VGACNTRL, 0x80000000); - - /* Disable plane */ - temp = REG_READ(DSPBCNTR); - if ((temp & DISPLAY_PLANE_ENABLE) != 0) { - REG_WRITE(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE); - REG_READ(DSPBCNTR); - /* Flush the plane changes */ - REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); - REG_READ(DSPBSURF); - } - - /* Disable pipe B */ - temp = REG_READ(PIPEBCONF); - if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(PIPEBCONF, temp & ~PIPEACONF_ENABLE); - REG_READ(PIPEBCONF); - } - - /* Disable LNW Pipes, etc */ - temp = REG_READ(PCH_PIPEBCONF); - if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(PCH_PIPEBCONF, temp & ~PIPEACONF_ENABLE); - REG_READ(PCH_PIPEBCONF); - } - /* wait for pipe off */ - udelay(150); - /* Disable dpll */ - temp = REG_READ(DPLL_CTRL); - if ((temp & DPLL_PWRDN) == 0) { - REG_WRITE(DPLL_CTRL, temp | (DPLL_PWRDN | DPLL_RESET)); - REG_WRITE(DPLL_STATUS, 0x1); - } - /* wait for dpll off */ - udelay(150); - break; - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - /* Enable dpll */ - temp = REG_READ(DPLL_CTRL); - if ((temp & DPLL_PWRDN) != 0) { - REG_WRITE(DPLL_CTRL, temp & ~(DPLL_PWRDN | DPLL_RESET)); - temp = REG_READ(DPLL_CLK_ENABLE); - REG_WRITE(DPLL_CLK_ENABLE, temp | DPLL_EN_DISP | DPLL_SEL_HDMI | DPLL_EN_HDMI); - REG_READ(DPLL_CLK_ENABLE); - } - /* wait for dpll warm up */ - udelay(150); - - /* Enable pipe B */ - temp = REG_READ(PIPEBCONF); - if ((temp & PIPEACONF_ENABLE) == 0) { - REG_WRITE(PIPEBCONF, temp | PIPEACONF_ENABLE); - REG_READ(PIPEBCONF); - } - - /* Enable LNW Pipe B */ - temp = REG_READ(PCH_PIPEBCONF); - if ((temp & PIPEACONF_ENABLE) == 0) { - REG_WRITE(PCH_PIPEBCONF, temp | PIPEACONF_ENABLE); - REG_READ(PCH_PIPEBCONF); - } - wait_for_vblank(dev); - - /* Enable plane */ - temp = REG_READ(DSPBCNTR); - if ((temp & DISPLAY_PLANE_ENABLE) == 0) { - REG_WRITE(DSPBCNTR, temp | DISPLAY_PLANE_ENABLE); - /* Flush the plane changes */ - REG_WRITE(DSPBSURF, REG_READ(DSPBSURF)); - REG_READ(DSPBSURF); - } - psb_intel_crtc_load_lut(crtc); - } - /* DSPARB */ - REG_WRITE(DSPARB, 0x00003fbf); - /* FW1 */ - REG_WRITE(0x70034, 0x3f880a0a); - /* FW2 */ - REG_WRITE(0x70038, 0x0b060808); - /* FW4 */ - REG_WRITE(0x70050, 0x08030404); - /* FW5 */ - REG_WRITE(0x70054, 0x04040404); - /* LNC Chicken Bits */ - REG_WRITE(0x70400, 0x4000); -} - - static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode) { static int dpms_mode = -1; @@ -327,182 +176,6 @@ static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode) HDMI_WRITE(HDMI_VIDEO_REG, temp); } -static unsigned int htotal_calculate(struct drm_display_mode *mode) -{ - u32 htotal, new_crtc_htotal; - - htotal = (mode->crtc_hdisplay - 1) | ((mode->crtc_htotal - 1) << 16); - - /* - * 1024 x 768 new_crtc_htotal = 0x1024; - * 1280 x 1024 new_crtc_htotal = 0x0c34; - */ - new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock; - - return (mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16); -} - -static void oaktrail_hdmi_find_dpll(struct drm_crtc *crtc, int target, - int refclk, struct oaktrail_hdmi_clock *best_clock) -{ - int np_min, np_max, nr_min, nr_max; - int np, nr, nf; - - np_min = DIV_ROUND_UP(oaktrail_hdmi_limit.vco.min, target * 10); - np_max = oaktrail_hdmi_limit.vco.max / (target * 10); - if (np_min < oaktrail_hdmi_limit.np.min) - np_min = oaktrail_hdmi_limit.np.min; - if (np_max > oaktrail_hdmi_limit.np.max) - np_max = oaktrail_hdmi_limit.np.max; - - nr_min = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_max)); - nr_max = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_min)); - if (nr_min < oaktrail_hdmi_limit.nr.min) - nr_min = oaktrail_hdmi_limit.nr.min; - if (nr_max > oaktrail_hdmi_limit.nr.max) - nr_max = oaktrail_hdmi_limit.nr.max; - - np = DIV_ROUND_UP((refclk * 1000), (target * 10 * nr_max)); - nr = DIV_ROUND_UP((refclk * 1000), (target * 10 * np)); - nf = DIV_ROUND_CLOSEST((target * 10 * np * nr), refclk); - DRM_DEBUG_KMS("np, nr, nf %d %d %d\n", np, nr, nf); - - /* - * 1024 x 768 np = 1; nr = 0x26; nf = 0x0fd8000; - * 1280 x 1024 np = 1; nr = 0x17; nf = 0x1034000; - */ - best_clock->np = np; - best_clock->nr = nr - 1; - best_clock->nf = (nf << 14); -} - -int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode, - int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; - int pipe = 1; - int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B; - int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B; - int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B; - int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B; - int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B; - int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B; - int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE; - int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS; - int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - int refclk; - struct oaktrail_hdmi_clock clock; - u32 dspcntr, pipeconf, dpll, temp; - int dspcntr_reg = DSPBCNTR; - - /* Disable the VGA plane that we never use */ - REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); - - /* XXX: Disable the panel fitter if it was on our pipe */ - - /* Disable dpll if necessary */ - dpll = REG_READ(DPLL_CTRL); - if ((dpll & DPLL_PWRDN) == 0) { - REG_WRITE(DPLL_CTRL, dpll | (DPLL_PWRDN | DPLL_RESET)); - REG_WRITE(DPLL_DIV_CTRL, 0x00000000); - REG_WRITE(DPLL_STATUS, 0x1); - } - udelay(150); - - /* reset controller: FIXME - can we sort out the ioremap mess ? */ - iounmap(hdmi_dev->regs); - oaktrail_hdmi_reset(dev); - - /* program and enable dpll */ - refclk = 25000; - oaktrail_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock); - - /* Setting DPLL */ - dpll = REG_READ(DPLL_CTRL); - dpll &= ~DPLL_PDIV_MASK; - dpll &= ~(DPLL_PWRDN | DPLL_RESET); - REG_WRITE(DPLL_CTRL, 0x00000008); - REG_WRITE(DPLL_DIV_CTRL, ((clock.nf << 6) | clock.nr)); - REG_WRITE(DPLL_ADJUST, ((clock.nf >> 14) - 1)); - REG_WRITE(DPLL_CTRL, (dpll | (clock.np << DPLL_PDIV_SHIFT) | DPLL_ENSTAT | DPLL_DITHEN)); - REG_WRITE(DPLL_UPDATE, 0x80000000); - REG_WRITE(DPLL_CLK_ENABLE, 0x80050102); - udelay(150); - - hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len); - if (hdmi_dev->regs == NULL) { - DRM_ERROR("failed to do hdmi mmio mapping\n"); - return -ENOMEM; - } - - /* configure HDMI */ - HDMI_WRITE(0x1004, 0x1fd); - HDMI_WRITE(0x2000, 0x1); - HDMI_WRITE(0x2008, 0x0); - HDMI_WRITE(0x3130, 0x8); - HDMI_WRITE(0x101c, 0x1800810); - - temp = htotal_calculate(adjusted_mode); - REG_WRITE(htot_reg, temp); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); - REG_WRITE(pipesrc_reg, - ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); - - REG_WRITE(PCH_HTOTAL_B, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(PCH_HBLANK_B, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(PCH_HSYNC_B, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(PCH_VTOTAL_B, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(PCH_VBLANK_B, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(PCH_VSYNC_B, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); - REG_WRITE(PCH_PIPEBSRC, - ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1)); - - temp = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start; - HDMI_WRITE(HDMI_HBLANK_A, ((adjusted_mode->crtc_hdisplay - 1) << 16) | temp); - - REG_WRITE(dspsize_reg, - ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); - REG_WRITE(dsppos_reg, 0); - - /* Flush the plane changes */ - { - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->mode_set_base(crtc, x, y, old_fb); - } - - /* Set up the display plane register */ - dspcntr = REG_READ(dspcntr_reg); - dspcntr |= DISPPLANE_GAMMA_ENABLE; - dspcntr |= DISPPLANE_SEL_PIPE_B; - dspcntr |= DISPLAY_PLANE_ENABLE; - - /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); - pipeconf |= PIPEACONF_ENABLE; - - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); - - REG_WRITE(PCH_PIPEBCONF, pipeconf); - REG_READ(PCH_PIPEBCONF); - wait_for_vblank(dev); - - REG_WRITE(dspcntr_reg, dspcntr); - wait_for_vblank(dev); - - return 0; -} - static int oaktrail_hdmi_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { @@ -692,7 +365,7 @@ failed_connector: static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) }, - {} + { 0 } }; void oaktrail_hdmi_setup(struct drm_device *dev) @@ -766,6 +439,7 @@ void oaktrail_hdmi_save(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + struct psb_state *regs = &dev_priv->regs.psb; int i; /* dpll */ @@ -776,14 +450,14 @@ void oaktrail_hdmi_save(struct drm_device *dev) hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE); /* pipe B */ - dev_priv->savePIPEBCONF = PSB_RVDC32(PIPEBCONF); - dev_priv->savePIPEBSRC = PSB_RVDC32(PIPEBSRC); - dev_priv->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B); - dev_priv->saveHBLANK_B = PSB_RVDC32(HBLANK_B); - dev_priv->saveHSYNC_B = PSB_RVDC32(HSYNC_B); - dev_priv->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B); - dev_priv->saveVBLANK_B = PSB_RVDC32(VBLANK_B); - dev_priv->saveVSYNC_B = PSB_RVDC32(VSYNC_B); + regs->savePIPEBCONF = PSB_RVDC32(PIPEBCONF); + regs->savePIPEBSRC = PSB_RVDC32(PIPEBSRC); + regs->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B); + regs->saveHBLANK_B = PSB_RVDC32(HBLANK_B); + regs->saveHSYNC_B = PSB_RVDC32(HSYNC_B); + regs->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B); + regs->saveVBLANK_B = PSB_RVDC32(VBLANK_B); + regs->saveVSYNC_B = PSB_RVDC32(VSYNC_B); hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF); hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC); @@ -795,21 +469,21 @@ void oaktrail_hdmi_save(struct drm_device *dev) hdmi_dev->savePCH_VSYNC_B = PSB_RVDC32(PCH_VSYNC_B); /* plane */ - dev_priv->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR); - dev_priv->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE); - dev_priv->saveDSPBADDR = PSB_RVDC32(DSPBBASE); - dev_priv->saveDSPBSURF = PSB_RVDC32(DSPBSURF); - dev_priv->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF); - dev_priv->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF); + regs->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR); + regs->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE); + regs->saveDSPBADDR = PSB_RVDC32(DSPBBASE); + regs->saveDSPBSURF = PSB_RVDC32(DSPBSURF); + regs->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF); + regs->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF); /* cursor B */ - dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR); - dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE); - dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS); + regs->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR); + regs->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE); + regs->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS); /* save palette */ for (i = 0; i < 256; i++) - dev_priv->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2)); + regs->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2)); } /* restore HDMI register state */ @@ -817,6 +491,7 @@ void oaktrail_hdmi_restore(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + struct psb_state *regs = &dev_priv->regs.psb; int i; /* dpll */ @@ -828,13 +503,13 @@ void oaktrail_hdmi_restore(struct drm_device *dev) DRM_UDELAY(150); /* pipe */ - PSB_WVDC32(dev_priv->savePIPEBSRC, PIPEBSRC); - PSB_WVDC32(dev_priv->saveHTOTAL_B, HTOTAL_B); - PSB_WVDC32(dev_priv->saveHBLANK_B, HBLANK_B); - PSB_WVDC32(dev_priv->saveHSYNC_B, HSYNC_B); - PSB_WVDC32(dev_priv->saveVTOTAL_B, VTOTAL_B); - PSB_WVDC32(dev_priv->saveVBLANK_B, VBLANK_B); - PSB_WVDC32(dev_priv->saveVSYNC_B, VSYNC_B); + PSB_WVDC32(regs->savePIPEBSRC, PIPEBSRC); + PSB_WVDC32(regs->saveHTOTAL_B, HTOTAL_B); + PSB_WVDC32(regs->saveHBLANK_B, HBLANK_B); + PSB_WVDC32(regs->saveHSYNC_B, HSYNC_B); + PSB_WVDC32(regs->saveVTOTAL_B, VTOTAL_B); + PSB_WVDC32(regs->saveVBLANK_B, VBLANK_B); + PSB_WVDC32(regs->saveVSYNC_B, VSYNC_B); PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC); PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B); @@ -844,22 +519,22 @@ void oaktrail_hdmi_restore(struct drm_device *dev) PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B); PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B, PCH_VSYNC_B); - PSB_WVDC32(dev_priv->savePIPEBCONF, PIPEBCONF); + PSB_WVDC32(regs->savePIPEBCONF, PIPEBCONF); PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF); /* plane */ - PSB_WVDC32(dev_priv->saveDSPBLINOFF, DSPBLINOFF); - PSB_WVDC32(dev_priv->saveDSPBSTRIDE, DSPBSTRIDE); - PSB_WVDC32(dev_priv->saveDSPBTILEOFF, DSPBTILEOFF); - PSB_WVDC32(dev_priv->saveDSPBCNTR, DSPBCNTR); - PSB_WVDC32(dev_priv->saveDSPBSURF, DSPBSURF); + PSB_WVDC32(regs->saveDSPBLINOFF, DSPBLINOFF); + PSB_WVDC32(regs->saveDSPBSTRIDE, DSPBSTRIDE); + PSB_WVDC32(regs->saveDSPBTILEOFF, DSPBTILEOFF); + PSB_WVDC32(regs->saveDSPBCNTR, DSPBCNTR); + PSB_WVDC32(regs->saveDSPBSURF, DSPBSURF); /* cursor B */ - PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR); - PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS); - PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE); + PSB_WVDC32(regs->saveDSPBCURSOR_CTRL, CURBCNTR); + PSB_WVDC32(regs->saveDSPBCURSOR_POS, CURBPOS); + PSB_WVDC32(regs->saveDSPBCURSOR_BASE, CURBBASE); /* restore palette */ for (i = 0; i < 256; i++) - PSB_WVDC32(dev_priv->save_palette_b[i], PALETTE_B + (i << 2)); + PSB_WVDC32(regs->save_palette_b[i], PALETTE_B + (i << 2)); } diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c index 705440874ac0..5e84fbde749b 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c @@ -127,7 +127,7 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, { struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap); struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev; - int i, err = 0; + int i; mutex_lock(&i2c_dev->i2c_lock); @@ -139,9 +139,9 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap, for (i = 0; i < num; i++) { if (pmsg->len && pmsg->buf) { if (pmsg->flags & I2C_M_RD) - err = xfer_read(adap, pmsg); + xfer_read(adap, pmsg); else - err = xfer_write(adap, pmsg); + xfer_write(adap, pmsg); } pmsg++; /* next message */ } diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index 238bbe105304..654f32b22b21 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -192,7 +192,7 @@ static u32 oaktrail_lvds_get_max_backlight(struct drm_device *dev) gma_power_end(dev); } else - ret = ((dev_priv->saveBLC_PWM_CTL & + ret = ((dev_priv->regs.saveBLC_PWM_CTL & BACKLIGHT_MODULATION_FREQ_MASK) >> BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; @@ -331,7 +331,6 @@ void oaktrail_lvds_init(struct drm_device *dev, struct drm_encoder *encoder; struct drm_psb_private *dev_priv = dev->dev_private; struct edid *edid; - int ret = 0; struct i2c_adapter *i2c_adap; struct drm_display_mode *scan; /* *modes, *bios_mode; */ @@ -400,7 +399,7 @@ void oaktrail_lvds_init(struct drm_device *dev, if (edid) { drm_mode_connector_update_edid_property(connector, edid); - ret = drm_add_edid_modes(connector, edid); + drm_add_edid_modes(connector, edid); kfree(edid); } diff --git a/drivers/gpu/drm/gma500/power.c b/drivers/gpu/drm/gma500/power.c index 94025693bae1..889b854751da 100644 --- a/drivers/gpu/drm/gma500/power.c +++ b/drivers/gpu/drm/gma500/power.c @@ -58,7 +58,8 @@ void gma_power_init(struct drm_device *dev) spin_lock_init(&power_ctrl_lock); mutex_init(&power_mutex); - dev_priv->ops->init_pm(dev); + if (dev_priv->ops->init_pm) + dev_priv->ops->init_pm(dev); } /** @@ -101,9 +102,6 @@ static void gma_resume_display(struct pci_dev *pdev) struct drm_device *dev = pci_get_drvdata(pdev); struct drm_psb_private *dev_priv = dev->dev_private; - if (dev_priv->suspended == false) - return; - /* turn on the display power island */ dev_priv->ops->power_up(dev); dev_priv->suspended = false; @@ -132,9 +130,9 @@ static void gma_suspend_pci(struct pci_dev *pdev) pci_save_state(pdev); pci_read_config_dword(pdev, 0x5C, &bsm); - dev_priv->saveBSM = bsm; + dev_priv->regs.saveBSM = bsm; pci_read_config_dword(pdev, 0xFC, &vbt); - dev_priv->saveVBT = vbt; + dev_priv->regs.saveVBT = vbt; pci_read_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, &dev_priv->msi_addr); pci_read_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, &dev_priv->msi_data); @@ -162,8 +160,8 @@ static bool gma_resume_pci(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - pci_write_config_dword(pdev, 0x5c, dev_priv->saveBSM); - pci_write_config_dword(pdev, 0xFC, dev_priv->saveVBT); + pci_write_config_dword(pdev, 0x5c, dev_priv->regs.saveBSM); + pci_write_config_dword(pdev, 0xFC, dev_priv->regs.saveVBT); /* restoring MSI address and data in PCIx space */ pci_write_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, dev_priv->msi_addr); pci_write_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, dev_priv->msi_data); @@ -195,6 +193,7 @@ int gma_power_suspend(struct device *_dev) if (!dev_priv->suspended) { if (dev_priv->display_count) { mutex_unlock(&power_mutex); + dev_err(dev->dev, "GPU hardware busy, cannot suspend\n"); return -EBUSY; } psb_irq_uninstall(dev); @@ -302,7 +301,7 @@ int psb_runtime_suspend(struct device *dev) int psb_runtime_resume(struct device *dev) { - return gma_power_resume(dev);; + return gma_power_resume(dev); } int psb_runtime_idle(struct device *dev) diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index e5f5906172b0..95d163e4f1f4 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -177,16 +177,17 @@ static int psb_save_display_registers(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; struct drm_crtc *crtc; struct drm_connector *connector; + struct psb_state *regs = &dev_priv->regs.psb; /* Display arbitration control + watermarks */ - dev_priv->saveDSPARB = PSB_RVDC32(DSPARB); - dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1); - dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2); - dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3); - dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4); - dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5); - dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6); - dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); + regs->saveDSPARB = PSB_RVDC32(DSPARB); + regs->saveDSPFW1 = PSB_RVDC32(DSPFW1); + regs->saveDSPFW2 = PSB_RVDC32(DSPFW2); + regs->saveDSPFW3 = PSB_RVDC32(DSPFW3); + regs->saveDSPFW4 = PSB_RVDC32(DSPFW4); + regs->saveDSPFW5 = PSB_RVDC32(DSPFW5); + regs->saveDSPFW6 = PSB_RVDC32(DSPFW6); + regs->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT); /* Save crtc and output state */ mutex_lock(&dev->mode_config.mutex); @@ -213,16 +214,17 @@ static int psb_restore_display_registers(struct drm_device *dev) struct drm_psb_private *dev_priv = dev->dev_private; struct drm_crtc *crtc; struct drm_connector *connector; + struct psb_state *regs = &dev_priv->regs.psb; /* Display arbitration + watermarks */ - PSB_WVDC32(dev_priv->saveDSPARB, DSPARB); - PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1); - PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2); - PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3); - PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4); - PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5); - PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6); - PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT); + PSB_WVDC32(regs->saveDSPARB, DSPARB); + PSB_WVDC32(regs->saveDSPFW1, DSPFW1); + PSB_WVDC32(regs->saveDSPFW2, DSPFW2); + PSB_WVDC32(regs->saveDSPFW3, DSPFW3); + PSB_WVDC32(regs->saveDSPFW4, DSPFW4); + PSB_WVDC32(regs->saveDSPFW5, DSPFW5); + PSB_WVDC32(regs->saveDSPFW6, DSPFW6); + PSB_WVDC32(regs->saveCHICKENBIT, DSPCHICKENBIT); /*make sure VGA plane is off. it initializes to on after reset!*/ PSB_WVDC32(0x80000000, VGACNTRL); diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index f14768f2b364..c34adf9d910a 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -60,6 +60,16 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { /* Atom E620 */ { 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops}, #endif +#if defined(CONFIG_DRM_MEDFIELD) + {0x8086, 0x0130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + {0x8086, 0x0131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + {0x8086, 0x0132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + {0x8086, 0x0133, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + {0x8086, 0x0134, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + {0x8086, 0x0135, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + {0x8086, 0x0136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, + {0x8086, 0x0137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops}, +#endif #if defined(CONFIG_DRM_GMA3600) { 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, { 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, @@ -70,7 +80,7 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { { 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, { 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, #endif - { 0, 0, 0} + { 0, } }; MODULE_DEVICE_TABLE(pci, pciidlist); @@ -78,27 +88,27 @@ MODULE_DEVICE_TABLE(pci, pciidlist); * Standard IOCTLs. */ -#define DRM_IOCTL_PSB_ADB \ +#define DRM_IOCTL_GMA_ADB \ DRM_IOWR(DRM_GMA_ADB + DRM_COMMAND_BASE, uint32_t) -#define DRM_IOCTL_PSB_MODE_OPERATION \ +#define DRM_IOCTL_GMA_MODE_OPERATION \ DRM_IOWR(DRM_GMA_MODE_OPERATION + DRM_COMMAND_BASE, \ struct drm_psb_mode_operation_arg) -#define DRM_IOCTL_PSB_STOLEN_MEMORY \ +#define DRM_IOCTL_GMA_STOLEN_MEMORY \ DRM_IOWR(DRM_GMA_STOLEN_MEMORY + DRM_COMMAND_BASE, \ struct drm_psb_stolen_memory_arg) -#define DRM_IOCTL_PSB_GAMMA \ +#define DRM_IOCTL_GMA_GAMMA \ DRM_IOWR(DRM_GMA_GAMMA + DRM_COMMAND_BASE, \ struct drm_psb_dpst_lut_arg) -#define DRM_IOCTL_PSB_DPST_BL \ +#define DRM_IOCTL_GMA_DPST_BL \ DRM_IOWR(DRM_GMA_DPST_BL + DRM_COMMAND_BASE, \ uint32_t) -#define DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID \ +#define DRM_IOCTL_GMA_GET_PIPE_FROM_CRTC_ID \ DRM_IOWR(DRM_GMA_GET_PIPE_FROM_CRTC_ID + DRM_COMMAND_BASE, \ struct drm_psb_get_pipe_from_crtc_id_arg) -#define DRM_IOCTL_PSB_GEM_CREATE \ +#define DRM_IOCTL_GMA_GEM_CREATE \ DRM_IOWR(DRM_GMA_GEM_CREATE + DRM_COMMAND_BASE, \ struct drm_psb_gem_create) -#define DRM_IOCTL_PSB_GEM_MMAP \ +#define DRM_IOCTL_GMA_GEM_MMAP \ DRM_IOWR(DRM_GMA_GEM_MMAP + DRM_COMMAND_BASE, \ struct drm_psb_gem_mmap) @@ -113,22 +123,19 @@ static int psb_gamma_ioctl(struct drm_device *dev, void *data, static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -#define PSB_IOCTL_DEF(ioctl, func, flags) \ - [DRM_IOCTL_NR(ioctl) - DRM_COMMAND_BASE] = {ioctl, flags, func} - static struct drm_ioctl_desc psb_ioctls[] = { - PSB_IOCTL_DEF(DRM_IOCTL_PSB_ADB, psb_adb_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_MODE_OPERATION, psb_mode_operation_ioctl, + DRM_IOCTL_DEF_DRV(GMA_ADB, psb_adb_ioctl, DRM_AUTH), + DRM_IOCTL_DEF_DRV(GMA_MODE_OPERATION, psb_mode_operation_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_STOLEN_MEMORY, psb_stolen_memory_ioctl, + DRM_IOCTL_DEF_DRV(GMA_STOLEN_MEMORY, psb_stolen_memory_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_GAMMA, psb_gamma_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_DPST_BL, psb_dpst_bl_ioctl, DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID, + DRM_IOCTL_DEF_DRV(GMA_GAMMA, psb_gamma_ioctl, DRM_AUTH), + DRM_IOCTL_DEF_DRV(GMA_DPST_BL, psb_dpst_bl_ioctl, DRM_AUTH), + DRM_IOCTL_DEF_DRV(GMA_GET_PIPE_FROM_CRTC_ID, psb_intel_get_pipe_from_crtc_id, 0), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_CREATE, psb_gem_create_ioctl, + DRM_IOCTL_DEF_DRV(GMA_GEM_CREATE, psb_gem_create_ioctl, DRM_UNLOCKED | DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_MMAP, psb_gem_mmap_ioctl, + DRM_IOCTL_DEF_DRV(GMA_GEM_MMAP, psb_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), }; @@ -268,10 +275,8 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) { struct drm_psb_private *dev_priv; unsigned long resource_start; - struct psb_gtt *pg; unsigned long irqflags; int ret = -ENOMEM; - uint32_t tt_pages; struct drm_connector *connector; struct psb_intel_encoder *psb_intel_encoder; @@ -283,6 +288,8 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->dev = dev; dev->dev_private = (void *) dev_priv; + pci_set_master(dev->pdev); + if (!IS_PSB(dev)) { if (pci_enable_msi(dev->pdev)) dev_warn(dev->dev, "Enabling MSI failed!\n"); @@ -327,12 +334,6 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) if (!dev_priv->mmu) goto out_err; - pg = &dev_priv->gtt; - - tt_pages = (pg->gatt_pages < PSB_TT_PRIV0_PLIMIT) ? - (pg->gatt_pages) : PSB_TT_PRIV0_PLIMIT; - - dev_priv->pf_pd = psb_mmu_alloc_pd(dev_priv->mmu, 1, 0); if (!dev_priv->pf_pd) goto out_err; @@ -409,7 +410,7 @@ out_err: return ret; } -int psb_driver_device_is_agp(struct drm_device *dev) +static int psb_driver_device_is_agp(struct drm_device *dev) { return 0; } @@ -600,7 +601,7 @@ static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd, /* When a client dies: * - Check for and clean up flipped page state */ -void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv) +static void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv) { } @@ -677,7 +678,9 @@ static struct pci_driver psb_pci_driver = { .id_table = pciidlist, .probe = psb_probe, .remove = psb_remove, - .driver.pm = &psb_pm_ops, + .driver = { + .pm = &psb_pm_ops, + } }; static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index eb1568a0da95..40ce2c9bc2e4 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -276,6 +276,217 @@ struct intel_gmbus { u32 reg0; }; +/* + * Register save state. This is used to hold the context when the + * device is powered off. In the case of Oaktrail this can (but does not + * yet) include screen blank. Operations occuring during the save + * update the register cache instead. + */ +struct psb_state { + uint32_t saveDSPACNTR; + uint32_t saveDSPBCNTR; + uint32_t savePIPEACONF; + uint32_t savePIPEBCONF; + uint32_t savePIPEASRC; + uint32_t savePIPEBSRC; + uint32_t saveFPA0; + uint32_t saveFPA1; + uint32_t saveDPLL_A; + uint32_t saveDPLL_A_MD; + uint32_t saveHTOTAL_A; + uint32_t saveHBLANK_A; + uint32_t saveHSYNC_A; + uint32_t saveVTOTAL_A; + uint32_t saveVBLANK_A; + uint32_t saveVSYNC_A; + uint32_t saveDSPASTRIDE; + uint32_t saveDSPASIZE; + uint32_t saveDSPAPOS; + uint32_t saveDSPABASE; + uint32_t saveDSPASURF; + uint32_t saveDSPASTATUS; + uint32_t saveFPB0; + uint32_t saveFPB1; + uint32_t saveDPLL_B; + uint32_t saveDPLL_B_MD; + uint32_t saveHTOTAL_B; + uint32_t saveHBLANK_B; + uint32_t saveHSYNC_B; + uint32_t saveVTOTAL_B; + uint32_t saveVBLANK_B; + uint32_t saveVSYNC_B; + uint32_t saveDSPBSTRIDE; + uint32_t saveDSPBSIZE; + uint32_t saveDSPBPOS; + uint32_t saveDSPBBASE; + uint32_t saveDSPBSURF; + uint32_t saveDSPBSTATUS; + uint32_t saveVCLK_DIVISOR_VGA0; + uint32_t saveVCLK_DIVISOR_VGA1; + uint32_t saveVCLK_POST_DIV; + uint32_t saveVGACNTRL; + uint32_t saveADPA; + uint32_t saveLVDS; + uint32_t saveDVOA; + uint32_t saveDVOB; + uint32_t saveDVOC; + uint32_t savePP_ON; + uint32_t savePP_OFF; + uint32_t savePP_CONTROL; + uint32_t savePP_CYCLE; + uint32_t savePFIT_CONTROL; + uint32_t savePaletteA[256]; + uint32_t savePaletteB[256]; + uint32_t saveCLOCKGATING; + uint32_t saveDSPARB; + uint32_t saveDSPATILEOFF; + uint32_t saveDSPBTILEOFF; + uint32_t saveDSPAADDR; + uint32_t saveDSPBADDR; + uint32_t savePFIT_AUTO_RATIOS; + uint32_t savePFIT_PGM_RATIOS; + uint32_t savePP_ON_DELAYS; + uint32_t savePP_OFF_DELAYS; + uint32_t savePP_DIVISOR; + uint32_t saveBCLRPAT_A; + uint32_t saveBCLRPAT_B; + uint32_t saveDSPALINOFF; + uint32_t saveDSPBLINOFF; + uint32_t savePERF_MODE; + uint32_t saveDSPFW1; + uint32_t saveDSPFW2; + uint32_t saveDSPFW3; + uint32_t saveDSPFW4; + uint32_t saveDSPFW5; + uint32_t saveDSPFW6; + uint32_t saveCHICKENBIT; + uint32_t saveDSPACURSOR_CTRL; + uint32_t saveDSPBCURSOR_CTRL; + uint32_t saveDSPACURSOR_BASE; + uint32_t saveDSPBCURSOR_BASE; + uint32_t saveDSPACURSOR_POS; + uint32_t saveDSPBCURSOR_POS; + uint32_t save_palette_a[256]; + uint32_t save_palette_b[256]; + uint32_t saveOV_OVADD; + uint32_t saveOV_OGAMC0; + uint32_t saveOV_OGAMC1; + uint32_t saveOV_OGAMC2; + uint32_t saveOV_OGAMC3; + uint32_t saveOV_OGAMC4; + uint32_t saveOV_OGAMC5; + uint32_t saveOVC_OVADD; + uint32_t saveOVC_OGAMC0; + uint32_t saveOVC_OGAMC1; + uint32_t saveOVC_OGAMC2; + uint32_t saveOVC_OGAMC3; + uint32_t saveOVC_OGAMC4; + uint32_t saveOVC_OGAMC5; + + /* DPST register save */ + uint32_t saveHISTOGRAM_INT_CONTROL_REG; + uint32_t saveHISTOGRAM_LOGIC_CONTROL_REG; + uint32_t savePWM_CONTROL_LOGIC; +}; + +struct medfield_state { + uint32_t saveDPLL_A; + uint32_t saveFPA0; + uint32_t savePIPEACONF; + uint32_t saveHTOTAL_A; + uint32_t saveHBLANK_A; + uint32_t saveHSYNC_A; + uint32_t saveVTOTAL_A; + uint32_t saveVBLANK_A; + uint32_t saveVSYNC_A; + uint32_t savePIPEASRC; + uint32_t saveDSPASTRIDE; + uint32_t saveDSPALINOFF; + uint32_t saveDSPATILEOFF; + uint32_t saveDSPASIZE; + uint32_t saveDSPAPOS; + uint32_t saveDSPASURF; + uint32_t saveDSPACNTR; + uint32_t saveDSPASTATUS; + uint32_t save_palette_a[256]; + uint32_t saveMIPI; + + uint32_t saveDPLL_B; + uint32_t saveFPB0; + uint32_t savePIPEBCONF; + uint32_t saveHTOTAL_B; + uint32_t saveHBLANK_B; + uint32_t saveHSYNC_B; + uint32_t saveVTOTAL_B; + uint32_t saveVBLANK_B; + uint32_t saveVSYNC_B; + uint32_t savePIPEBSRC; + uint32_t saveDSPBSTRIDE; + uint32_t saveDSPBLINOFF; + uint32_t saveDSPBTILEOFF; + uint32_t saveDSPBSIZE; + uint32_t saveDSPBPOS; + uint32_t saveDSPBSURF; + uint32_t saveDSPBCNTR; + uint32_t saveDSPBSTATUS; + uint32_t save_palette_b[256]; + + uint32_t savePIPECCONF; + uint32_t saveHTOTAL_C; + uint32_t saveHBLANK_C; + uint32_t saveHSYNC_C; + uint32_t saveVTOTAL_C; + uint32_t saveVBLANK_C; + uint32_t saveVSYNC_C; + uint32_t savePIPECSRC; + uint32_t saveDSPCSTRIDE; + uint32_t saveDSPCLINOFF; + uint32_t saveDSPCTILEOFF; + uint32_t saveDSPCSIZE; + uint32_t saveDSPCPOS; + uint32_t saveDSPCSURF; + uint32_t saveDSPCCNTR; + uint32_t saveDSPCSTATUS; + uint32_t save_palette_c[256]; + uint32_t saveMIPI_C; + + uint32_t savePFIT_CONTROL; + uint32_t savePFIT_PGM_RATIOS; + uint32_t saveHDMIPHYMISCCTL; + uint32_t saveHDMIB_CONTROL; +}; + +struct cdv_state { + uint32_t saveDSPCLK_GATE_D; + uint32_t saveRAMCLK_GATE_D; + uint32_t saveDSPARB; + uint32_t saveDSPFW[6]; + uint32_t saveADPA; + uint32_t savePP_CONTROL; + uint32_t savePFIT_PGM_RATIOS; + uint32_t saveLVDS; + uint32_t savePFIT_CONTROL; + uint32_t savePP_ON_DELAYS; + uint32_t savePP_OFF_DELAYS; + uint32_t savePP_CYCLE; + uint32_t saveVGACNTRL; + uint32_t saveIER; + uint32_t saveIMR; + u8 saveLBB; +}; + +struct psb_save_area { + uint32_t saveBSM; + uint32_t saveVBT; + union { + struct psb_state psb; + struct medfield_state mdfld; + struct cdv_state cdv; + }; + uint32_t saveBLC_PWM_CTL2; + uint32_t saveBLC_PWM_CTL; +}; + struct psb_ops; #define PSB_NUM_PIPE 3 @@ -397,216 +608,21 @@ struct drm_psb_private { struct oaktrail_vbt vbt_data; struct oaktrail_gct_data gct_data; - /* MIPI Panel type etc */ - int panel_id; - bool dual_mipi; /* dual display - DPI & DBI */ - bool dpi_panel_on; /* The DPI panel power is on */ - bool dpi_panel_on2; /* The DPI panel power is on */ - bool dbi_panel_on; /* The DBI panel power is on */ - bool dbi_panel_on2; /* The DBI panel power is on */ - u32 dsr_fb_update; /* DSR FB update counter */ - - /* Moorestown HDMI state */ + /* Oaktrail HDMI state */ struct oaktrail_hdmi_dev *hdmi_priv; - - /* Moorestown pipe config register value cache */ - uint32_t pipeconf; - uint32_t pipeconf1; - uint32_t pipeconf2; - - /* Moorestown plane control register value cache */ - uint32_t dspcntr; - uint32_t dspcntr1; - uint32_t dspcntr2; - - /* Moorestown MM backlight cache */ - uint8_t saveBKLTCNT; - uint8_t saveBKLTREQ; - uint8_t saveBKLTBRTL; - + /* * Register state */ - uint32_t saveDSPACNTR; - uint32_t saveDSPBCNTR; - uint32_t savePIPEACONF; - uint32_t savePIPEBCONF; - uint32_t savePIPEASRC; - uint32_t savePIPEBSRC; - uint32_t saveFPA0; - uint32_t saveFPA1; - uint32_t saveDPLL_A; - uint32_t saveDPLL_A_MD; - uint32_t saveHTOTAL_A; - uint32_t saveHBLANK_A; - uint32_t saveHSYNC_A; - uint32_t saveVTOTAL_A; - uint32_t saveVBLANK_A; - uint32_t saveVSYNC_A; - uint32_t saveDSPASTRIDE; - uint32_t saveDSPASIZE; - uint32_t saveDSPAPOS; - uint32_t saveDSPABASE; - uint32_t saveDSPASURF; - uint32_t saveDSPASTATUS; - uint32_t saveFPB0; - uint32_t saveFPB1; - uint32_t saveDPLL_B; - uint32_t saveDPLL_B_MD; - uint32_t saveHTOTAL_B; - uint32_t saveHBLANK_B; - uint32_t saveHSYNC_B; - uint32_t saveVTOTAL_B; - uint32_t saveVBLANK_B; - uint32_t saveVSYNC_B; - uint32_t saveDSPBSTRIDE; - uint32_t saveDSPBSIZE; - uint32_t saveDSPBPOS; - uint32_t saveDSPBBASE; - uint32_t saveDSPBSURF; - uint32_t saveDSPBSTATUS; - uint32_t saveVCLK_DIVISOR_VGA0; - uint32_t saveVCLK_DIVISOR_VGA1; - uint32_t saveVCLK_POST_DIV; - uint32_t saveVGACNTRL; - uint32_t saveADPA; - uint32_t saveLVDS; - uint32_t saveDVOA; - uint32_t saveDVOB; - uint32_t saveDVOC; - uint32_t savePP_ON; - uint32_t savePP_OFF; - uint32_t savePP_CONTROL; - uint32_t savePP_CYCLE; - uint32_t savePFIT_CONTROL; - uint32_t savePaletteA[256]; - uint32_t savePaletteB[256]; - uint32_t saveBLC_PWM_CTL2; - uint32_t saveBLC_PWM_CTL; - uint32_t saveCLOCKGATING; - uint32_t saveDSPARB; - uint32_t saveDSPATILEOFF; - uint32_t saveDSPBTILEOFF; - uint32_t saveDSPAADDR; - uint32_t saveDSPBADDR; - uint32_t savePFIT_AUTO_RATIOS; - uint32_t savePFIT_PGM_RATIOS; - uint32_t savePP_ON_DELAYS; - uint32_t savePP_OFF_DELAYS; - uint32_t savePP_DIVISOR; - uint32_t saveBSM; - uint32_t saveVBT; - uint32_t saveBCLRPAT_A; - uint32_t saveBCLRPAT_B; - uint32_t saveDSPALINOFF; - uint32_t saveDSPBLINOFF; - uint32_t savePERF_MODE; - uint32_t saveDSPFW1; - uint32_t saveDSPFW2; - uint32_t saveDSPFW3; - uint32_t saveDSPFW4; - uint32_t saveDSPFW5; - uint32_t saveDSPFW6; - uint32_t saveCHICKENBIT; - uint32_t saveDSPACURSOR_CTRL; - uint32_t saveDSPBCURSOR_CTRL; - uint32_t saveDSPACURSOR_BASE; - uint32_t saveDSPBCURSOR_BASE; - uint32_t saveDSPACURSOR_POS; - uint32_t saveDSPBCURSOR_POS; - uint32_t save_palette_a[256]; - uint32_t save_palette_b[256]; - uint32_t saveOV_OVADD; - uint32_t saveOV_OGAMC0; - uint32_t saveOV_OGAMC1; - uint32_t saveOV_OGAMC2; - uint32_t saveOV_OGAMC3; - uint32_t saveOV_OGAMC4; - uint32_t saveOV_OGAMC5; - uint32_t saveOVC_OVADD; - uint32_t saveOVC_OGAMC0; - uint32_t saveOVC_OGAMC1; - uint32_t saveOVC_OGAMC2; - uint32_t saveOVC_OGAMC3; - uint32_t saveOVC_OGAMC4; - uint32_t saveOVC_OGAMC5; + + struct psb_save_area regs; /* MSI reg save */ uint32_t msi_addr; uint32_t msi_data; - /* Medfield specific register save state */ - uint32_t saveHDMIPHYMISCCTL; - uint32_t saveHDMIB_CONTROL; - uint32_t saveDSPCCNTR; - uint32_t savePIPECCONF; - uint32_t savePIPECSRC; - uint32_t saveHTOTAL_C; - uint32_t saveHBLANK_C; - uint32_t saveHSYNC_C; - uint32_t saveVTOTAL_C; - uint32_t saveVBLANK_C; - uint32_t saveVSYNC_C; - uint32_t saveDSPCSTRIDE; - uint32_t saveDSPCSIZE; - uint32_t saveDSPCPOS; - uint32_t saveDSPCSURF; - uint32_t saveDSPCSTATUS; - uint32_t saveDSPCLINOFF; - uint32_t saveDSPCTILEOFF; - uint32_t saveDSPCCURSOR_CTRL; - uint32_t saveDSPCCURSOR_BASE; - uint32_t saveDSPCCURSOR_POS; - uint32_t save_palette_c[256]; - uint32_t saveOV_OVADD_C; - uint32_t saveOV_OGAMC0_C; - uint32_t saveOV_OGAMC1_C; - uint32_t saveOV_OGAMC2_C; - uint32_t saveOV_OGAMC3_C; - uint32_t saveOV_OGAMC4_C; - uint32_t saveOV_OGAMC5_C; - - /* DSI register save */ - uint32_t saveDEVICE_READY_REG; - uint32_t saveINTR_EN_REG; - uint32_t saveDSI_FUNC_PRG_REG; - uint32_t saveHS_TX_TIMEOUT_REG; - uint32_t saveLP_RX_TIMEOUT_REG; - uint32_t saveTURN_AROUND_TIMEOUT_REG; - uint32_t saveDEVICE_RESET_REG; - uint32_t saveDPI_RESOLUTION_REG; - uint32_t saveHORIZ_SYNC_PAD_COUNT_REG; - uint32_t saveHORIZ_BACK_PORCH_COUNT_REG; - uint32_t saveHORIZ_FRONT_PORCH_COUNT_REG; - uint32_t saveHORIZ_ACTIVE_AREA_COUNT_REG; - uint32_t saveVERT_SYNC_PAD_COUNT_REG; - uint32_t saveVERT_BACK_PORCH_COUNT_REG; - uint32_t saveVERT_FRONT_PORCH_COUNT_REG; - uint32_t saveHIGH_LOW_SWITCH_COUNT_REG; - uint32_t saveINIT_COUNT_REG; - uint32_t saveMAX_RET_PAK_REG; - uint32_t saveVIDEO_FMT_REG; - uint32_t saveEOT_DISABLE_REG; - uint32_t saveLP_BYTECLK_REG; - uint32_t saveHS_LS_DBI_ENABLE_REG; - uint32_t saveTXCLKESC_REG; - uint32_t saveDPHY_PARAM_REG; - uint32_t saveMIPI_CONTROL_REG; - uint32_t saveMIPI; - uint32_t saveMIPI_C; - - /* DPST register save */ - uint32_t saveHISTOGRAM_INT_CONTROL_REG; - uint32_t saveHISTOGRAM_LOGIC_CONTROL_REG; - uint32_t savePWM_CONTROL_LOGIC; /* - * DSI info. - */ - void * dbi_dsr_info; - void * dbi_dpu_info; - void * dsi_configs[2]; - /* * LID-Switch */ spinlock_t lid_lock; @@ -635,6 +651,24 @@ struct drm_psb_private { /* 2D acceleration */ spinlock_t lock_2d; + + /* + * Panel brightness + */ + int brightness; + int brightness_adjusted; + + bool dsr_enable; + u32 dsr_fb_update; + bool dpi_panel_on[3]; + void *dsi_configs[2]; + u32 bpp; + u32 bpp2; + + u32 pipeconf[3]; + u32 dspcntr[3]; + + int mdfld_panel_id; }; @@ -830,6 +864,9 @@ extern const struct psb_ops psb_chip_ops; /* oaktrail_device.c */ extern const struct psb_ops oaktrail_chip_ops; +/* mdlfd_device.c */ +extern const struct psb_ops mdfld_chip_ops; + /* cdv_device.c */ extern const struct psb_ops cdv_chip_ops; diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index 49e983508d5c..2616558457c8 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -333,7 +333,7 @@ void psb_intel_wait_for_vblank(struct drm_device *dev) mdelay(20); } -int psb_intel_pipe_set_base(struct drm_crtc *crtc, +static int psb_intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; @@ -433,7 +433,6 @@ static void psb_intel_crtc_dpms(struct drm_crtc *crtc, int mode) int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE; int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; u32 temp; - bool enabled; /* XXX: When our outputs are all unaware of DPMS modes other than off * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. @@ -518,8 +517,6 @@ static void psb_intel_crtc_dpms(struct drm_crtc *crtc, int mode) break; } - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; - /*Set FIFO Watermarks*/ REG_WRITE(DSPARB, 0x3F3E); } @@ -611,8 +608,8 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, int refclk; struct psb_intel_clock_t clock; u32 dpll = 0, fp = 0, dspcntr, pipeconf; - bool ok, is_sdvo = false, is_dvo = false; - bool is_crt = false, is_lvds = false, is_tv = false; + bool ok, is_sdvo = false; + bool is_lvds = false, is_tv = false; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; @@ -637,15 +634,9 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_SDVO: is_sdvo = true; break; - case INTEL_OUTPUT_DVO: - is_dvo = true; - break; case INTEL_OUTPUT_TVOUT: is_tv = true; break; - case INTEL_OUTPUT_ANALOG: - is_crt = true; - break; } } @@ -845,7 +836,7 @@ void psb_intel_crtc_load_lut(struct drm_crtc *crtc) gma_power_end(dev); } else { for (i = 0; i < 256; i++) { - dev_priv->save_palette_a[i] = + dev_priv->regs.psb.save_palette_a[i] = ((psb_intel_crtc->lut_r[i] + psb_intel_crtc->lut_adj[i]) << 16) | ((psb_intel_crtc->lut_g[i] + @@ -1141,18 +1132,20 @@ static int psb_intel_crtc_clock_get(struct drm_device *dev, gma_power_end(dev); } else { dpll = (pipe == 0) ? - dev_priv->saveDPLL_A : dev_priv->saveDPLL_B; + dev_priv->regs.psb.saveDPLL_A : + dev_priv->regs.psb.saveDPLL_B; if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) fp = (pipe == 0) ? - dev_priv->saveFPA0 : - dev_priv->saveFPB0; + dev_priv->regs.psb.saveFPA0 : + dev_priv->regs.psb.saveFPB0; else fp = (pipe == 0) ? - dev_priv->saveFPA1 : - dev_priv->saveFPB1; + dev_priv->regs.psb.saveFPA1 : + dev_priv->regs.psb.saveFPB1; - is_lvds = (pipe == 1) && (dev_priv->saveLVDS & LVDS_PORT_EN); + is_lvds = (pipe == 1) && (dev_priv->regs.psb.saveLVDS & + LVDS_PORT_EN); } clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; @@ -1218,13 +1211,17 @@ struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, gma_power_end(dev); } else { htot = (pipe == 0) ? - dev_priv->saveHTOTAL_A : dev_priv->saveHTOTAL_B; + dev_priv->regs.psb.saveHTOTAL_A : + dev_priv->regs.psb.saveHTOTAL_B; hsync = (pipe == 0) ? - dev_priv->saveHSYNC_A : dev_priv->saveHSYNC_B; + dev_priv->regs.psb.saveHSYNC_A : + dev_priv->regs.psb.saveHSYNC_B; vtot = (pipe == 0) ? - dev_priv->saveVTOTAL_A : dev_priv->saveVTOTAL_B; + dev_priv->regs.psb.saveVTOTAL_A : + dev_priv->regs.psb.saveVTOTAL_B; vsync = (pipe == 0) ? - dev_priv->saveVSYNC_A : dev_priv->saveVSYNC_B; + dev_priv->regs.psb.saveVSYNC_A : + dev_priv->regs.psb.saveVSYNC_B; } mode = kzalloc(sizeof(*mode), GFP_KERNEL); @@ -1419,13 +1416,6 @@ int psb_intel_connector_clones(struct drm_device *dev, int type_mask) return index_mask; } - -void psb_intel_modeset_cleanup(struct drm_device *dev) -{ - drm_mode_config_cleanup(dev); -} - - /* current intel driver doesn't take advantage of encoders always give back the encoder for the connector */ diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index 0a437586d8cc..c83f5b5d1057 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -77,7 +77,7 @@ static u32 psb_intel_lvds_get_max_backlight(struct drm_device *dev) ret = REG_READ(BLC_PWM_CTL); gma_power_end(dev); } else /* Powered off, use the saved value */ - ret = dev_priv->saveBLC_PWM_CTL; + ret = dev_priv->regs.saveBLC_PWM_CTL; /* Top 15bits hold the frequency mask */ ret = (ret & BACKLIGHT_MODULATION_FREQ_MASK) >> @@ -86,7 +86,7 @@ static u32 psb_intel_lvds_get_max_backlight(struct drm_device *dev) ret *= 2; /* Return a 16bit range as needed for setting */ if (ret == 0) dev_err(dev->dev, "BL bug: Reg %08x save %08X\n", - REG_READ(BLC_PWM_CTL), dev_priv->saveBLC_PWM_CTL); + REG_READ(BLC_PWM_CTL), dev_priv->regs.saveBLC_PWM_CTL); return ret; } @@ -203,13 +203,13 @@ static void psb_intel_lvds_set_backlight(struct drm_device *dev, int level) REG_WRITE(BLC_PWM_CTL, (blc_pwm_ctl | (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); - dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl | + dev_priv->regs.saveBLC_PWM_CTL = (blc_pwm_ctl | (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); gma_power_end(dev); } else { - blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL & + blc_pwm_ctl = dev_priv->regs.saveBLC_PWM_CTL & ~BACKLIGHT_DUTY_CYCLE_MASK; - dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl | + dev_priv->regs.saveBLC_PWM_CTL = (blc_pwm_ctl | (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); } } @@ -283,7 +283,7 @@ static void psb_intel_lvds_save(struct drm_connector *connector) lvds_priv->savePFIT_PGM_RATIOS = REG_READ(PFIT_PGM_RATIOS); /*TODO: move backlight_duty_cycle to psb_intel_lvds_priv*/ - dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL & + dev_priv->backlight_duty_cycle = (dev_priv->regs.saveBLC_PWM_CTL & BACKLIGHT_DUTY_CYCLE_MASK); /* diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h index fcc0af03d685..e89d3a2e8fdc 100644 --- a/drivers/gpu/drm/gma500/psb_intel_reg.h +++ b/drivers/gpu/drm/gma500/psb_intel_reg.h @@ -177,6 +177,9 @@ #define LVDSPP_OFF 0x6120c #define PP_CYCLE 0x61210 +#define PP_ON_DELAYS 0x61208 /* Cedartrail */ +#define PP_OFF_DELAYS 0x6120c /* Cedartrail */ + #define PFIT_CONTROL 0x61230 #define PFIT_ENABLE (1 << 31) #define PFIT_PIPE_MASK (3 << 29) @@ -1252,6 +1255,12 @@ No status bits are changed. # define SB_BYTE_ENABLE_SHIFT 4 # define SB_BUSY (1 << 0) +#define DSPCLK_GATE_D 0x6200 +# define VRHUNIT_CLOCK_GATE_DISABLE (1 << 28) /* Fixed value on CDV */ +# define DPOUNIT_CLOCK_GATE_DISABLE (1 << 11) +# define DPIOUNIT_CLOCK_GATE_DISABLE (1 << 6) + +#define RAMCLK_GATE_D 0x6210 /* 32-bit value read/written from the DPIO reg. */ #define SB_DATA 0x02104 /* cedarview */ diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index 88b42971c0fd..36330cabcea2 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1301,7 +1301,7 @@ psb_intel_sdvo_get_analog_edid(struct drm_connector *connector) return NULL; } -enum drm_connector_status +static enum drm_connector_status psb_intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) { struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector); @@ -2312,10 +2312,8 @@ static bool psb_intel_sdvo_tv_create_property(struct psb_intel_sdvo *psb_intel_s psb_intel_sdvo_connector->max_##name = data_value[0]; \ psb_intel_sdvo_connector->cur_##name = response; \ psb_intel_sdvo_connector->name = \ - drm_property_create(dev, DRM_MODE_PROP_RANGE, #name, 2); \ + drm_property_create_range(dev, 0, #name, 0, data_value[0]); \ if (!psb_intel_sdvo_connector->name) return false; \ - psb_intel_sdvo_connector->name->values[0] = 0; \ - psb_intel_sdvo_connector->name->values[1] = data_value[0]; \ drm_connector_attach_property(connector, \ psb_intel_sdvo_connector->name, \ psb_intel_sdvo_connector->cur_##name); \ @@ -2349,25 +2347,19 @@ psb_intel_sdvo_create_enhance_property_tv(struct psb_intel_sdvo *psb_intel_sdvo, psb_intel_sdvo_connector->left_margin = data_value[0] - response; psb_intel_sdvo_connector->right_margin = psb_intel_sdvo_connector->left_margin; psb_intel_sdvo_connector->left = - drm_property_create(dev, DRM_MODE_PROP_RANGE, - "left_margin", 2); + drm_property_create_range(dev, 0, "left_margin", 0, data_value[0]); if (!psb_intel_sdvo_connector->left) return false; - psb_intel_sdvo_connector->left->values[0] = 0; - psb_intel_sdvo_connector->left->values[1] = data_value[0]; drm_connector_attach_property(connector, psb_intel_sdvo_connector->left, psb_intel_sdvo_connector->left_margin); psb_intel_sdvo_connector->right = - drm_property_create(dev, DRM_MODE_PROP_RANGE, - "right_margin", 2); + drm_property_create_range(dev, 0, "right_margin", 0, data_value[0]); if (!psb_intel_sdvo_connector->right) return false; - psb_intel_sdvo_connector->right->values[0] = 0; - psb_intel_sdvo_connector->right->values[1] = data_value[0]; drm_connector_attach_property(connector, psb_intel_sdvo_connector->right, psb_intel_sdvo_connector->right_margin); @@ -2391,25 +2383,19 @@ psb_intel_sdvo_create_enhance_property_tv(struct psb_intel_sdvo *psb_intel_sdvo, psb_intel_sdvo_connector->top_margin = data_value[0] - response; psb_intel_sdvo_connector->bottom_margin = psb_intel_sdvo_connector->top_margin; psb_intel_sdvo_connector->top = - drm_property_create(dev, DRM_MODE_PROP_RANGE, - "top_margin", 2); + drm_property_create_range(dev, 0, "top_margin", 0, data_value[0]); if (!psb_intel_sdvo_connector->top) return false; - psb_intel_sdvo_connector->top->values[0] = 0; - psb_intel_sdvo_connector->top->values[1] = data_value[0]; drm_connector_attach_property(connector, psb_intel_sdvo_connector->top, psb_intel_sdvo_connector->top_margin); psb_intel_sdvo_connector->bottom = - drm_property_create(dev, DRM_MODE_PROP_RANGE, - "bottom_margin", 2); + drm_property_create_range(dev, 0, "bottom_margin", 0, data_value[0]); if (!psb_intel_sdvo_connector->bottom) return false; - psb_intel_sdvo_connector->bottom->values[0] = 0; - psb_intel_sdvo_connector->bottom->values[1] = data_value[0]; drm_connector_attach_property(connector, psb_intel_sdvo_connector->bottom, psb_intel_sdvo_connector->bottom_margin); @@ -2438,12 +2424,10 @@ psb_intel_sdvo_create_enhance_property_tv(struct psb_intel_sdvo *psb_intel_sdvo, psb_intel_sdvo_connector->max_dot_crawl = 1; psb_intel_sdvo_connector->cur_dot_crawl = response & 0x1; psb_intel_sdvo_connector->dot_crawl = - drm_property_create(dev, DRM_MODE_PROP_RANGE, "dot_crawl", 2); + drm_property_create_range(dev, 0, "dot_crawl", 0, 1); if (!psb_intel_sdvo_connector->dot_crawl) return false; - psb_intel_sdvo_connector->dot_crawl->values[0] = 0; - psb_intel_sdvo_connector->dot_crawl->values[1] = 1; drm_connector_attach_property(connector, psb_intel_sdvo_connector->dot_crawl, psb_intel_sdvo_connector->cur_dot_crawl); diff --git a/drivers/gpu/drm/gma500/psb_irq.c b/drivers/gpu/drm/gma500/psb_irq.c index 7be802baceb5..1869586457b1 100644 --- a/drivers/gpu/drm/gma500/psb_irq.c +++ b/drivers/gpu/drm/gma500/psb_irq.c @@ -27,6 +27,8 @@ #include "psb_reg.h" #include "psb_intel_reg.h" #include "power.h" +#include "psb_irq.h" +#include "mdfld_output.h" /* * inline functions @@ -113,7 +115,7 @@ psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask) } } -void mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe) +static void mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe) { if (gma_power_begin(dev_priv->dev, false)) { u32 pipe_event = mid_pipe_event(pipe); @@ -124,7 +126,7 @@ void mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe) } } -void mid_disable_pipe_event(struct drm_psb_private *dev_priv, int pipe) +static void mid_disable_pipe_event(struct drm_psb_private *dev_priv, int pipe) { if (dev_priv->pipestat[pipe] == 0) { if (gma_power_begin(dev_priv->dev, false)) { @@ -453,6 +455,11 @@ int psb_enable_vblank(struct drm_device *dev, int pipe) uint32_t reg_val = 0; uint32_t pipeconf_reg = mid_pipeconf(pipe); + /* Medfield is different - we should perhaps extract out vblank + and blacklight etc ops */ + if (IS_MFLD(dev)) + return mdfld_enable_te(dev, pipe); + if (gma_power_begin(dev, false)) { reg_val = REG_READ(pipeconf_reg); gma_power_end(dev); @@ -485,6 +492,8 @@ void psb_disable_vblank(struct drm_device *dev, int pipe) struct drm_psb_private *dev_priv = dev->dev_private; unsigned long irqflags; + if (IS_MFLD(dev)) + mdfld_disable_te(dev, pipe); spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); if (pipe == 0) @@ -499,6 +508,55 @@ void psb_disable_vblank(struct drm_device *dev, int pipe) spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); } +/* + * It is used to enable TE interrupt + */ +int mdfld_enable_te(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + unsigned long irqflags; + uint32_t reg_val = 0; + uint32_t pipeconf_reg = mid_pipeconf(pipe); + + if (gma_power_begin(dev, false)) { + reg_val = REG_READ(pipeconf_reg); + gma_power_end(dev); + } + + if (!(reg_val & PIPEACONF_ENABLE)) + return -EINVAL; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + mid_enable_pipe_event(dev_priv, pipe); + psb_enable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE); + + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); + + return 0; +} + +/* + * It is used to disable TE interrupt + */ +void mdfld_disable_te(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = + (struct drm_psb_private *) dev->dev_private; + unsigned long irqflags; + + if (!dev_priv->dsr_enable) + return; + + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + + mid_disable_pipe_event(dev_priv, pipe); + psb_disable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE); + + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); +} + /* Called from drm generic code, passed a 'crtc', which * we use as a pipe index */ diff --git a/drivers/gpu/drm/gma500/psb_irq.h b/drivers/gpu/drm/gma500/psb_irq.h index 216fda38b57d..603045bee58a 100644 --- a/drivers/gpu/drm/gma500/psb_irq.h +++ b/drivers/gpu/drm/gma500/psb_irq.h @@ -42,4 +42,6 @@ int psb_enable_vblank(struct drm_device *dev, int pipe); void psb_disable_vblank(struct drm_device *dev, int pipe); u32 psb_get_vblank_counter(struct drm_device *dev, int pipe); +int mdfld_enable_te(struct drm_device *dev, int pipe); +void mdfld_disable_te(struct drm_device *dev, int pipe); #endif /* _SYSIRQ_H_ */ diff --git a/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c new file mode 100644 index 000000000000..4a07ab596174 --- /dev/null +++ b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c @@ -0,0 +1,829 @@ +/* + * Copyright © 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#include "mdfld_dsi_dpi.h" +#include "mdfld_output.h" +#include "mdfld_dsi_pkg_sender.h" +#include "tc35876x-dsi-lvds.h" +#include <linux/i2c/tc35876x.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/intel_scu_ipc.h> + +static struct i2c_client *tc35876x_client; +static struct i2c_client *cmi_lcd_i2c_client; + +#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end)) +#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end)) + +/* DSI D-PHY Layer Registers */ +#define D0W_DPHYCONTTX 0x0004 +#define CLW_DPHYCONTRX 0x0020 +#define D0W_DPHYCONTRX 0x0024 +#define D1W_DPHYCONTRX 0x0028 +#define D2W_DPHYCONTRX 0x002C +#define D3W_DPHYCONTRX 0x0030 +#define COM_DPHYCONTRX 0x0038 +#define CLW_CNTRL 0x0040 +#define D0W_CNTRL 0x0044 +#define D1W_CNTRL 0x0048 +#define D2W_CNTRL 0x004C +#define D3W_CNTRL 0x0050 +#define DFTMODE_CNTRL 0x0054 + +/* DSI PPI Layer Registers */ +#define PPI_STARTPPI 0x0104 +#define PPI_BUSYPPI 0x0108 +#define PPI_LINEINITCNT 0x0110 +#define PPI_LPTXTIMECNT 0x0114 +#define PPI_LANEENABLE 0x0134 +#define PPI_TX_RX_TA 0x013C +#define PPI_CLS_ATMR 0x0140 +#define PPI_D0S_ATMR 0x0144 +#define PPI_D1S_ATMR 0x0148 +#define PPI_D2S_ATMR 0x014C +#define PPI_D3S_ATMR 0x0150 +#define PPI_D0S_CLRSIPOCOUNT 0x0164 +#define PPI_D1S_CLRSIPOCOUNT 0x0168 +#define PPI_D2S_CLRSIPOCOUNT 0x016C +#define PPI_D3S_CLRSIPOCOUNT 0x0170 +#define CLS_PRE 0x0180 +#define D0S_PRE 0x0184 +#define D1S_PRE 0x0188 +#define D2S_PRE 0x018C +#define D3S_PRE 0x0190 +#define CLS_PREP 0x01A0 +#define D0S_PREP 0x01A4 +#define D1S_PREP 0x01A8 +#define D2S_PREP 0x01AC +#define D3S_PREP 0x01B0 +#define CLS_ZERO 0x01C0 +#define D0S_ZERO 0x01C4 +#define D1S_ZERO 0x01C8 +#define D2S_ZERO 0x01CC +#define D3S_ZERO 0x01D0 +#define PPI_CLRFLG 0x01E0 +#define PPI_CLRSIPO 0x01E4 +#define HSTIMEOUT 0x01F0 +#define HSTIMEOUTENABLE 0x01F4 + +/* DSI Protocol Layer Registers */ +#define DSI_STARTDSI 0x0204 +#define DSI_BUSYDSI 0x0208 +#define DSI_LANEENABLE 0x0210 +#define DSI_LANESTATUS0 0x0214 +#define DSI_LANESTATUS1 0x0218 +#define DSI_INTSTATUS 0x0220 +#define DSI_INTMASK 0x0224 +#define DSI_INTCLR 0x0228 +#define DSI_LPTXTO 0x0230 + +/* DSI General Registers */ +#define DSIERRCNT 0x0300 + +/* DSI Application Layer Registers */ +#define APLCTRL 0x0400 +#define RDPKTLN 0x0404 + +/* Video Path Registers */ +#define VPCTRL 0x0450 +#define HTIM1 0x0454 +#define HTIM2 0x0458 +#define VTIM1 0x045C +#define VTIM2 0x0460 +#define VFUEN 0x0464 + +/* LVDS Registers */ +#define LVMX0003 0x0480 +#define LVMX0407 0x0484 +#define LVMX0811 0x0488 +#define LVMX1215 0x048C +#define LVMX1619 0x0490 +#define LVMX2023 0x0494 +#define LVMX2427 0x0498 +#define LVCFG 0x049C +#define LVPHY0 0x04A0 +#define LVPHY1 0x04A4 + +/* System Registers */ +#define SYSSTAT 0x0500 +#define SYSRST 0x0504 + +/* GPIO Registers */ +/*#define GPIOC 0x0520*/ +#define GPIOO 0x0524 +#define GPIOI 0x0528 + +/* I2C Registers */ +#define I2CTIMCTRL 0x0540 +#define I2CMADDR 0x0544 +#define WDATAQ 0x0548 +#define RDATAQ 0x054C + +/* Chip/Rev Registers */ +#define IDREG 0x0580 + +/* Debug Registers */ +#define DEBUG00 0x05A0 +#define DEBUG01 0x05A4 + +/* Panel CABC registers */ +#define PANEL_PWM_CONTROL 0x90 +#define PANEL_FREQ_DIVIDER_HI 0x91 +#define PANEL_FREQ_DIVIDER_LO 0x92 +#define PANEL_DUTY_CONTROL 0x93 +#define PANEL_MODIFY_RGB 0x94 +#define PANEL_FRAMERATE_CONTROL 0x96 +#define PANEL_PWM_MIN 0x97 +#define PANEL_PWM_REF 0x98 +#define PANEL_PWM_MAX 0x99 +#define PANEL_ALLOW_DISTORT 0x9A +#define PANEL_BYPASS_PWMI 0x9B + +/* Panel color management registers */ +#define PANEL_CM_ENABLE 0x700 +#define PANEL_CM_HUE 0x701 +#define PANEL_CM_SATURATION 0x702 +#define PANEL_CM_INTENSITY 0x703 +#define PANEL_CM_BRIGHTNESS 0x704 +#define PANEL_CM_CE_ENABLE 0x705 +#define PANEL_CM_PEAK_EN 0x710 +#define PANEL_CM_GAIN 0x711 +#define PANEL_CM_HUETABLE_START 0x730 +#define PANEL_CM_HUETABLE_END 0x747 /* inclusive */ + +/* Input muxing for registers LVMX0003...LVMX2427 */ +enum { + INPUT_R0, /* 0 */ + INPUT_R1, + INPUT_R2, + INPUT_R3, + INPUT_R4, + INPUT_R5, + INPUT_R6, + INPUT_R7, + INPUT_G0, /* 8 */ + INPUT_G1, + INPUT_G2, + INPUT_G3, + INPUT_G4, + INPUT_G5, + INPUT_G6, + INPUT_G7, + INPUT_B0, /* 16 */ + INPUT_B1, + INPUT_B2, + INPUT_B3, + INPUT_B4, + INPUT_B5, + INPUT_B6, + INPUT_B7, + INPUT_HSYNC, /* 24 */ + INPUT_VSYNC, + INPUT_DE, + LOGIC_0, + /* 28...31 undefined */ +}; + +#define INPUT_MUX(lvmx03, lvmx02, lvmx01, lvmx00) \ + (FLD_VAL(lvmx03, 29, 24) | FLD_VAL(lvmx02, 20, 16) | \ + FLD_VAL(lvmx01, 12, 8) | FLD_VAL(lvmx00, 4, 0)) + +/** + * tc35876x_regw - Write DSI-LVDS bridge register using I2C + * @client: struct i2c_client to use + * @reg: register address + * @value: value to write + * + * Returns 0 on success, or a negative error value. + */ +static int tc35876x_regw(struct i2c_client *client, u16 reg, u32 value) +{ + int r; + u8 tx_data[] = { + /* NOTE: Register address big-endian, data little-endian. */ + (reg >> 8) & 0xff, + reg & 0xff, + value & 0xff, + (value >> 8) & 0xff, + (value >> 16) & 0xff, + (value >> 24) & 0xff, + }; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .buf = tx_data, + .len = ARRAY_SIZE(tx_data), + }, + }; + + r = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (r < 0) { + dev_err(&client->dev, "%s: reg 0x%04x val 0x%08x error %d\n", + __func__, reg, value, r); + return r; + } + + if (r < ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "%s: reg 0x%04x val 0x%08x msgs %d\n", + __func__, reg, value, r); + return -EAGAIN; + } + + dev_dbg(&client->dev, "%s: reg 0x%04x val 0x%08x\n", + __func__, reg, value); + + return 0; +} + +/** + * tc35876x_regr - Read DSI-LVDS bridge register using I2C + * @client: struct i2c_client to use + * @reg: register address + * @value: pointer for storing the value + * + * Returns 0 on success, or a negative error value. + */ +static int tc35876x_regr(struct i2c_client *client, u16 reg, u32 *value) +{ + int r; + u8 tx_data[] = { + (reg >> 8) & 0xff, + reg & 0xff, + }; + u8 rx_data[4]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .buf = tx_data, + .len = ARRAY_SIZE(tx_data), + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = rx_data, + .len = ARRAY_SIZE(rx_data), + }, + }; + + r = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (r < 0) { + dev_err(&client->dev, "%s: reg 0x%04x error %d\n", __func__, + reg, r); + return r; + } + + if (r < ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "%s: reg 0x%04x msgs %d\n", __func__, + reg, r); + return -EAGAIN; + } + + *value = rx_data[0] << 24 | rx_data[1] << 16 | + rx_data[2] << 8 | rx_data[3]; + + dev_dbg(&client->dev, "%s: reg 0x%04x value 0x%08x\n", __func__, + reg, *value); + + return 0; +} + +void tc35876x_set_bridge_reset_state(struct drm_device *dev, int state) +{ + struct tc35876x_platform_data *pdata; + + if (WARN(!tc35876x_client, "%s called before probe", __func__)) + return; + + dev_dbg(&tc35876x_client->dev, "%s: state %d\n", __func__, state); + + pdata = dev_get_platdata(&tc35876x_client->dev); + + if (pdata->gpio_bridge_reset == -1) + return; + + if (state) { + gpio_set_value_cansleep(pdata->gpio_bridge_reset, 0); + mdelay(10); + } else { + /* Pull MIPI Bridge reset pin to Low */ + gpio_set_value_cansleep(pdata->gpio_bridge_reset, 0); + mdelay(20); + /* Pull MIPI Bridge reset pin to High */ + gpio_set_value_cansleep(pdata->gpio_bridge_reset, 1); + mdelay(40); + } +} + +void tc35876x_configure_lvds_bridge(struct drm_device *dev) +{ + struct i2c_client *i2c = tc35876x_client; + u32 ppi_lptxtimecnt; + u32 txtagocnt; + u32 txtasurecnt; + u32 id; + + if (WARN(!tc35876x_client, "%s called before probe", __func__)) + return; + + dev_dbg(&tc35876x_client->dev, "%s\n", __func__); + + if (!tc35876x_regr(i2c, IDREG, &id)) + dev_info(&tc35876x_client->dev, "tc35876x ID 0x%08x\n", id); + else + dev_err(&tc35876x_client->dev, "Cannot read ID\n"); + + ppi_lptxtimecnt = 4; + txtagocnt = (5 * ppi_lptxtimecnt - 3) / 4; + txtasurecnt = 3 * ppi_lptxtimecnt / 2; + tc35876x_regw(i2c, PPI_TX_RX_TA, FLD_VAL(txtagocnt, 26, 16) | + FLD_VAL(txtasurecnt, 10, 0)); + tc35876x_regw(i2c, PPI_LPTXTIMECNT, FLD_VAL(ppi_lptxtimecnt, 10, 0)); + + tc35876x_regw(i2c, PPI_D0S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0)); + tc35876x_regw(i2c, PPI_D1S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0)); + tc35876x_regw(i2c, PPI_D2S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0)); + tc35876x_regw(i2c, PPI_D3S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0)); + + /* Enabling MIPI & PPI lanes, Enable 4 lanes */ + tc35876x_regw(i2c, PPI_LANEENABLE, + BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0)); + tc35876x_regw(i2c, DSI_LANEENABLE, + BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0)); + tc35876x_regw(i2c, PPI_STARTPPI, BIT(0)); + tc35876x_regw(i2c, DSI_STARTDSI, BIT(0)); + + /* Setting LVDS output frequency */ + tc35876x_regw(i2c, LVPHY0, FLD_VAL(1, 20, 16) | + FLD_VAL(2, 15, 14) | FLD_VAL(6, 4, 0)); /* 0x00048006 */ + + /* Setting video panel control register,0x00000120 VTGen=ON ?!?!? */ + tc35876x_regw(i2c, VPCTRL, BIT(8) | BIT(5)); + + /* Horizontal back porch and horizontal pulse width. 0x00280028 */ + tc35876x_regw(i2c, HTIM1, FLD_VAL(40, 24, 16) | FLD_VAL(40, 8, 0)); + + /* Horizontal front porch and horizontal active video size. 0x00500500*/ + tc35876x_regw(i2c, HTIM2, FLD_VAL(80, 24, 16) | FLD_VAL(1280, 10, 0)); + + /* Vertical back porch and vertical sync pulse width. 0x000e000a */ + tc35876x_regw(i2c, VTIM1, FLD_VAL(14, 23, 16) | FLD_VAL(10, 7, 0)); + + /* Vertical front porch and vertical display size. 0x000e0320 */ + tc35876x_regw(i2c, VTIM2, FLD_VAL(14, 23, 16) | FLD_VAL(800, 10, 0)); + + /* Set above HTIM1, HTIM2, VTIM1, and VTIM2 at next VSYNC. */ + tc35876x_regw(i2c, VFUEN, BIT(0)); + + /* Soft reset LCD controller. */ + tc35876x_regw(i2c, SYSRST, BIT(2)); + + /* LVDS-TX input muxing */ + tc35876x_regw(i2c, LVMX0003, + INPUT_MUX(INPUT_R5, INPUT_R4, INPUT_R3, INPUT_R2)); + tc35876x_regw(i2c, LVMX0407, + INPUT_MUX(INPUT_G2, INPUT_R7, INPUT_R1, INPUT_R6)); + tc35876x_regw(i2c, LVMX0811, + INPUT_MUX(INPUT_G1, INPUT_G0, INPUT_G4, INPUT_G3)); + tc35876x_regw(i2c, LVMX1215, + INPUT_MUX(INPUT_B2, INPUT_G7, INPUT_G6, INPUT_G5)); + tc35876x_regw(i2c, LVMX1619, + INPUT_MUX(INPUT_B4, INPUT_B3, INPUT_B1, INPUT_B0)); + tc35876x_regw(i2c, LVMX2023, + INPUT_MUX(LOGIC_0, INPUT_B7, INPUT_B6, INPUT_B5)); + tc35876x_regw(i2c, LVMX2427, + INPUT_MUX(INPUT_R0, INPUT_DE, INPUT_VSYNC, INPUT_HSYNC)); + + /* Enable LVDS transmitter. */ + tc35876x_regw(i2c, LVCFG, BIT(0)); + + /* Clear notifications. Don't write reserved bits. Was write 0xffffffff + * to 0x0288, must be in error?! */ + tc35876x_regw(i2c, DSI_INTCLR, FLD_MASK(31, 30) | FLD_MASK(22, 0)); +} + +#define GPIOPWMCTRL 0x38F +#define PWM0CLKDIV0 0x62 /* low byte */ +#define PWM0CLKDIV1 0x61 /* high byte */ + +#define SYSTEMCLK 19200000UL /* 19.2 MHz */ +#define PWM_FREQUENCY 9600 /* Hz */ + +/* f = baseclk / (clkdiv + 1) => clkdiv = (baseclk - f) / f */ +static inline u16 calc_clkdiv(unsigned long baseclk, unsigned int f) +{ + return (baseclk - f) / f; +} + +static void tc35876x_brightness_init(struct drm_device *dev) +{ + int ret; + u8 pwmctrl; + u16 clkdiv; + + /* Make sure the PWM reference is the 19.2 MHz system clock. Read first + * instead of setting directly to catch potential conflicts between PWM + * users. */ + ret = intel_scu_ipc_ioread8(GPIOPWMCTRL, &pwmctrl); + if (ret || pwmctrl != 0x01) { + if (ret) + dev_err(&dev->pdev->dev, "GPIOPWMCTRL read failed\n"); + else + dev_warn(&dev->pdev->dev, "GPIOPWMCTRL was not set to system clock (pwmctrl = 0x%02x)\n", pwmctrl); + + ret = intel_scu_ipc_iowrite8(GPIOPWMCTRL, 0x01); + if (ret) + dev_err(&dev->pdev->dev, "GPIOPWMCTRL set failed\n"); + } + + clkdiv = calc_clkdiv(SYSTEMCLK, PWM_FREQUENCY); + + ret = intel_scu_ipc_iowrite8(PWM0CLKDIV1, (clkdiv >> 8) & 0xff); + if (!ret) + ret = intel_scu_ipc_iowrite8(PWM0CLKDIV0, clkdiv & 0xff); + + if (ret) + dev_err(&dev->pdev->dev, "PWM0CLKDIV set failed\n"); + else + dev_dbg(&dev->pdev->dev, "PWM0CLKDIV set to 0x%04x (%d Hz)\n", + clkdiv, PWM_FREQUENCY); +} + +#define PWM0DUTYCYCLE 0x67 + +void tc35876x_brightness_control(struct drm_device *dev, int level) +{ + int ret; + u8 duty_val; + u8 panel_duty_val; + + level = clamp(level, 0, MDFLD_DSI_BRIGHTNESS_MAX_LEVEL); + + /* PWM duty cycle 0x00...0x63 corresponds to 0...99% */ + duty_val = level * 0x63 / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL; + + /* I won't pretend to understand this formula. The panel spec is quite + * bad engrish. + */ + panel_duty_val = (2 * level - 100) * 0xA9 / + MDFLD_DSI_BRIGHTNESS_MAX_LEVEL + 0x56; + + ret = intel_scu_ipc_iowrite8(PWM0DUTYCYCLE, duty_val); + if (ret) + dev_err(&tc35876x_client->dev, "%s: ipc write fail\n", + __func__); + + if (cmi_lcd_i2c_client) { + ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client, + PANEL_PWM_MAX, panel_duty_val); + if (ret < 0) + dev_err(&cmi_lcd_i2c_client->dev, "%s: i2c write failed\n", + __func__); + } +} + +void tc35876x_toshiba_bridge_panel_off(struct drm_device *dev) +{ + struct tc35876x_platform_data *pdata; + + if (WARN(!tc35876x_client, "%s called before probe", __func__)) + return; + + dev_dbg(&tc35876x_client->dev, "%s\n", __func__); + + pdata = dev_get_platdata(&tc35876x_client->dev); + + if (pdata->gpio_panel_bl_en != -1) + gpio_set_value_cansleep(pdata->gpio_panel_bl_en, 0); + + if (pdata->gpio_panel_vadd != -1) + gpio_set_value_cansleep(pdata->gpio_panel_vadd, 0); +} + +void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev) +{ + struct tc35876x_platform_data *pdata; + struct drm_psb_private *dev_priv = dev->dev_private; + + if (WARN(!tc35876x_client, "%s called before probe", __func__)) + return; + + dev_dbg(&tc35876x_client->dev, "%s\n", __func__); + + pdata = dev_get_platdata(&tc35876x_client->dev); + + if (pdata->gpio_panel_vadd != -1) { + gpio_set_value_cansleep(pdata->gpio_panel_vadd, 1); + msleep(260); + } + + if (cmi_lcd_i2c_client) { + int ret; + dev_dbg(&cmi_lcd_i2c_client->dev, "setting TCON\n"); + /* Bit 4 is average_saving. Setting it to 1, the brightness is + * referenced to the average of the frame content. 0 means + * reference to the maximum of frame contents. Bits 3:0 are + * allow_distort. When set to a nonzero value, all color values + * between 255-allow_distort*2 and 255 are mapped to the + * 255-allow_distort*2 value. + */ + ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client, + PANEL_ALLOW_DISTORT, 0x10); + if (ret < 0) + dev_err(&cmi_lcd_i2c_client->dev, + "i2c write failed (%d)\n", ret); + ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client, + PANEL_BYPASS_PWMI, 0); + if (ret < 0) + dev_err(&cmi_lcd_i2c_client->dev, + "i2c write failed (%d)\n", ret); + /* Set minimum brightness value - this is tunable */ + ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client, + PANEL_PWM_MIN, 0x35); + if (ret < 0) + dev_err(&cmi_lcd_i2c_client->dev, + "i2c write failed (%d)\n", ret); + } + + if (pdata->gpio_panel_bl_en != -1) + gpio_set_value_cansleep(pdata->gpio_panel_bl_en, 1); + + tc35876x_brightness_control(dev, dev_priv->brightness_adjusted); +} + +static struct drm_display_mode *tc35876x_get_config_mode(struct drm_device *dev) +{ + struct drm_display_mode *mode; + + dev_dbg(&dev->pdev->dev, "%s\n", __func__); + + mode = kzalloc(sizeof(*mode), GFP_KERNEL); + if (!mode) + return NULL; + + /* FIXME: do this properly. */ + mode->hdisplay = 1280; + mode->vdisplay = 800; + mode->hsync_start = 1360; + mode->hsync_end = 1400; + mode->htotal = 1440; + mode->vsync_start = 814; + mode->vsync_end = 824; + mode->vtotal = 838; + mode->clock = 33324 << 1; + + dev_info(&dev->pdev->dev, "hdisplay(w) = %d\n", mode->hdisplay); + dev_info(&dev->pdev->dev, "vdisplay(h) = %d\n", mode->vdisplay); + dev_info(&dev->pdev->dev, "HSS = %d\n", mode->hsync_start); + dev_info(&dev->pdev->dev, "HSE = %d\n", mode->hsync_end); + dev_info(&dev->pdev->dev, "htotal = %d\n", mode->htotal); + dev_info(&dev->pdev->dev, "VSS = %d\n", mode->vsync_start); + dev_info(&dev->pdev->dev, "VSE = %d\n", mode->vsync_end); + dev_info(&dev->pdev->dev, "vtotal = %d\n", mode->vtotal); + dev_info(&dev->pdev->dev, "clock = %d\n", mode->clock); + + drm_mode_set_name(mode); + drm_mode_set_crtcinfo(mode, 0); + + mode->type |= DRM_MODE_TYPE_PREFERRED; + + return mode; +} + +/* DV1 Active area 216.96 x 135.6 mm */ +#define DV1_PANEL_WIDTH 217 +#define DV1_PANEL_HEIGHT 136 + +static int tc35876x_get_panel_info(struct drm_device *dev, int pipe, + struct panel_info *pi) +{ + if (!dev || !pi) + return -EINVAL; + + pi->width_mm = DV1_PANEL_WIDTH; + pi->height_mm = DV1_PANEL_HEIGHT; + + return 0; +} + +static int tc35876x_bridge_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tc35876x_platform_data *pdata; + + dev_info(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "%s: i2c_check_functionality() failed\n", + __func__); + return -ENODEV; + } + + pdata = dev_get_platdata(&client->dev); + if (!pdata) { + dev_err(&client->dev, "%s: no platform data\n", __func__); + return -ENODEV; + } + + if (pdata->gpio_bridge_reset != -1) { + gpio_request(pdata->gpio_bridge_reset, "tc35876x bridge reset"); + gpio_direction_output(pdata->gpio_bridge_reset, 0); + } + + if (pdata->gpio_panel_bl_en != -1) { + gpio_request(pdata->gpio_panel_bl_en, "tc35876x panel bl en"); + gpio_direction_output(pdata->gpio_panel_bl_en, 0); + } + + if (pdata->gpio_panel_vadd != -1) { + gpio_request(pdata->gpio_panel_vadd, "tc35876x panel vadd"); + gpio_direction_output(pdata->gpio_panel_vadd, 0); + } + + tc35876x_client = client; + + return 0; +} + +static int tc35876x_bridge_remove(struct i2c_client *client) +{ + struct tc35876x_platform_data *pdata = dev_get_platdata(&client->dev); + + dev_dbg(&client->dev, "%s\n", __func__); + + if (pdata->gpio_bridge_reset != -1) + gpio_free(pdata->gpio_bridge_reset); + + if (pdata->gpio_panel_bl_en != -1) + gpio_free(pdata->gpio_panel_bl_en); + + if (pdata->gpio_panel_vadd != -1) + gpio_free(pdata->gpio_panel_vadd); + + tc35876x_client = NULL; + + return 0; +} + +static const struct i2c_device_id tc35876x_bridge_id[] = { + { "i2c_disp_brig", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tc35876x_bridge_id); + +static struct i2c_driver tc35876x_bridge_i2c_driver = { + .driver = { + .name = "i2c_disp_brig", + }, + .id_table = tc35876x_bridge_id, + .probe = tc35876x_bridge_probe, + .remove = __devexit_p(tc35876x_bridge_remove), +}; + +/* LCD panel I2C */ +static int cmi_lcd_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + dev_info(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "%s: i2c_check_functionality() failed\n", + __func__); + return -ENODEV; + } + + cmi_lcd_i2c_client = client; + + return 0; +} + +static int cmi_lcd_i2c_remove(struct i2c_client *client) +{ + dev_dbg(&client->dev, "%s\n", __func__); + + cmi_lcd_i2c_client = NULL; + + return 0; +} + +static const struct i2c_device_id cmi_lcd_i2c_id[] = { + { "cmi-lcd", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cmi_lcd_i2c_id); + +static struct i2c_driver cmi_lcd_i2c_driver = { + .driver = { + .name = "cmi-lcd", + }, + .id_table = cmi_lcd_i2c_id, + .probe = cmi_lcd_i2c_probe, + .remove = __devexit_p(cmi_lcd_i2c_remove), +}; + +/* HACK to create I2C device while it's not created by platform code */ +#define CMI_LCD_I2C_ADAPTER 2 +#define CMI_LCD_I2C_ADDR 0x60 + +static int cmi_lcd_hack_create_device(void) +{ + struct i2c_adapter *adapter; + struct i2c_client *client; + struct i2c_board_info info = { + .type = "cmi-lcd", + .addr = CMI_LCD_I2C_ADDR, + }; + + pr_debug("%s\n", __func__); + + adapter = i2c_get_adapter(CMI_LCD_I2C_ADAPTER); + if (!adapter) { + pr_err("%s: i2c_get_adapter(%d) failed\n", __func__, + CMI_LCD_I2C_ADAPTER); + return -EINVAL; + } + + client = i2c_new_device(adapter, &info); + if (!client) { + pr_err("%s: i2c_new_device() failed\n", __func__); + i2c_put_adapter(adapter); + return -EINVAL; + } + + return 0; +} + +static const struct drm_encoder_helper_funcs tc35876x_encoder_helper_funcs = { + .dpms = mdfld_dsi_dpi_dpms, + .mode_fixup = mdfld_dsi_dpi_mode_fixup, + .prepare = mdfld_dsi_dpi_prepare, + .mode_set = mdfld_dsi_dpi_mode_set, + .commit = mdfld_dsi_dpi_commit, +}; + +static const struct drm_encoder_funcs tc35876x_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +const struct panel_funcs mdfld_tc35876x_funcs = { + .encoder_funcs = &tc35876x_encoder_funcs, + .encoder_helper_funcs = &tc35876x_encoder_helper_funcs, + .get_config_mode = tc35876x_get_config_mode, + .get_panel_info = tc35876x_get_panel_info, +}; + +void tc35876x_init(struct drm_device *dev) +{ + int r; + + dev_dbg(&dev->pdev->dev, "%s\n", __func__); + + cmi_lcd_hack_create_device(); + + r = i2c_add_driver(&cmi_lcd_i2c_driver); + if (r < 0) + dev_err(&dev->pdev->dev, + "%s: i2c_add_driver() for %s failed (%d)\n", + __func__, cmi_lcd_i2c_driver.driver.name, r); + + r = i2c_add_driver(&tc35876x_bridge_i2c_driver); + if (r < 0) + dev_err(&dev->pdev->dev, + "%s: i2c_add_driver() for %s failed (%d)\n", + __func__, tc35876x_bridge_i2c_driver.driver.name, r); + + tc35876x_brightness_init(dev); +} + +void tc35876x_exit(void) +{ + pr_debug("%s\n", __func__); + + i2c_del_driver(&tc35876x_bridge_i2c_driver); + + if (cmi_lcd_i2c_client) + i2c_del_driver(&cmi_lcd_i2c_driver); +} diff --git a/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.h b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.h new file mode 100644 index 000000000000..b14b7f9e7d1e --- /dev/null +++ b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2011 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __MDFLD_DSI_LVDS_BRIDGE_H__ +#define __MDFLD_DSI_LVDS_BRIDGE_H__ + +void tc35876x_set_bridge_reset_state(struct drm_device *dev, int state); +void tc35876x_configure_lvds_bridge(struct drm_device *dev); +void tc35876x_brightness_control(struct drm_device *dev, int level); +void tc35876x_toshiba_bridge_panel_off(struct drm_device *dev); +void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev); +void tc35876x_init(struct drm_device *dev); +void tc35876x_exit(void); + +extern const struct panel_funcs mdfld_tc35876x_funcs; + +#endif /*__MDFLD_DSI_LVDS_BRIDGE_H__*/ |