diff options
Diffstat (limited to 'drivers/gpu/drm/gma500')
67 files changed, 13770 insertions, 4872 deletions
diff --git a/drivers/gpu/drm/gma500/Kconfig b/drivers/gpu/drm/gma500/Kconfig index 754e14bdc80..17f928ec84e 100644 --- a/drivers/gpu/drm/gma500/Kconfig +++ b/drivers/gpu/drm/gma500/Kconfig @@ -1,11 +1,16 @@ config DRM_GMA500 tristate "Intel GMA5/600 KMS Framebuffer" - depends on DRM && PCI && X86 && EXPERIMENTAL + depends on DRM && PCI && X86 select FB_CFB_COPYAREA - select FB_CFB_FILLRECT - select FB_CFB_IMAGEBLIT - select DRM_KMS_HELPER - select DRM_TTM + select FB_CFB_FILLRECT + select FB_CFB_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select DRM_TTM + # GMA500 depends on ACPI_VIDEO when ACPI is enabled, just like i915 + select ACPI_VIDEO if ACPI + select BACKLIGHT_CLASS_DEVICE if ACPI + select INPUT if ACPI help Say yes for an experimental 2D KMS framebuffer driver for the Intel GMA500 ('Poulsbo') and other Intel IMG based graphics @@ -16,8 +21,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 +29,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 81c103be5e2..b1531557637 100644 --- a/drivers/gpu/drm/gma500/Makefile +++ b/drivers/gpu/drm/gma500/Makefile @@ -1,9 +1,9 @@ # # KMS driver for the GMA500 # -ccflags-y += -Iinclude/drm +ccflags-y += -I$(srctree)/include/drm -gma500_gfx-y += gem_glue.o \ +gma500_gfx-y += \ accel_2d.o \ backlight.o \ framebuffer.o \ @@ -12,10 +12,12 @@ gma500_gfx-y += gem_glue.o \ intel_bios.o \ intel_i2c.o \ intel_gmbus.o \ - intel_opregion.o \ mmu.o \ + blitter.o \ power.o \ psb_drv.o \ + gma_display.o \ + gma_device.o \ psb_intel_display.o \ psb_intel_lvds.o \ psb_intel_modes.o \ @@ -25,11 +27,14 @@ gma500_gfx-y += gem_glue.o \ psb_device.o \ mid_bios.o +gma500_gfx-$(CONFIG_ACPI) += opregion.o \ + gma500_gfx-$(CONFIG_DRM_GMA3600) += cdv_device.o \ cdv_intel_crt.o \ cdv_intel_display.o \ cdv_intel_hdmi.o \ - cdv_intel_lvds.o + cdv_intel_lvds.o \ + cdv_intel_dp.o gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \ oaktrail_crtc.o \ @@ -37,4 +42,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/accel_2d.c b/drivers/gpu/drm/gma500/accel_2d.c index d5ef1a5793c..de6f62a6ceb 100644 --- a/drivers/gpu/drm/gma500/accel_2d.c +++ b/drivers/gpu/drm/gma500/accel_2d.c @@ -326,7 +326,7 @@ int psbfb_sync(struct fb_info *info) struct psb_framebuffer *psbfb = &fbdev->pfb; struct drm_device *dev = psbfb->base.dev; struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long _end = jiffies + DRM_HZ; + unsigned long _end = jiffies + HZ; int busy = 0; unsigned long flags; diff --git a/drivers/gpu/drm/gma500/backlight.c b/drivers/gpu/drm/gma500/backlight.c index 20793951fca..ea7dfc59d79 100644 --- a/drivers/gpu/drm/gma500/backlight.c +++ b/drivers/gpu/drm/gma500/backlight.c @@ -26,10 +26,55 @@ #include "intel_bios.h" #include "power.h" +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE +static void do_gma_backlight_set(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + backlight_update_status(dev_priv->backlight_device); +} +#endif + +void gma_backlight_enable(struct drm_device *dev) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_enabled = true; + if (dev_priv->backlight_device) { + dev_priv->backlight_device->props.brightness = dev_priv->backlight_level; + do_gma_backlight_set(dev); + } +#endif +} + +void gma_backlight_disable(struct drm_device *dev) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_enabled = false; + if (dev_priv->backlight_device) { + dev_priv->backlight_device->props.brightness = 0; + do_gma_backlight_set(dev); + } +#endif +} + +void gma_backlight_set(struct drm_device *dev, int v) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_level = v; + if (dev_priv->backlight_device && dev_priv->backlight_enabled) { + dev_priv->backlight_device->props.brightness = v; + do_gma_backlight_set(dev); + } +#endif +} + int gma_backlight_init(struct drm_device *dev) { #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_enabled = true; return dev_priv->ops->backlight_init(dev); #else return 0; diff --git a/drivers/gpu/drm/gma500/blitter.c b/drivers/gpu/drm/gma500/blitter.c new file mode 100644 index 00000000000..9cd54a6fb89 --- /dev/null +++ b/drivers/gpu/drm/gma500/blitter.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2014, Patrik Jakobsson + * 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. + * + * Authors: Patrik Jakobsson <patrik.r.jakobsson@gmail.com> + */ + +#include "psb_drv.h" + +#include "blitter.h" +#include "psb_reg.h" + +/* Wait for the blitter to be completely idle */ +int gma_blt_wait_idle(struct drm_psb_private *dev_priv) +{ + unsigned long stop = jiffies + HZ; + int busy = 1; + + /* NOP for Cedarview */ + if (IS_CDV(dev_priv->dev)) + return 0; + + /* First do a quick check */ + if ((PSB_RSGX32(PSB_CR_2D_SOCIF) == _PSB_C2_SOCIF_EMPTY) && + ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & _PSB_C2B_STATUS_BUSY) == 0)) + return 0; + + do { + busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY); + } while (busy && !time_after_eq(jiffies, stop)); + + if (busy) + return -EBUSY; + + do { + busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & + _PSB_C2B_STATUS_BUSY) != 0); + } while (busy && !time_after_eq(jiffies, stop)); + + /* If still busy, we probably have a hang */ + return (busy) ? -EBUSY : 0; +} diff --git a/drivers/gpu/drm/gma500/blitter.h b/drivers/gpu/drm/gma500/blitter.h new file mode 100644 index 00000000000..b83648df590 --- /dev/null +++ b/drivers/gpu/drm/gma500/blitter.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2014, Patrik Jakobsson + * 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. + * + * Authors: Patrik Jakobsson <patrik.r.jakobsson@gmail.com> + */ + +#ifndef __BLITTER_H +#define __BLITTER_H + +extern int gma_blt_wait_idle(struct drm_psb_private *dev_priv); + +#endif diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index 4a5b099c3bc..3531f90e53d 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -20,12 +20,13 @@ #include <linux/backlight.h> #include <drm/drmP.h> #include <drm/drm.h> -#include "gma_drm.h" +#include <drm/gma_drm.h> #include "psb_drv.h" #include "psb_reg.h" #include "psb_intel_reg.h" #include "intel_bios.h" #include "cdv_device.h" +#include "gma_device.h" #define VGA_SR_INDEX 0x3c4 #define VGA_SR_DATA 0x3c5 @@ -49,93 +50,101 @@ static void cdv_disable_vga(struct drm_device *dev) static int cdv_output_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; + + drm_mode_create_scaling_mode_property(dev); + cdv_disable_vga(dev); cdv_intel_crt_init(dev, &dev_priv->mode_dev); cdv_intel_lvds_init(dev, &dev_priv->mode_dev); - /* These bits indicate HDMI not SDVO on CDV, but we don't yet support - the HDMI interface */ - if (REG_READ(SDVOB) & SDVO_DETECTED) + /* These bits indicate HDMI not SDVO on CDV */ + if (REG_READ(SDVOB) & SDVO_DETECTED) { cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOB); - if (REG_READ(SDVOC) & SDVO_DETECTED) + if (REG_READ(DP_B) & DP_DETECTED) + cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_B); + } + + if (REG_READ(SDVOC) & SDVO_DETECTED) { cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOC); + if (REG_READ(DP_C) & DP_DETECTED) + cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_C); + } return 0; } #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE /* - * Poulsbo Backlight Interfaces + * Cedartrail Backlght Interfaces */ -#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ -#define BLC_PWM_FREQ_CALC_CONSTANT 32 -#define MHz 1000000 - -#define PSB_BLC_PWM_PRECISION_FACTOR 10 -#define PSB_BLC_MAX_PWM_REG_FREQ 0xFFFE -#define PSB_BLC_MIN_PWM_REG_FREQ 0x2 - -#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) -#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) - -static int cdv_brightness; static struct backlight_device *cdv_backlight_device; -static int cdv_get_brightness(struct backlight_device *bd) +static int cdv_backlight_combination_mode(struct drm_device *dev) { - /* return locally cached var instead of HW read (due to DPST etc.) */ - /* FIXME: ideally return actual value in case firmware fiddled with - it */ - return cdv_brightness; + return REG_READ(BLC_PWM_CTL2) & PWM_LEGACY_MODE; } - -static int cdv_backlight_setup(struct drm_device *dev) +static u32 cdv_get_max_backlight(struct drm_device *dev) { - struct drm_psb_private *dev_priv = dev->dev_private; - unsigned long core_clock; - /* u32 bl_max_freq; */ - /* unsigned long value; */ - u16 bl_max_freq; - uint32_t value; - uint32_t blc_pwm_precision_factor; - - /* get bl_max_freq and pol from dev_priv*/ - if (!dev_priv->lvds_bl) { - dev_err(dev->dev, "Has no valid LVDS backlight info\n"); - return -ENOENT; + u32 max = REG_READ(BLC_PWM_CTL); + + if (max == 0) { + DRM_DEBUG_KMS("LVDS Panel PWM value is 0!\n"); + /* i915 does this, I believe which means that we should not + * smash PWM control as firmware will take control of it. */ + return 1; } - bl_max_freq = dev_priv->lvds_bl->freq; - blc_pwm_precision_factor = PSB_BLC_PWM_PRECISION_FACTOR; - core_clock = dev_priv->core_freq; + max >>= 16; + if (cdv_backlight_combination_mode(dev)) + max *= 0xff; + return max; +} - value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT; - value *= blc_pwm_precision_factor; - value /= bl_max_freq; - value /= blc_pwm_precision_factor; +static int cdv_get_brightness(struct backlight_device *bd) +{ + struct drm_device *dev = bl_get_data(bd); + u32 val = REG_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; - if (value > (unsigned long long)PSB_BLC_MAX_PWM_REG_FREQ || - value < (unsigned long long)PSB_BLC_MIN_PWM_REG_FREQ) - return -ERANGE; - else { - /* FIXME */ + if (cdv_backlight_combination_mode(dev)) { + u8 lbpc; + + val &= ~1; + pci_read_config_byte(dev->pdev, 0xF4, &lbpc); + val *= lbpc; } - return 0; + return (val * 100)/cdv_get_max_backlight(dev); + } static int cdv_set_brightness(struct backlight_device *bd) { + struct drm_device *dev = bl_get_data(bd); int level = bd->props.brightness; + u32 blc_pwm_ctl; /* Percentage 1-100% being valid */ if (level < 1) level = 1; - /*cdv_intel_lvds_set_brightness(dev, level); FIXME */ - cdv_brightness = level; + level *= cdv_get_max_backlight(dev); + level /= 100; + + if (cdv_backlight_combination_mode(dev)) { + u32 max = cdv_get_max_backlight(dev); + u8 lbpc; + + lbpc = level * 0xfe / max + 1; + level /= lbpc; + + pci_write_config_byte(dev->pdev, 0xF4, lbpc); + } + + blc_pwm_ctl = REG_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; + REG_WRITE(BLC_PWM_CTL, (blc_pwm_ctl | + (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); return 0; } @@ -147,7 +156,6 @@ static const struct backlight_ops cdv_ops = { static int cdv_backlight_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; - int ret; struct backlight_properties props; memset(&props, 0, sizeof(struct backlight_properties)); @@ -159,16 +167,11 @@ static int cdv_backlight_init(struct drm_device *dev) if (IS_ERR(cdv_backlight_device)) return PTR_ERR(cdv_backlight_device); - ret = cdv_backlight_setup(dev); - if (ret < 0) { - backlight_device_unregister(cdv_backlight_device); - cdv_backlight_device = NULL; - return ret; - } - cdv_backlight_device->props.brightness = 100; - cdv_backlight_device->props.max_brightness = 100; + cdv_backlight_device->props.brightness = + cdv_get_brightness(cdv_backlight_device); backlight_update_status(cdv_backlight_device); dev_priv->backlight_device = cdv_backlight_device; + dev_priv->backlight_enabled = true; return 0; } @@ -202,13 +205,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 +223,35 @@ 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"); +} + +static void cdv_errata(struct drm_device *dev) +{ + /* Disable bonus launch. + * CPU and GPU competes for memory and display misses updates and + * flickers. Worst with dual core, dual displays. + * + * Fixes were done to Win 7 gfx driver to disable a feature called + * Bonus Launch to work around the issue, by degrading + * performance. + */ + CDV_MSG_WRITE32(3, 0x30, 0x08027108); } /** @@ -249,11 +260,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_dbg(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,60 +317,275 @@ 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_errata(dev); + + 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; } -/* FIXME ? - shared with Poulsbo */ -static void cdv_get_core_freq(struct drm_device *dev) +static void cdv_hotplug_work_func(struct work_struct *work) { - uint32_t clock; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + struct drm_psb_private *dev_priv = container_of(work, struct drm_psb_private, + hotplug_work); + struct drm_device *dev = dev_priv->dev; + + /* Just fire off a uevent and let userspace tell us what to do */ + drm_helper_hpd_irq_event(dev); +} + +/* The core driver has received a hotplug IRQ. We are in IRQ context + so extract the needed information and kick off queued processing */ + +static int cdv_hotplug_event(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + schedule_work(&dev_priv->hotplug_work); + REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT)); + return 1; +} + +static void cdv_hotplug_enable(struct drm_device *dev, bool on) +{ + if (on) { + u32 hotplug = REG_READ(PORT_HOTPLUG_EN); + hotplug |= HDMIB_HOTPLUG_INT_EN | HDMIC_HOTPLUG_INT_EN | + HDMID_HOTPLUG_INT_EN | CRT_HOTPLUG_INT_EN; + REG_WRITE(PORT_HOTPLUG_EN, hotplug); + } else { + REG_WRITE(PORT_HOTPLUG_EN, 0); + REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT)); + } +} + +static const char *force_audio_names[] = { + "off", + "auto", + "on", +}; + +void cdv_intel_attach_force_audio_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_property *prop; + int i; - pci_write_config_dword(pci_root, 0xD0, 0xD0050300); - pci_read_config_dword(pci_root, 0xD4, &clock); - pci_dev_put(pci_root); + prop = dev_priv->force_audio_property; + if (prop == NULL) { + prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, + "audio", + ARRAY_SIZE(force_audio_names)); + if (prop == NULL) + return; - switch (clock & 0x07) { - case 0: - dev_priv->core_freq = 100; - break; - case 1: - dev_priv->core_freq = 133; - break; - case 2: - dev_priv->core_freq = 150; - break; - case 3: - dev_priv->core_freq = 178; - break; - case 4: - dev_priv->core_freq = 200; - break; - case 5: - case 6: - case 7: - dev_priv->core_freq = 266; - default: - dev_priv->core_freq = 0; + for (i = 0; i < ARRAY_SIZE(force_audio_names); i++) + drm_property_add_enum(prop, i, i-1, force_audio_names[i]); + + dev_priv->force_audio_property = prop; } + drm_object_attach_property(&connector->base, prop, 0); } + +static const char *broadcast_rgb_names[] = { + "Full", + "Limited 16:235", +}; + +void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_property *prop; + int i; + + prop = dev_priv->broadcast_rgb_property; + if (prop == NULL) { + prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, + "Broadcast RGB", + ARRAY_SIZE(broadcast_rgb_names)); + if (prop == NULL) + return; + + for (i = 0; i < ARRAY_SIZE(broadcast_rgb_names); i++) + drm_property_add_enum(prop, i, i, broadcast_rgb_names[i]); + + dev_priv->broadcast_rgb_property = prop; + } + + drm_object_attach_property(&connector->base, prop, 0); +} + +/* Cedarview */ +static const struct psb_offset cdv_regmap[2] = { + { + .fp0 = FPA0, + .fp1 = FPA1, + .cntr = DSPACNTR, + .conf = PIPEACONF, + .src = PIPEASRC, + .dpll = DPLL_A, + .dpll_md = DPLL_A_MD, + .htotal = HTOTAL_A, + .hblank = HBLANK_A, + .hsync = HSYNC_A, + .vtotal = VTOTAL_A, + .vblank = VBLANK_A, + .vsync = VSYNC_A, + .stride = DSPASTRIDE, + .size = DSPASIZE, + .pos = DSPAPOS, + .base = DSPABASE, + .surf = DSPASURF, + .addr = DSPABASE, + .status = PIPEASTAT, + .linoff = DSPALINOFF, + .tileoff = DSPATILEOFF, + .palette = PALETTE_A, + }, + { + .fp0 = FPB0, + .fp1 = FPB1, + .cntr = DSPBCNTR, + .conf = PIPEBCONF, + .src = PIPEBSRC, + .dpll = DPLL_B, + .dpll_md = DPLL_B_MD, + .htotal = HTOTAL_B, + .hblank = HBLANK_B, + .hsync = HSYNC_B, + .vtotal = VTOTAL_B, + .vblank = VBLANK_B, + .vsync = VSYNC_B, + .stride = DSPBSTRIDE, + .size = DSPBSIZE, + .pos = DSPBPOS, + .base = DSPBBASE, + .surf = DSPBSURF, + .addr = DSPBBASE, + .status = PIPEBSTAT, + .linoff = DSPBLINOFF, + .tileoff = DSPBTILEOFF, + .palette = PALETTE_B, + } +}; + static int cdv_chip_setup(struct drm_device *dev) { - cdv_get_core_freq(dev); - gma_intel_opregion_init(dev); + struct drm_psb_private *dev_priv = dev->dev_private; + INIT_WORK(&dev_priv->hotplug_work, cdv_hotplug_work_func); + + if (pci_enable_msi(dev->pdev)) + dev_warn(dev->dev, "Enabling MSI failed!\n"); + dev_priv->regmap = cdv_regmap; + gma_get_core_freq(dev); + psb_intel_opregion_init(dev); psb_intel_init_bios(dev); + cdv_hotplug_enable(dev, false); return 0; } @@ -331,13 +596,21 @@ const struct psb_ops cdv_chip_ops = { .accel_2d = 0, .pipes = 2, .crtcs = 2, + .hdmi_mask = (1 << 0) | (1 << 1), + .lvds_mask = (1 << 1), + .sdvo_mask = (1 << 0), + .cursor_needs_phys = 0, .sgx_offset = MRST_SGX_OFFSET, .chip_setup = cdv_chip_setup, + .errata = cdv_errata, .crtc_helper = &cdv_intel_helper_funcs, .crtc_funcs = &cdv_intel_crtc_funcs, + .clock_funcs = &cdv_clock_funcs, .output_init = cdv_output_init, + .hotplug = cdv_hotplug_event, + .hotplug_enable = cdv_hotplug_enable, #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE .backlight_init = cdv_backlight_init, @@ -348,4 +621,6 @@ const struct psb_ops cdv_chip_ops = { .restore_regs = cdv_restore_display_registers, .power_down = cdv_power_down, .power_up = cdv_power_up, + .update_wm = cdv_update_wm, + .disable_sr = cdv_disable_sr, }; diff --git a/drivers/gpu/drm/gma500/cdv_device.h b/drivers/gpu/drm/gma500/cdv_device.h index 2a88b7beb55..705c11d47d4 100644 --- a/drivers/gpu/drm/gma500/cdv_device.h +++ b/drivers/gpu/drm/gma500/cdv_device.h @@ -17,6 +17,7 @@ extern const struct drm_crtc_helper_funcs cdv_intel_helper_funcs; extern const struct drm_crtc_funcs cdv_intel_crtc_funcs; +extern const struct gma_clock_funcs cdv_clock_funcs; extern void cdv_intel_crt_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev); extern void cdv_intel_lvds_init(struct drm_device *dev, @@ -25,12 +26,5 @@ extern void cdv_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device * int reg); 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) -{ - /* Wait for 20ms, i.e. one cycle at 50hz. */ - /* FIXME: msleep ?? */ - mdelay(20); -} - - +extern void cdv_update_wm(struct drm_device *dev, struct drm_crtc *crtc); +extern void cdv_disable_sr(struct drm_device *dev); diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c index c100f3e9c92..c18268cd516 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> @@ -66,8 +67,6 @@ static void cdv_intel_crt_dpms(struct drm_encoder *encoder, int mode) static int cdv_intel_crt_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct drm_psb_private *dev_priv = connector->dev->dev_private; - int max_clock = 0; if (mode->flags & DRM_MODE_FLAG_DBLSCAN) return MODE_NO_DBLESCAN; @@ -76,28 +75,12 @@ static int cdv_intel_crt_mode_valid(struct drm_connector *connector, return MODE_CLOCK_LOW; /* The max clock for CDV is 355 instead of 400 */ - max_clock = 355000; - if (mode->clock > max_clock) + if (mode->clock > 355000) return MODE_CLOCK_HIGH; - if (mode->hdisplay > 1680 || mode->vdisplay > 1050) - return MODE_PANEL; - - /* We assume worst case scenario of 32 bpp here, since we don't know */ - if ((ALIGN(mode->hdisplay * 4, 64) * mode->vdisplay) > - dev_priv->vram_stolen_size) - return MODE_MEM; - return MODE_OK; } -static bool cdv_intel_crt_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - static void cdv_intel_crt_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -105,13 +88,12 @@ static void cdv_intel_crt_mode_set(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct drm_crtc *crtc = encoder->crtc; - struct psb_intel_crtc *psb_intel_crtc = - to_psb_intel_crtc(crtc); + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); int dpll_md_reg; u32 adpa, dpll_md; u32 adpa_reg; - if (psb_intel_crtc->pipe == 0) + if (gma_crtc->pipe == 0) dpll_md_reg = DPLL_A_MD; else dpll_md_reg = DPLL_B_MD; @@ -134,7 +116,7 @@ static void cdv_intel_crt_mode_set(struct drm_encoder *encoder, if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) adpa |= ADPA_VSYNC_ACTIVE_HIGH; - if (psb_intel_crtc->pipe == 0) + if (gma_crtc->pipe == 0) adpa |= ADPA_PIPE_A_SELECT; else adpa |= ADPA_PIPE_B_SELECT; @@ -155,13 +137,7 @@ static bool cdv_intel_crt_detect_hotplug(struct drm_connector *connector, struct drm_device *dev = connector->dev; u32 hotplug_en; int i, tries = 0, ret = false; - u32 adpa_orig; - - /* disable the DAC when doing the hotplug detection */ - - adpa_orig = REG_READ(ADPA); - - REG_WRITE(ADPA, adpa_orig & ~(ADPA_DAC_ENABLE)); + u32 orig; /* * On a CDV thep, CRT detect sequence need to be done twice @@ -169,7 +145,7 @@ static bool cdv_intel_crt_detect_hotplug(struct drm_connector *connector, */ tries = 2; - hotplug_en = REG_READ(PORT_HOTPLUG_EN); + orig = hotplug_en = REG_READ(PORT_HOTPLUG_EN); hotplug_en &= ~(CRT_HOTPLUG_DETECT_MASK); hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; @@ -194,8 +170,11 @@ static bool cdv_intel_crt_detect_hotplug(struct drm_connector *connector, CRT_HOTPLUG_MONITOR_NONE) ret = true; - /* Restore the saved ADPA */ - REG_WRITE(ADPA, adpa_orig); + /* clear the interrupt we just generated, if any */ + REG_WRITE(PORT_HOTPLUG_STAT, CRT_HOTPLUG_INT_STATUS); + + /* and put the bits back */ + REG_WRITE(PORT_HOTPLUG_EN, orig); return ret; } @@ -210,10 +189,9 @@ static enum drm_connector_status cdv_intel_crt_detect( static void cdv_intel_crt_destroy(struct drm_connector *connector) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); - psb_intel_i2c_destroy(psb_intel_encoder->ddc_bus); + psb_intel_i2c_destroy(gma_encoder->ddc_bus); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); kfree(connector); @@ -221,9 +199,9 @@ static void cdv_intel_crt_destroy(struct drm_connector *connector) static int cdv_intel_crt_get_modes(struct drm_connector *connector) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); - return psb_intel_ddc_get_modes(connector, &psb_intel_encoder->ddc_bus->adapter); + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + return psb_intel_ddc_get_modes(connector, + &gma_encoder->ddc_bus->adapter); } static int cdv_intel_crt_set_property(struct drm_connector *connector, @@ -239,9 +217,9 @@ static int cdv_intel_crt_set_property(struct drm_connector *connector, static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = { .dpms = cdv_intel_crt_dpms, - .mode_fixup = cdv_intel_crt_mode_fixup, - .prepare = psb_intel_encoder_prepare, - .commit = psb_intel_encoder_commit, + .mode_fixup = gma_encoder_mode_fixup, + .prepare = gma_encoder_prepare, + .commit = gma_encoder_commit, .mode_set = cdv_intel_crt_mode_set, }; @@ -257,7 +235,7 @@ static const struct drm_connector_helper_funcs cdv_intel_crt_connector_helper_funcs = { .mode_valid = cdv_intel_crt_mode_valid, .get_modes = cdv_intel_crt_get_modes, - .best_encoder = psb_intel_best_encoder, + .best_encoder = gma_best_encoder, }; static void cdv_intel_crt_enc_destroy(struct drm_encoder *encoder) @@ -273,31 +251,31 @@ void cdv_intel_crt_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev) { - struct psb_intel_connector *psb_intel_connector; - struct psb_intel_encoder *psb_intel_encoder; + struct gma_connector *gma_connector; + struct gma_encoder *gma_encoder; struct drm_connector *connector; struct drm_encoder *encoder; u32 i2c_reg; - psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL); - if (!psb_intel_encoder) + gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); + if (!gma_encoder) return; - psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL); - if (!psb_intel_connector) + gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); + if (!gma_connector) goto failed_connector; - connector = &psb_intel_connector->base; + connector = &gma_connector->base; + connector->polled = DRM_CONNECTOR_POLL_HPD; drm_connector_init(dev, connector, &cdv_intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); - encoder = &psb_intel_encoder->base; + encoder = &gma_encoder->base; drm_encoder_init(dev, encoder, &cdv_intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC); - psb_intel_connector_attach_encoder(psb_intel_connector, - psb_intel_encoder); + gma_connector_attach_encoder(gma_connector, gma_encoder); /* Set up the DDC bus. */ i2c_reg = GPIOA; @@ -306,15 +284,15 @@ void cdv_intel_crt_init(struct drm_device *dev, if (dev_priv->crt_ddc_bus != 0) i2c_reg = dev_priv->crt_ddc_bus; }*/ - psb_intel_encoder->ddc_bus = psb_intel_i2c_create(dev, + gma_encoder->ddc_bus = psb_intel_i2c_create(dev, i2c_reg, "CRTDDC_A"); - if (!psb_intel_encoder->ddc_bus) { + if (!gma_encoder->ddc_bus) { dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " "failed.\n"); goto failed_ddc; } - psb_intel_encoder->type = INTEL_OUTPUT_ANALOG; + gma_encoder->type = INTEL_OUTPUT_ANALOG; /* psb_intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT); psb_intel_output->crtc_mask = (1 << 0) | (1 << 1); @@ -330,10 +308,10 @@ void cdv_intel_crt_init(struct drm_device *dev, return; failed_ddc: - drm_encoder_cleanup(&psb_intel_encoder->base); - drm_connector_cleanup(&psb_intel_connector->base); - kfree(psb_intel_connector); + drm_encoder_cleanup(&gma_encoder->base); + drm_connector_cleanup(&gma_connector->base); + kfree(gma_connector); failed_connector: - kfree(psb_intel_encoder); + kfree(gma_encoder); return; } diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 18d11525095..66727328832 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -19,53 +19,30 @@ */ #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <drm/drmP.h> #include "framebuffer.h" #include "psb_drv.h" #include "psb_intel_drv.h" #include "psb_intel_reg.h" -#include "psb_intel_display.h" +#include "gma_display.h" #include "power.h" #include "cdv_device.h" +static bool cdv_intel_find_dp_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, + int refclk, struct gma_clock_t *best_clock); -struct cdv_intel_range_t { - int min, max; -}; - -struct cdv_intel_p2_t { - int dot_limit; - int p2_slow, p2_fast; -}; - -struct cdv_intel_clock_t { - /* given values */ - int n; - int m1, m2; - int p1, p2; - /* derived values */ - int dot; - int vco; - int m; - int p; -}; - -#define INTEL_P2_NUM 2 - -struct cdv_intel_limit_t { - struct cdv_intel_range_t dot, vco, n, m, m1, m2, p, p1; - struct cdv_intel_p2_t p2; -}; #define CDV_LIMIT_SINGLE_LVDS_96 0 #define CDV_LIMIT_SINGLE_LVDS_100 1 #define CDV_LIMIT_DAC_HDMI_27 2 #define CDV_LIMIT_DAC_HDMI_96 3 +#define CDV_LIMIT_DP_27 4 +#define CDV_LIMIT_DP_100 5 -static const struct cdv_intel_limit_t cdv_intel_limits[] = { - { /* CDV_SIGNLE_LVDS_96MHz */ +static const struct gma_limit_t cdv_intel_limits[] = { + { /* CDV_SINGLE_LVDS_96MHz */ .dot = {.min = 20000, .max = 115500}, .vco = {.min = 1800000, .max = 3600000}, .n = {.min = 2, .max = 6}, @@ -74,8 +51,8 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .m2 = {.min = 58, .max = 158}, .p = {.min = 28, .max = 140}, .p1 = {.min = 2, .max = 10}, - .p2 = {.dot_limit = 200000, - .p2_slow = 14, .p2_fast = 14}, + .p2 = {.dot_limit = 200000, .p2_slow = 14, .p2_fast = 14}, + .find_pll = gma_find_best_pll, }, { /* CDV_SINGLE_LVDS_100MHz */ .dot = {.min = 20000, .max = 115500}, @@ -90,6 +67,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { * is 80-224Mhz. Prefer single channel as much as possible. */ .p2 = {.dot_limit = 200000, .p2_slow = 14, .p2_fast = 14}, + .find_pll = gma_find_best_pll, }, { /* CDV_DAC_HDMI_27MHz */ .dot = {.min = 20000, .max = 400000}, @@ -101,6 +79,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .p = {.min = 5, .max = 90}, .p1 = {.min = 1, .max = 9}, .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, + .find_pll = gma_find_best_pll, }, { /* CDV_DAC_HDMI_96MHz */ .dot = {.min = 20000, .max = 400000}, @@ -112,7 +91,32 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .p = {.min = 5, .max = 100}, .p1 = {.min = 1, .max = 10}, .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, + .find_pll = gma_find_best_pll, }, + { /* CDV_DP_27MHz */ + .dot = {.min = 160000, .max = 272000}, + .vco = {.min = 1809000, .max = 3564000}, + .n = {.min = 1, .max = 1}, + .m = {.min = 67, .max = 132}, + .m1 = {.min = 0, .max = 0}, + .m2 = {.min = 65, .max = 130}, + .p = {.min = 5, .max = 90}, + .p1 = {.min = 1, .max = 9}, + .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 10}, + .find_pll = cdv_intel_find_dp_pll, + }, + { /* CDV_DP_100MHz */ + .dot = {.min = 160000, .max = 272000}, + .vco = {.min = 1800000, .max = 3600000}, + .n = {.min = 2, .max = 6}, + .m = {.min = 60, .max = 164}, + .m1 = {.min = 0, .max = 0}, + .m2 = {.min = 58, .max = 162}, + .p = {.min = 5, .max = 100}, + .p1 = {.min = 1, .max = 10}, + .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 10}, + .find_pll = cdv_intel_find_dp_pll, + } }; #define _wait_for(COND, MS, W) ({ \ @@ -132,7 +136,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { #define wait_for(COND, MS) _wait_for(COND, MS, 1) -static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) +int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) { int ret; @@ -159,7 +163,7 @@ static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) return 0; } -static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) +int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) { int ret; static bool dpio_debug = true; @@ -201,7 +205,7 @@ static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) /* Reset the DPIO configuration register. The BIOS does this at every * mode set. */ -static void cdv_sb_reset(struct drm_device *dev) +void cdv_sb_reset(struct drm_device *dev) { REG_WRITE(DPIO_CFG, 0); @@ -216,22 +220,22 @@ static void cdv_sb_reset(struct drm_device *dev) */ static int cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, - struct cdv_intel_clock_t *clock) + struct gma_clock_t *clock, bool is_lvds, u32 ddi_select) { - struct psb_intel_crtc *psb_crtc = - to_psb_intel_crtc(crtc); - int pipe = psb_crtc->pipe; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int pipe = gma_crtc->pipe; u32 m, n_vco, p; int ret = 0; int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; + int ref_sfr = (pipe == 0) ? SB_REF_DPLLA : SB_REF_DPLLB; u32 ref_value; + u32 lane_reg, lane_value; cdv_sb_reset(dev); - if ((REG_READ(dpll_reg) & DPLL_SYNCLOCK_ENABLE) == 0) { - DRM_ERROR("Attempting to set DPLL with refclk disabled\n"); - return -EBUSY; - } + REG_WRITE(dpll_reg, DPLL_SYNCLOCK_ENABLE | DPLL_VGA_MODE_DIS); + + udelay(100); /* Follow the BIOS and write the REF/SFR Register. Hardcoded value */ ref_value = 0x68A701; @@ -241,6 +245,35 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, /* We don't know what the other fields of these regs are, so * leave them in place. */ + /* + * The BIT 14:13 of 0x8010/0x8030 is used to select the ref clk + * for the pipe A/B. Display spec 1.06 has wrong definition. + * Correct definition is like below: + * + * refclka mean use clock from same PLL + * + * if DPLLA sets 01 and DPLLB sets 01, they use clock from their pll + * + * if DPLLA sets 01 and DPLLB sets 02, both use clk from DPLLA + * + */ + ret = cdv_sb_read(dev, ref_sfr, &ref_value); + if (ret) + return ret; + ref_value &= ~(REF_CLK_MASK); + + /* use DPLL_A for pipeB on CRT/HDMI */ + if (pipe == 1 && !is_lvds && !(ddi_select & DP_MASK)) { + DRM_DEBUG_KMS("use DPLLA for pipe B\n"); + ref_value |= REF_CLK_DPLLA; + } else { + DRM_DEBUG_KMS("use their DPLL for pipe A/B\n"); + ref_value |= REF_CLK_DPLL; + } + ret = cdv_sb_write(dev, ref_sfr, ref_value); + if (ret) + return ret; + ret = cdv_sb_read(dev, SB_M(pipe), &m); if (ret) return ret; @@ -307,65 +340,41 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, if (ret) return ret; - /* always Program the Lane Register for the Pipe A*/ - if (pipe == 0) { - /* Program the Lane0/1 for HDMI B */ - u32 lane_reg, lane_value; - - lane_reg = PSB_LANE0; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE; - cdv_sb_write(dev, lane_reg, lane_value); - - lane_reg = PSB_LANE1; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE; - cdv_sb_write(dev, lane_reg, lane_value); - - /* Program the Lane2/3 for HDMI C */ - lane_reg = PSB_LANE2; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE; - cdv_sb_write(dev, lane_reg, lane_value); - - lane_reg = PSB_LANE3; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE; - cdv_sb_write(dev, lane_reg, lane_value); - } - - return 0; -} - -/* - * 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) -{ - struct drm_device *dev = crtc->dev; - struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_connector *l_entry; - - list_for_each_entry(l_entry, &mode_config->connector_list, head) { - if (l_entry->encoder && l_entry->encoder->crtc == crtc) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(l_entry); - if (psb_intel_encoder->type == type) - return true; + if (ddi_select) { + if ((ddi_select & DDI_MASK) == DDI0_SELECT) { + lane_reg = PSB_LANE0; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + + lane_reg = PSB_LANE1; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + } else { + lane_reg = PSB_LANE2; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + + lane_reg = PSB_LANE3; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); } } - return false; + return 0; } -static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc, - int refclk) +static const struct gma_limit_t *cdv_intel_limit(struct drm_crtc *crtc, + int refclk) { - const struct cdv_intel_limit_t *limit; - if (cdv_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + const struct gma_limit_t *limit; + if (gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { /* * Now only single-channel LVDS is supported on CDV. If it is * incorrect, please add the dual-channel LVDS. @@ -374,6 +383,12 @@ static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc, limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_96]; else limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_100]; + } else if (gma_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) || + gma_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { + if (refclk == 27000) + limit = &cdv_intel_limits[CDV_LIMIT_DP_27]; + else + limit = &cdv_intel_limits[CDV_LIMIT_DP_100]; } else { if (refclk == 27000) limit = &cdv_intel_limits[CDV_LIMIT_DAC_HDMI_27]; @@ -384,8 +399,7 @@ static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc, } /* m1 is reserved as 0 in CDV, n is a ring counter */ -static void cdv_intel_clock(struct drm_device *dev, - int refclk, struct cdv_intel_clock_t *clock) +static void cdv_intel_clock(int refclk, struct gma_clock_t *clock) { clock->m = clock->m2 + 2; clock->p = clock->p1 * clock->p2; @@ -393,317 +407,158 @@ static void cdv_intel_clock(struct drm_device *dev, clock->dot = clock->vco / clock->p; } - -#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } -static bool cdv_intel_PLL_is_valid(struct drm_crtc *crtc, - const struct cdv_intel_limit_t *limit, - struct cdv_intel_clock_t *clock) +static bool cdv_intel_find_dp_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, + int refclk, + struct gma_clock_t *best_clock) { - if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) - INTELPllInvalid("p1 out of range\n"); - if (clock->p < limit->p.min || limit->p.max < clock->p) - INTELPllInvalid("p out of range\n"); - /* unnecessary to check the range of m(m1/M2)/n again */ - if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) - INTELPllInvalid("vco out of range\n"); - /* XXX: We may need to be checking "Dot clock" - * depending on the multiplier, connector, etc., - * rather than just a single range. - */ - if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) - INTELPllInvalid("dot out of range\n"); + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + struct gma_clock_t clock; - return true; -} - -static bool cdv_intel_find_best_PLL(struct drm_crtc *crtc, int target, - int refclk, - struct cdv_intel_clock_t *best_clock) -{ - struct drm_device *dev = crtc->dev; - struct cdv_intel_clock_t clock; - const struct cdv_intel_limit_t *limit = cdv_intel_limit(crtc, refclk); - int err = target; - - - if (cdv_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && - (REG_READ(LVDS) & LVDS_PORT_EN) != 0) { - /* - * For LVDS, if the panel is on, just rely on its current - * settings for dual-channel. We haven't figured out how to - * reliably set up different single/dual channel state, if we - * even can. - */ - if ((REG_READ(LVDS) & LVDS_CLKB_POWER_MASK) == - LVDS_CLKB_POWER_UP) - clock.p2 = limit->p2.p2_fast; - else - clock.p2 = limit->p2.p2_slow; - } else { - if (target < limit->p2.dot_limit) - clock.p2 = limit->p2.p2_slow; - else - clock.p2 = limit->p2.p2_fast; - } + switch (refclk) { + case 27000: + if (target < 200000) { + clock.p1 = 2; + clock.p2 = 10; + clock.n = 1; + clock.m1 = 0; + clock.m2 = 118; + } else { + clock.p1 = 1; + clock.p2 = 10; + clock.n = 1; + clock.m1 = 0; + clock.m2 = 98; + } + break; - memset(best_clock, 0, sizeof(*best_clock)); - clock.m1 = 0; - /* m1 is reserved as 0 in CDV, n is a ring counter. - So skip the m1 loop */ - for (clock.n = limit->n.min; clock.n <= limit->n.max; clock.n++) { - for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; - clock.m2++) { - for (clock.p1 = limit->p1.min; - clock.p1 <= limit->p1.max; - clock.p1++) { - int this_err; - - cdv_intel_clock(dev, refclk, &clock); - - if (!cdv_intel_PLL_is_valid(crtc, - limit, &clock)) - continue; - - this_err = abs(clock.dot - target); - if (this_err < err) { - *best_clock = clock; - err = this_err; - } - } + case 100000: + if (target < 200000) { + clock.p1 = 2; + clock.p2 = 10; + clock.n = 5; + clock.m1 = 0; + clock.m2 = 160; + } else { + clock.p1 = 1; + clock.p2 = 10; + clock.n = 5; + clock.m1 = 0; + clock.m2 = 133; } + break; + + default: + return false; } - return err != target; + gma_crtc->clock_funcs->clock(refclk, &clock); + memcpy(best_clock, &clock, sizeof(struct gma_clock_t)); + return true; } -int cdv_intel_pipe_set_base(struct drm_crtc *crtc, - int x, int y, struct drm_framebuffer *old_fb) +#define FIFO_PIPEA (1 << 0) +#define FIFO_PIPEB (1 << 1) + +static bool cdv_intel_pipe_enabled(struct drm_device *dev, int pipe) { - struct drm_device *dev = crtc->dev; - 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 dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); - int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - u32 dspcntr; - int ret = 0; + struct drm_crtc *crtc; + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = NULL; - if (!gma_power_begin(dev, true)) - return 0; + crtc = dev_priv->pipe_to_crtc_mapping[pipe]; + gma_crtc = to_gma_crtc(crtc); - /* no fb bound */ - if (!crtc->fb) { - dev_err(dev->dev, "No FB bound\n"); - goto psb_intel_pipe_cleaner; - } + if (crtc->primary->fb == NULL || !gma_crtc->active) + return false; + return true; +} +void cdv_disable_sr(struct drm_device *dev) +{ + if (REG_READ(FW_BLC_SELF) & FW_BLC_SELF_EN) { - /* We are displaying this buffer, make sure it is actually loaded - into the GTT */ - ret = psb_gtt_pin(psbfb->gtt); - if (ret < 0) - goto psb_intel_pipe_set_base_exit; - start = psbfb->gtt->offset; - offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); + /* Disable self-refresh before adjust WM */ + REG_WRITE(FW_BLC_SELF, (REG_READ(FW_BLC_SELF) & ~FW_BLC_SELF_EN)); + REG_READ(FW_BLC_SELF); - REG_WRITE(dspstride, crtc->fb->pitches[0]); + gma_wait_for_vblank(dev); - dspcntr = REG_READ(dspcntr_reg); - dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + /* Cedarview workaround to write ovelay plane, which force to leave + * MAX_FIFO state. + */ + REG_WRITE(OV_OVADD, 0/*dev_priv->ovl_offset*/); + REG_READ(OV_OVADD); - 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; - default: - dev_err(dev->dev, "Unknown color depth\n"); - ret = -EINVAL; - goto psb_intel_pipe_set_base_exit; + gma_wait_for_vblank(dev); } - REG_WRITE(dspcntr_reg, dspcntr); - dev_dbg(dev->dev, - "Writing base %08lX %08lX %d %d\n", start, offset, x, y); - - REG_WRITE(dspbase, offset); - REG_READ(dspbase); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); - -psb_intel_pipe_cleaner: - /* If there was a previous display we can now unpin it */ - if (old_fb) - psb_gtt_unpin(to_psb_fb(old_fb)->gtt); - -psb_intel_pipe_set_base_exit: - gma_power_end(dev); - return ret; } -/** - * 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 cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) +void cdv_update_wm(struct drm_device *dev, struct drm_crtc *crtc) { - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - 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. - */ - 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) { - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - } - - /* Jim Bish - switch plan and pipe per scott */ - /* 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)); + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + + /* Is only one pipe enabled? */ + if (cdv_intel_pipe_enabled(dev, 0) ^ cdv_intel_pipe_enabled(dev, 1)) { + u32 fw; + + fw = REG_READ(DSPFW1); + fw &= ~DSP_FIFO_SR_WM_MASK; + fw |= (0x7e << DSP_FIFO_SR_WM_SHIFT); + fw &= ~CURSOR_B_FIFO_WM_MASK; + fw |= (0x4 << CURSOR_B_FIFO_WM_SHIFT); + REG_WRITE(DSPFW1, fw); + + fw = REG_READ(DSPFW2); + fw &= ~CURSOR_A_FIFO_WM_MASK; + fw |= (0x6 << CURSOR_A_FIFO_WM_SHIFT); + fw &= ~DSP_PLANE_C_FIFO_WM_MASK; + fw |= (0x8 << DSP_PLANE_C_FIFO_WM_SHIFT); + REG_WRITE(DSPFW2, fw); + + REG_WRITE(DSPFW3, 0x36000000); + + /* ignore FW4 */ + + /* Is pipe b lvds ? */ + if (gma_crtc->pipe == 1 && + gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) { + REG_WRITE(DSPFW5, 0x00040330); + } else { + fw = (3 << DSP_PLANE_B_FIFO_WM1_SHIFT) | + (4 << DSP_PLANE_A_FIFO_WM1_SHIFT) | + (3 << CURSOR_B_FIFO_WM1_SHIFT) | + (4 << CURSOR_FIFO_SR_WM1_SHIFT); + REG_WRITE(DSPFW5, fw); } - udelay(150); - - /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) == 0) - REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); - - 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 */ - - /* Disable the VGA plane that we never use */ - REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); - - /* Jim Bish - changed pipe/plane here as well. */ - - /* Wait for vblank for the disable to take effect */ - cdv_intel_wait_for_vblank(dev); + REG_WRITE(DSPFW6, 0x10); - /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - REG_READ(pipeconf_reg); - } + gma_wait_for_vblank(dev); - /* Wait for vblank for the disable to take effect. */ - cdv_intel_wait_for_vblank(dev); + /* enable self-refresh for single pipe active */ + REG_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN); + REG_READ(FW_BLC_SELF); + gma_wait_for_vblank(dev); - udelay(150); + } else { - /* 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); - } + /* HW team suggested values... */ + REG_WRITE(DSPFW1, 0x3f880808); + REG_WRITE(DSPFW2, 0x0b020202); + REG_WRITE(DSPFW3, 0x24000000); + REG_WRITE(DSPFW4, 0x08030202); + REG_WRITE(DSPFW5, 0x01010101); + REG_WRITE(DSPFW6, 0x1d0); - temp = REG_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) != 0) { - REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - } + gma_wait_for_vblank(dev); - /* Wait for the clocks to turn off. */ - udelay(150); - break; + dev_priv->ops->disable_sr(dev); } - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; - /*Set FIFO Watermarks*/ - REG_WRITE(DSPARB, 0x3F3E); -} - -static void cdv_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 cdv_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); -} - -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) -{ - return true; -} - - /** * Return the pipe currently connected to the panel fitter, * or -1 if the panel fitter is not present or not in use @@ -727,48 +582,35 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dpll_md_reg = (psb_intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - 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; + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; int refclk; - struct cdv_intel_clock_t clock; + struct gma_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; + bool is_hdmi = false, is_dp = false; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; + const struct gma_limit_t *limit; + u32 ddi_select = 0; + bool is_edp = false; list_for_each_entry(connector, &mode_config->connector_list, head) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); + struct gma_encoder *gma_encoder = + gma_attached_encoder(connector); if (!connector->encoder || connector->encoder->crtc != crtc) continue; - switch (psb_intel_encoder->type) { + ddi_select = gma_encoder->ddi_select; + switch (gma_encoder->type) { 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; @@ -778,22 +620,53 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_HDMI: is_hdmi = true; break; + case INTEL_OUTPUT_DISPLAYPORT: + is_dp = true; + break; + case INTEL_OUTPUT_EDP: + is_edp = true; + break; + default: + DRM_ERROR("invalid output type.\n"); + return 0; } } - refclk = 96000; - - /* Hack selection about ref clk for CRT */ - /* Select 27MHz as the reference clk for HDMI */ - if (is_crt || is_hdmi) + if (dev_priv->dplla_96mhz) + /* low-end sku, 96/100 mhz */ + refclk = 96000; + else + /* high-end sku, 27/100 mhz */ refclk = 27000; + if (is_dp || is_edp) { + /* + * Based on the spec the low-end SKU has only CRT/LVDS. So it is + * unnecessary to consider it for DP/eDP. + * On the high-end SKU, it will use the 27/100M reference clk + * for DP/eDP. When using SSC clock, the ref clk is 100MHz.Otherwise + * it will be 27MHz. From the VBIOS code it seems that the pipe A choose + * 27MHz for DP/eDP while the Pipe B chooses the 100MHz. + */ + if (pipe == 0) + refclk = 27000; + else + refclk = 100000; + } + + if (is_lvds && dev_priv->lvds_use_ssc) { + refclk = dev_priv->lvds_ssc_freq * 1000; + DRM_DEBUG_KMS("Use SSC reference clock %d Mhz\n", dev_priv->lvds_ssc_freq); + } drm_mode_debug_printmodeline(adjusted_mode); + + limit = gma_crtc->clock_funcs->limit(crtc, refclk); - ok = cdv_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, + ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock); if (!ok) { - dev_err(dev->dev, "Couldn't find PLL settings for mode!\n"); + DRM_ERROR("Couldn't find PLL settings for mode! target: %d, actual: %d", + adjusted_mode->clock, clock.dot); return 0; } @@ -803,19 +676,52 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ dpll |= 3; } - dpll |= PLL_REF_INPUT_DREFCLK; +/* dpll |= PLL_REF_INPUT_DREFCLK; */ + + if (is_dp || is_edp) { + cdv_intel_dp_set_m_n(crtc, mode, adjusted_mode); + } else { + REG_WRITE(PIPE_GMCH_DATA_M(pipe), 0); + REG_WRITE(PIPE_GMCH_DATA_N(pipe), 0); + REG_WRITE(PIPE_DP_LINK_M(pipe), 0); + REG_WRITE(PIPE_DP_LINK_N(pipe), 0); + } dpll |= DPLL_SYNCLOCK_ENABLE; - dpll |= DPLL_VGA_MODE_DIS; - if (is_lvds) +/* if (is_lvds) dpll |= DPLLB_MODE_LVDS; else - dpll |= DPLLB_MODE_DAC_SERIAL; + dpll |= DPLLB_MODE_DAC_SERIAL; */ /* dpll |= (2 << 11); */ /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); + pipeconf = REG_READ(map->conf); + pipeconf &= ~(PIPE_BPC_MASK); + if (is_edp) { + switch (dev_priv->edp.bpp) { + case 24: + pipeconf |= PIPE_8BPC; + break; + case 18: + pipeconf |= PIPE_6BPC; + break; + case 30: + pipeconf |= PIPE_10BPC; + break; + default: + pipeconf |= PIPE_8BPC; + break; + } + } else if (is_lvds) { + /* the BPC will be 6 if it is 18-bit LVDS panel */ + if ((REG_READ(LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP) + pipeconf |= PIPE_8BPC; + else + pipeconf |= PIPE_6BPC; + } else + pipeconf |= PIPE_8BPC; + /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -827,10 +733,10 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, dspcntr |= DISPLAY_PLANE_ENABLE; pipeconf |= PIPEACONF_ENABLE; - REG_WRITE(dpll_reg, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE); + REG_READ(map->dpll); - cdv_dpll_set_clock_cdv(dev, crtc, &clock); + cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds, ddi_select); udelay(150); @@ -872,48 +778,48 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); drm_mode_debug_printmodeline(mode); - REG_WRITE(dpll_reg, - (REG_READ(dpll_reg) & ~DPLL_LOCK) | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->dpll, + (REG_READ(map->dpll) & ~DPLL_LOCK) | DPLL_VCO_ENABLE); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); /* 42 usec w/o calibration, 110 with. rounded up. */ - if (!(REG_READ(dpll_reg) & DPLL_LOCK)) { + if (!(REG_READ(map->dpll) & DPLL_LOCK)) { dev_err(dev->dev, "Failed to get DPLL lock\n"); return -EBUSY; } { int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; - REG_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); + REG_WRITE(map->dpll_md, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); } - REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); /* pipesrc and dspsize control the size that is scaled from, * which should always be the user's requested size. */ - REG_WRITE(dspsize_reg, + REG_WRITE(map->size, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); - REG_WRITE(dsppos_reg, 0); - REG_WRITE(pipesrc_reg, + REG_WRITE(map->pos, 0); + REG_WRITE(map->src, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, pipeconf); + REG_READ(map->conf); - cdv_intel_wait_for_vblank(dev); + gma_wait_for_vblank(dev); - REG_WRITE(dspcntr_reg, dspcntr); + REG_WRITE(map->cntr, dspcntr); /* Flush the plane changes */ { @@ -922,393 +828,16 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, crtc_funcs->mode_set_base(crtc, x, y, old_fb); } - cdv_intel_wait_for_vblank(dev); - - return 0; -} - -/** Loads the palette/gamma unit for the CRTC with the prepared values */ -void cdv_intel_crtc_load_lut(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int palreg = PALETTE_A; - int i; - - /* The clocks have to be on to load the palette. */ - if (!crtc->enabled) - return; - - switch (psb_intel_crtc->pipe) { - case 0: - break; - case 1: - palreg = PALETTE_B; - break; - case 2: - palreg = PALETTE_C; - break; - default: - dev_err(dev->dev, "Illegal Pipe Number.\n"); - return; - } - - if (gma_power_begin(dev, false)) { - for (i = 0; i < 256; i++) { - REG_WRITE(palreg + 4 * i, - ((psb_intel_crtc->lut_r[i] + - psb_intel_crtc->lut_adj[i]) << 16) | - ((psb_intel_crtc->lut_g[i] + - psb_intel_crtc->lut_adj[i]) << 8) | - (psb_intel_crtc->lut_b[i] + - psb_intel_crtc->lut_adj[i])); - } - gma_power_end(dev); - } else { - for (i = 0; i < 256; i++) { - dev_priv->save_palette_a[i] = - ((psb_intel_crtc->lut_r[i] + - psb_intel_crtc->lut_adj[i]) << 16) | - ((psb_intel_crtc->lut_g[i] + - psb_intel_crtc->lut_adj[i]) << 8) | - (psb_intel_crtc->lut_b[i] + - psb_intel_crtc->lut_adj[i]); - } - - } -} - -/** - * Save HW states of giving crtc - */ -static void cdv_intel_crtc_save(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - int pipeA = (psb_intel_crtc->pipe == 0); - uint32_t paletteReg; - int i; - - if (!crtc_state) { - dev_dbg(dev->dev, "No CRTC state found\n"); - return; - } - - crtc_state->saveDSPCNTR = REG_READ(pipeA ? DSPACNTR : DSPBCNTR); - crtc_state->savePIPECONF = REG_READ(pipeA ? PIPEACONF : PIPEBCONF); - crtc_state->savePIPESRC = REG_READ(pipeA ? PIPEASRC : PIPEBSRC); - crtc_state->saveFP0 = REG_READ(pipeA ? FPA0 : FPB0); - crtc_state->saveFP1 = REG_READ(pipeA ? FPA1 : FPB1); - crtc_state->saveDPLL = REG_READ(pipeA ? DPLL_A : DPLL_B); - crtc_state->saveHTOTAL = REG_READ(pipeA ? HTOTAL_A : HTOTAL_B); - crtc_state->saveHBLANK = REG_READ(pipeA ? HBLANK_A : HBLANK_B); - crtc_state->saveHSYNC = REG_READ(pipeA ? HSYNC_A : HSYNC_B); - crtc_state->saveVTOTAL = REG_READ(pipeA ? VTOTAL_A : VTOTAL_B); - crtc_state->saveVBLANK = REG_READ(pipeA ? VBLANK_A : VBLANK_B); - crtc_state->saveVSYNC = REG_READ(pipeA ? VSYNC_A : VSYNC_B); - crtc_state->saveDSPSTRIDE = REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE); - - /*NOTE: DSPSIZE DSPPOS only for psb*/ - crtc_state->saveDSPSIZE = REG_READ(pipeA ? DSPASIZE : DSPBSIZE); - crtc_state->saveDSPPOS = REG_READ(pipeA ? DSPAPOS : DSPBPOS); - - crtc_state->saveDSPBASE = REG_READ(pipeA ? DSPABASE : DSPBBASE); - - DRM_DEBUG("(%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", - crtc_state->saveDSPCNTR, - crtc_state->savePIPECONF, - crtc_state->savePIPESRC, - crtc_state->saveFP0, - crtc_state->saveFP1, - crtc_state->saveDPLL, - crtc_state->saveHTOTAL, - crtc_state->saveHBLANK, - crtc_state->saveHSYNC, - crtc_state->saveVTOTAL, - crtc_state->saveVBLANK, - crtc_state->saveVSYNC, - crtc_state->saveDSPSTRIDE, - crtc_state->saveDSPSIZE, - crtc_state->saveDSPPOS, - crtc_state->saveDSPBASE - ); - - paletteReg = pipeA ? PALETTE_A : PALETTE_B; - for (i = 0; i < 256; ++i) - crtc_state->savePalette[i] = REG_READ(paletteReg + (i << 2)); -} - -/** - * Restore HW states of giving crtc - */ -static void cdv_intel_crtc_restore(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_psb_private * dev_priv = - (struct drm_psb_private *)dev->dev_private; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - /* struct drm_crtc_helper_funcs * crtc_funcs = crtc->helper_private; */ - int pipeA = (psb_intel_crtc->pipe == 0); - uint32_t paletteReg; - int i; - - if (!crtc_state) { - dev_dbg(dev->dev, "No crtc state\n"); - return; - } - - DRM_DEBUG( - "current:(%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", - REG_READ(pipeA ? DSPACNTR : DSPBCNTR), - REG_READ(pipeA ? PIPEACONF : PIPEBCONF), - REG_READ(pipeA ? PIPEASRC : PIPEBSRC), - REG_READ(pipeA ? FPA0 : FPB0), - REG_READ(pipeA ? FPA1 : FPB1), - REG_READ(pipeA ? DPLL_A : DPLL_B), - REG_READ(pipeA ? HTOTAL_A : HTOTAL_B), - REG_READ(pipeA ? HBLANK_A : HBLANK_B), - REG_READ(pipeA ? HSYNC_A : HSYNC_B), - REG_READ(pipeA ? VTOTAL_A : VTOTAL_B), - REG_READ(pipeA ? VBLANK_A : VBLANK_B), - REG_READ(pipeA ? VSYNC_A : VSYNC_B), - REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE), - REG_READ(pipeA ? DSPASIZE : DSPBSIZE), - REG_READ(pipeA ? DSPAPOS : DSPBPOS), - REG_READ(pipeA ? DSPABASE : DSPBBASE) - ); - - DRM_DEBUG( - "saved: (%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x)\n", - crtc_state->saveDSPCNTR, - crtc_state->savePIPECONF, - crtc_state->savePIPESRC, - crtc_state->saveFP0, - crtc_state->saveFP1, - crtc_state->saveDPLL, - crtc_state->saveHTOTAL, - crtc_state->saveHBLANK, - crtc_state->saveHSYNC, - crtc_state->saveVTOTAL, - crtc_state->saveVBLANK, - crtc_state->saveVSYNC, - crtc_state->saveDSPSTRIDE, - crtc_state->saveDSPSIZE, - crtc_state->saveDSPPOS, - crtc_state->saveDSPBASE - ); - - - if (crtc_state->saveDPLL & DPLL_VCO_ENABLE) { - REG_WRITE(pipeA ? DPLL_A : DPLL_B, - crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); - REG_READ(pipeA ? DPLL_A : DPLL_B); - DRM_DEBUG("write dpll: %x\n", - REG_READ(pipeA ? DPLL_A : DPLL_B)); - udelay(150); - } - - REG_WRITE(pipeA ? FPA0 : FPB0, crtc_state->saveFP0); - REG_READ(pipeA ? FPA0 : FPB0); - - REG_WRITE(pipeA ? FPA1 : FPB1, crtc_state->saveFP1); - REG_READ(pipeA ? FPA1 : FPB1); - - REG_WRITE(pipeA ? DPLL_A : DPLL_B, crtc_state->saveDPLL); - REG_READ(pipeA ? DPLL_A : DPLL_B); - udelay(150); - - REG_WRITE(pipeA ? HTOTAL_A : HTOTAL_B, crtc_state->saveHTOTAL); - REG_WRITE(pipeA ? HBLANK_A : HBLANK_B, crtc_state->saveHBLANK); - REG_WRITE(pipeA ? HSYNC_A : HSYNC_B, crtc_state->saveHSYNC); - REG_WRITE(pipeA ? VTOTAL_A : VTOTAL_B, crtc_state->saveVTOTAL); - REG_WRITE(pipeA ? VBLANK_A : VBLANK_B, crtc_state->saveVBLANK); - REG_WRITE(pipeA ? VSYNC_A : VSYNC_B, crtc_state->saveVSYNC); - REG_WRITE(pipeA ? DSPASTRIDE : DSPBSTRIDE, crtc_state->saveDSPSTRIDE); - - REG_WRITE(pipeA ? DSPASIZE : DSPBSIZE, crtc_state->saveDSPSIZE); - REG_WRITE(pipeA ? DSPAPOS : DSPBPOS, crtc_state->saveDSPPOS); - - REG_WRITE(pipeA ? PIPEASRC : PIPEBSRC, crtc_state->savePIPESRC); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); - REG_WRITE(pipeA ? PIPEACONF : PIPEBCONF, crtc_state->savePIPECONF); - - cdv_intel_wait_for_vblank(dev); - - REG_WRITE(pipeA ? DSPACNTR : DSPBCNTR, crtc_state->saveDSPCNTR); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); - - cdv_intel_wait_for_vblank(dev); - - paletteReg = pipeA ? PALETTE_A : PALETTE_B; - for (i = 0; i < 256; ++i) - REG_WRITE(paletteReg + (i << 2), crtc_state->savePalette[i]); -} - -static int cdv_intel_crtc_cursor_set(struct drm_crtc *crtc, - struct drm_file *file_priv, - uint32_t handle, - uint32_t width, uint32_t height) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; - uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; - uint32_t temp; - size_t addr = 0; - struct gtt_range *gt; - struct drm_gem_object *obj; - int ret; - - /* if we want to turn of the cursor ignore width and height */ - if (!handle) { - /* turn off the cursor */ - temp = CURSOR_MODE_DISABLE; - - if (gma_power_begin(dev, false)) { - REG_WRITE(control, temp); - REG_WRITE(base, 0); - gma_power_end(dev); - } - - /* unpin the old GEM object */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = NULL; - } - - return 0; - } - - /* Currently we only support 64x64 cursors */ - if (width != 64 || height != 64) { - dev_dbg(dev->dev, "we currently only support 64x64 cursors\n"); - return -EINVAL; - } - - obj = drm_gem_object_lookup(dev, file_priv, handle); - if (!obj) - return -ENOENT; - - if (obj->size < width * height * 4) { - dev_dbg(dev->dev, "buffer is to small\n"); - return -ENOMEM; - } - - gt = container_of(obj, struct gtt_range, gem); - - /* Pin the memory into the GTT */ - ret = psb_gtt_pin(gt); - if (ret) { - dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); - return ret; - } - - addr = gt->offset; /* Or resource.start ??? */ - - psb_intel_crtc->cursor_addr = addr; + gma_wait_for_vblank(dev); - temp = 0; - /* set the pipe for the cursor */ - temp |= (pipe << 28); - temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; - - if (gma_power_begin(dev, false)) { - REG_WRITE(control, temp); - REG_WRITE(base, addr); - gma_power_end(dev); - } - - /* unpin the old GEM object */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = obj; - } return 0; } -static int cdv_intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - uint32_t temp = 0; - uint32_t adder; - - - if (x < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); - x = -x; - } - if (y < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); - y = -y; - } - - temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); - temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); - - adder = psb_intel_crtc->cursor_addr; - - if (gma_power_begin(dev, false)) { - REG_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); - REG_WRITE((pipe == 0) ? CURABASE : CURBBASE, adder); - gma_power_end(dev); - } - return 0; -} - -static void cdv_intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, uint32_t start, uint32_t size) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int i; - int end = (start + size > 256) ? 256 : start + size; - - for (i = start; i < end; i++) { - psb_intel_crtc->lut_r[i] = red[i] >> 8; - psb_intel_crtc->lut_g[i] = green[i] >> 8; - psb_intel_crtc->lut_b[i] = blue[i] >> 8; - } - - cdv_intel_crtc_load_lut(crtc); -} - -static int cdv_crtc_set_config(struct drm_mode_set *set) -{ - int ret = 0; - struct drm_device *dev = set->crtc->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - - if (!dev_priv->rpm_enabled) - return drm_crtc_helper_set_config(set); - - pm_runtime_forbid(&dev->pdev->dev); - - ret = drm_crtc_helper_set_config(set); - - pm_runtime_allow(&dev->pdev->dev); - - return ret; -} - /** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ /* FIXME: why are we using this, should it be cdv_ in this tree ? */ -static void i8xx_clock(int refclk, struct cdv_intel_clock_t *clock) +static void i8xx_clock(int refclk, struct gma_clock_t *clock) { clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); clock->p = clock->p1 * clock->p2; @@ -1320,36 +849,33 @@ static void i8xx_clock(int refclk, struct cdv_intel_clock_t *clock) static int cdv_intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) { - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 dpll; u32 fp; - struct cdv_intel_clock_t clock; + struct gma_clock_t clock; bool is_lvds; - struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; if (gma_power_begin(dev, false)) { - dpll = REG_READ((pipe == 0) ? DPLL_A : DPLL_B); + dpll = REG_READ(map->dpll); if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = REG_READ((pipe == 0) ? FPA0 : FPB0); + fp = REG_READ(map->fp0); else - fp = REG_READ((pipe == 0) ? FPA1 : FPB1); + fp = REG_READ(map->fp1); is_lvds = (pipe == 1) && (REG_READ(LVDS) & LVDS_PORT_EN); gma_power_end(dev); } else { - dpll = (pipe == 0) ? - dev_priv->saveDPLL_A : dev_priv->saveDPLL_B; - + dpll = p->dpll; if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = (pipe == 0) ? - dev_priv->saveFPA0 : - dev_priv->saveFPB0; + fp = p->fp0; else - fp = (pipe == 0) ? - dev_priv->saveFPA1 : - dev_priv->saveFPB1; + fp = p->fp1; - 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; @@ -1402,30 +928,28 @@ static int cdv_intel_crtc_clock_get(struct drm_device *dev, struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev, struct drm_crtc *crtc) { - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int pipe = gma_crtc->pipe; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; + const struct psb_offset *map = &dev_priv->regmap[pipe]; struct drm_display_mode *mode; int htot; int hsync; int vtot; int vsync; - struct drm_psb_private *dev_priv = dev->dev_private; if (gma_power_begin(dev, false)) { - htot = REG_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); - hsync = REG_READ((pipe == 0) ? HSYNC_A : HSYNC_B); - vtot = REG_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); - vsync = REG_READ((pipe == 0) ? VSYNC_A : VSYNC_B); + htot = REG_READ(map->htotal); + hsync = REG_READ(map->hsync); + vtot = REG_READ(map->vtotal); + vsync = REG_READ(map->vsync); gma_power_end(dev); } else { - htot = (pipe == 0) ? - dev_priv->saveHTOTAL_A : dev_priv->saveHTOTAL_B; - hsync = (pipe == 0) ? - dev_priv->saveHSYNC_A : dev_priv->saveHSYNC_B; - vtot = (pipe == 0) ? - dev_priv->saveVTOTAL_A : dev_priv->saveVTOTAL_B; - vsync = (pipe == 0) ? - dev_priv->saveVSYNC_A : dev_priv->saveVSYNC_B; + htot = p->htotal; + hsync = p->hsync; + vtot = p->vtotal; + vsync = p->vsync; } mode = kzalloc(sizeof(*mode), GFP_KERNEL); @@ -1448,61 +972,28 @@ struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev, return mode; } -static void cdv_intel_crtc_destroy(struct drm_crtc *crtc) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - - kfree(psb_intel_crtc->crtc_state); - drm_crtc_cleanup(crtc); - kfree(psb_intel_crtc); -} - const struct drm_crtc_helper_funcs cdv_intel_helper_funcs = { - .dpms = cdv_intel_crtc_dpms, - .mode_fixup = cdv_intel_crtc_mode_fixup, + .dpms = gma_crtc_dpms, + .mode_fixup = gma_crtc_mode_fixup, .mode_set = cdv_intel_crtc_mode_set, - .mode_set_base = cdv_intel_pipe_set_base, - .prepare = cdv_intel_crtc_prepare, - .commit = cdv_intel_crtc_commit, + .mode_set_base = gma_pipe_set_base, + .prepare = gma_crtc_prepare, + .commit = gma_crtc_commit, + .disable = gma_crtc_disable, }; const struct drm_crtc_funcs cdv_intel_crtc_funcs = { - .save = cdv_intel_crtc_save, - .restore = cdv_intel_crtc_restore, - .cursor_set = cdv_intel_crtc_cursor_set, - .cursor_move = cdv_intel_crtc_cursor_move, - .gamma_set = cdv_intel_crtc_gamma_set, - .set_config = cdv_crtc_set_config, - .destroy = cdv_intel_crtc_destroy, + .save = gma_crtc_save, + .restore = gma_crtc_restore, + .cursor_set = gma_crtc_cursor_set, + .cursor_move = gma_crtc_cursor_move, + .gamma_set = gma_crtc_gamma_set, + .set_config = gma_crtc_set_config, + .destroy = gma_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); -} - +const struct gma_clock_funcs cdv_clock_funcs = { + .clock = cdv_intel_clock, + .limit = cdv_intel_limit, + .pll_is_valid = gma_pll_is_valid, +}; diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c new file mode 100644 index 00000000000..9ff30c2efad --- /dev/null +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -0,0 +1,1952 @@ +/* + * Copyright © 2012 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: + * Keith Packard <keithp@keithp.com> + * + */ + +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "gma_display.h" +#include <drm/drm_dp_helper.h> + +#define _wait_for(COND, MS, W) ({ \ + unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ + int ret__ = 0; \ + while (! (COND)) { \ + if (time_after(jiffies, timeout__)) { \ + ret__ = -ETIMEDOUT; \ + break; \ + } \ + if (W && !in_dbg_master()) msleep(W); \ + } \ + ret__; \ +}) + +#define wait_for(COND, MS) _wait_for(COND, MS, 1) + +#define DP_LINK_STATUS_SIZE 6 +#define DP_LINK_CHECK_TIMEOUT (10 * 1000) + +#define DP_LINK_CONFIGURATION_SIZE 9 + +#define CDV_FAST_LINK_TRAIN 1 + +struct cdv_intel_dp { + uint32_t output_reg; + uint32_t DP; + uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; + bool has_audio; + int force_audio; + uint32_t color_range; + uint8_t link_bw; + uint8_t lane_count; + uint8_t dpcd[4]; + struct gma_encoder *encoder; + struct i2c_adapter adapter; + struct i2c_algo_dp_aux_data algo; + uint8_t train_set[4]; + uint8_t link_status[DP_LINK_STATUS_SIZE]; + int panel_power_up_delay; + int panel_power_down_delay; + int panel_power_cycle_delay; + int backlight_on_delay; + int backlight_off_delay; + struct drm_display_mode *panel_fixed_mode; /* for eDP */ + bool panel_on; +}; + +struct ddi_regoff { + uint32_t PreEmph1; + uint32_t PreEmph2; + uint32_t VSwing1; + uint32_t VSwing2; + uint32_t VSwing3; + uint32_t VSwing4; + uint32_t VSwing5; +}; + +static struct ddi_regoff ddi_DP_train_table[] = { + {.PreEmph1 = 0x812c, .PreEmph2 = 0x8124, .VSwing1 = 0x8154, + .VSwing2 = 0x8148, .VSwing3 = 0x814C, .VSwing4 = 0x8150, + .VSwing5 = 0x8158,}, + {.PreEmph1 = 0x822c, .PreEmph2 = 0x8224, .VSwing1 = 0x8254, + .VSwing2 = 0x8248, .VSwing3 = 0x824C, .VSwing4 = 0x8250, + .VSwing5 = 0x8258,}, +}; + +static uint32_t dp_vswing_premph_table[] = { + 0x55338954, 0x4000, + 0x554d8954, 0x2000, + 0x55668954, 0, + 0x559ac0d4, 0x6000, +}; +/** + * is_edp - is the given port attached to an eDP panel (either CPU or PCH) + * @intel_dp: DP struct + * + * If a CPU or PCH DP output is attached to an eDP panel, this function + * will return true, and false otherwise. + */ +static bool is_edp(struct gma_encoder *encoder) +{ + return encoder->type == INTEL_OUTPUT_EDP; +} + + +static void cdv_intel_dp_start_link_train(struct gma_encoder *encoder); +static void cdv_intel_dp_complete_link_train(struct gma_encoder *encoder); +static void cdv_intel_dp_link_down(struct gma_encoder *encoder); + +static int +cdv_intel_dp_max_lane_count(struct gma_encoder *encoder) +{ + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + int max_lane_count = 4; + + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) { + max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f; + switch (max_lane_count) { + case 1: case 2: case 4: + break; + default: + max_lane_count = 4; + } + } + return max_lane_count; +} + +static int +cdv_intel_dp_max_link_bw(struct gma_encoder *encoder) +{ + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; + + switch (max_link_bw) { + case DP_LINK_BW_1_62: + case DP_LINK_BW_2_7: + break; + default: + max_link_bw = DP_LINK_BW_1_62; + break; + } + return max_link_bw; +} + +static int +cdv_intel_dp_link_clock(uint8_t link_bw) +{ + if (link_bw == DP_LINK_BW_2_7) + return 270000; + else + return 162000; +} + +static int +cdv_intel_dp_link_required(int pixel_clock, int bpp) +{ + return (pixel_clock * bpp + 7) / 8; +} + +static int +cdv_intel_dp_max_data_rate(int max_link_clock, int max_lanes) +{ + return (max_link_clock * max_lanes * 19) / 20; +} + +static void cdv_intel_edp_panel_vdd_on(struct gma_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + u32 pp; + + if (intel_dp->panel_on) { + DRM_DEBUG_KMS("Skip VDD on because of panel on\n"); + return; + } + DRM_DEBUG_KMS("\n"); + + pp = REG_READ(PP_CONTROL); + + pp |= EDP_FORCE_VDD; + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + msleep(intel_dp->panel_power_up_delay); +} + +static void cdv_intel_edp_panel_vdd_off(struct gma_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + u32 pp; + + DRM_DEBUG_KMS("\n"); + pp = REG_READ(PP_CONTROL); + + pp &= ~EDP_FORCE_VDD; + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + +} + +/* Returns true if the panel was already on when called */ +static bool cdv_intel_edp_panel_on(struct gma_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_NONE; + + if (intel_dp->panel_on) + return true; + + DRM_DEBUG_KMS("\n"); + pp = REG_READ(PP_CONTROL); + pp &= ~PANEL_UNLOCK_MASK; + + pp |= (PANEL_UNLOCK_REGS | POWER_TARGET_ON); + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + + if (wait_for(((REG_READ(PP_STATUS) & idle_on_mask) == idle_on_mask), 1000)) { + DRM_DEBUG_KMS("Error in Powering up eDP panel, status %x\n", REG_READ(PP_STATUS)); + intel_dp->panel_on = false; + } else + intel_dp->panel_on = true; + msleep(intel_dp->panel_power_up_delay); + + return false; +} + +static void cdv_intel_edp_panel_off (struct gma_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + u32 pp, idle_off_mask = PP_ON ; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + + DRM_DEBUG_KMS("\n"); + + pp = REG_READ(PP_CONTROL); + + if ((pp & POWER_TARGET_ON) == 0) + return; + + intel_dp->panel_on = false; + pp &= ~PANEL_UNLOCK_MASK; + /* ILK workaround: disable reset around power sequence */ + + pp &= ~POWER_TARGET_ON; + pp &= ~EDP_FORCE_VDD; + pp &= ~EDP_BLC_ENABLE; + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + DRM_DEBUG_KMS("PP_STATUS %x\n", REG_READ(PP_STATUS)); + + if (wait_for((REG_READ(PP_STATUS) & idle_off_mask) == 0, 1000)) { + DRM_DEBUG_KMS("Error in turning off Panel\n"); + } + + msleep(intel_dp->panel_power_cycle_delay); + DRM_DEBUG_KMS("Over\n"); +} + +static void cdv_intel_edp_backlight_on (struct gma_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + u32 pp; + + DRM_DEBUG_KMS("\n"); + /* + * If we enable the backlight right away following a panel power + * on, we may see slight flicker as the panel syncs with the eDP + * link. So delay a bit to make sure the image is solid before + * allowing it to appear. + */ + msleep(300); + pp = REG_READ(PP_CONTROL); + + pp |= EDP_BLC_ENABLE; + REG_WRITE(PP_CONTROL, pp); + gma_backlight_enable(dev); +} + +static void cdv_intel_edp_backlight_off (struct gma_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + u32 pp; + + DRM_DEBUG_KMS("\n"); + gma_backlight_disable(dev); + msleep(10); + pp = REG_READ(PP_CONTROL); + + pp &= ~EDP_BLC_ENABLE; + REG_WRITE(PP_CONTROL, pp); + msleep(intel_dp->backlight_off_delay); +} + +static int +cdv_intel_dp_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct gma_encoder *encoder = gma_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + int max_link_clock = cdv_intel_dp_link_clock(cdv_intel_dp_max_link_bw(encoder)); + int max_lanes = cdv_intel_dp_max_lane_count(encoder); + struct drm_psb_private *dev_priv = connector->dev->dev_private; + + if (is_edp(encoder) && intel_dp->panel_fixed_mode) { + if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay) + return MODE_PANEL; + if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay) + return MODE_PANEL; + } + + /* only refuse the mode on non eDP since we have seen some weird eDP panels + which are outside spec tolerances but somehow work by magic */ + if (!is_edp(encoder) && + (cdv_intel_dp_link_required(mode->clock, dev_priv->edp.bpp) + > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes))) + return MODE_CLOCK_HIGH; + + if (is_edp(encoder)) { + if (cdv_intel_dp_link_required(mode->clock, 24) + > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes)) + return MODE_CLOCK_HIGH; + + } + if (mode->clock < 10000) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + +static uint32_t +pack_aux(uint8_t *src, int src_bytes) +{ + int i; + uint32_t v = 0; + + if (src_bytes > 4) + src_bytes = 4; + for (i = 0; i < src_bytes; i++) + v |= ((uint32_t) src[i]) << ((3-i) * 8); + return v; +} + +static void +unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) +{ + int i; + if (dst_bytes > 4) + dst_bytes = 4; + for (i = 0; i < dst_bytes; i++) + dst[i] = src >> ((3-i) * 8); +} + +static int +cdv_intel_dp_aux_ch(struct gma_encoder *encoder, + uint8_t *send, int send_bytes, + uint8_t *recv, int recv_size) +{ + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + uint32_t output_reg = intel_dp->output_reg; + struct drm_device *dev = encoder->base.dev; + uint32_t ch_ctl = output_reg + 0x10; + uint32_t ch_data = ch_ctl + 4; + int i; + int recv_bytes; + uint32_t status; + uint32_t aux_clock_divider; + int try, precharge; + + /* The clock divider is based off the hrawclk, + * and would like to run at 2MHz. So, take the + * hrawclk value and divide by 2 and use that + * On CDV platform it uses 200MHz as hrawclk. + * + */ + aux_clock_divider = 200 / 2; + + precharge = 4; + if (is_edp(encoder)) + precharge = 10; + + if (REG_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) { + DRM_ERROR("dp_aux_ch not started status 0x%08x\n", + REG_READ(ch_ctl)); + return -EBUSY; + } + + /* Must try at least 3 times according to DP spec */ + for (try = 0; try < 5; try++) { + /* Load the send data into the aux channel data registers */ + for (i = 0; i < send_bytes; i += 4) + REG_WRITE(ch_data + i, + pack_aux(send + i, send_bytes - i)); + + /* Send the command and wait for it to complete */ + REG_WRITE(ch_ctl, + DP_AUX_CH_CTL_SEND_BUSY | + DP_AUX_CH_CTL_TIME_OUT_400us | + (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | + (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | + (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) | + DP_AUX_CH_CTL_DONE | + DP_AUX_CH_CTL_TIME_OUT_ERROR | + DP_AUX_CH_CTL_RECEIVE_ERROR); + for (;;) { + status = REG_READ(ch_ctl); + if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) + break; + udelay(100); + } + + /* Clear done status and any errors */ + REG_WRITE(ch_ctl, + status | + DP_AUX_CH_CTL_DONE | + DP_AUX_CH_CTL_TIME_OUT_ERROR | + DP_AUX_CH_CTL_RECEIVE_ERROR); + if (status & DP_AUX_CH_CTL_DONE) + break; + } + + if ((status & DP_AUX_CH_CTL_DONE) == 0) { + DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); + return -EBUSY; + } + + /* Check for timeout or receive error. + * Timeouts occur when the sink is not connected + */ + if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { + DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); + return -EIO; + } + + /* Timeouts occur when the device isn't connected, so they're + * "normal" -- don't fill the kernel log with these */ + if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { + DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); + return -ETIMEDOUT; + } + + /* Unload any bytes sent back from the other side */ + recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >> + DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT); + if (recv_bytes > recv_size) + recv_bytes = recv_size; + + for (i = 0; i < recv_bytes; i += 4) + unpack_aux(REG_READ(ch_data + i), + recv + i, recv_bytes - i); + + return recv_bytes; +} + +/* Write data to the aux channel in native mode */ +static int +cdv_intel_dp_aux_native_write(struct gma_encoder *encoder, + uint16_t address, uint8_t *send, int send_bytes) +{ + int ret; + uint8_t msg[20]; + int msg_bytes; + uint8_t ack; + + if (send_bytes > 16) + return -1; + msg[0] = DP_AUX_NATIVE_WRITE << 4; + msg[1] = address >> 8; + msg[2] = address & 0xff; + msg[3] = send_bytes - 1; + memcpy(&msg[4], send, send_bytes); + msg_bytes = send_bytes + 4; + for (;;) { + ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, &ack, 1); + if (ret < 0) + return ret; + ack >>= 4; + if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_ACK) + break; + else if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_DEFER) + udelay(100); + else + return -EIO; + } + return send_bytes; +} + +/* Write a single byte to the aux channel in native mode */ +static int +cdv_intel_dp_aux_native_write_1(struct gma_encoder *encoder, + uint16_t address, uint8_t byte) +{ + return cdv_intel_dp_aux_native_write(encoder, address, &byte, 1); +} + +/* read bytes from a native aux channel */ +static int +cdv_intel_dp_aux_native_read(struct gma_encoder *encoder, + uint16_t address, uint8_t *recv, int recv_bytes) +{ + uint8_t msg[4]; + int msg_bytes; + uint8_t reply[20]; + int reply_bytes; + uint8_t ack; + int ret; + + msg[0] = DP_AUX_NATIVE_READ << 4; + msg[1] = address >> 8; + msg[2] = address & 0xff; + msg[3] = recv_bytes - 1; + + msg_bytes = 4; + reply_bytes = recv_bytes + 1; + + for (;;) { + ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, + reply, reply_bytes); + if (ret == 0) + return -EPROTO; + if (ret < 0) + return ret; + ack = reply[0] >> 4; + if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_ACK) { + memcpy(recv, reply + 1, ret - 1); + return ret - 1; + } + else if ((ack & DP_AUX_NATIVE_REPLY_MASK) == DP_AUX_NATIVE_REPLY_DEFER) + udelay(100); + else + return -EIO; + } +} + +static int +cdv_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, + uint8_t write_byte, uint8_t *read_byte) +{ + struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; + struct cdv_intel_dp *intel_dp = container_of(adapter, + struct cdv_intel_dp, + adapter); + struct gma_encoder *encoder = intel_dp->encoder; + uint16_t address = algo_data->address; + uint8_t msg[5]; + uint8_t reply[2]; + unsigned retry; + int msg_bytes; + int reply_bytes; + int ret; + + /* Set up the command byte */ + if (mode & MODE_I2C_READ) + msg[0] = DP_AUX_I2C_READ << 4; + else + msg[0] = DP_AUX_I2C_WRITE << 4; + + if (!(mode & MODE_I2C_STOP)) + msg[0] |= DP_AUX_I2C_MOT << 4; + + msg[1] = address >> 8; + msg[2] = address; + + switch (mode) { + case MODE_I2C_WRITE: + msg[3] = 0; + msg[4] = write_byte; + msg_bytes = 5; + reply_bytes = 1; + break; + case MODE_I2C_READ: + msg[3] = 0; + msg_bytes = 4; + reply_bytes = 2; + break; + default: + msg_bytes = 3; + reply_bytes = 1; + break; + } + + for (retry = 0; retry < 5; retry++) { + ret = cdv_intel_dp_aux_ch(encoder, + msg, msg_bytes, + reply, reply_bytes); + if (ret < 0) { + DRM_DEBUG_KMS("aux_ch failed %d\n", ret); + return ret; + } + + switch ((reply[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK) { + case DP_AUX_NATIVE_REPLY_ACK: + /* I2C-over-AUX Reply field is only valid + * when paired with AUX ACK. + */ + break; + case DP_AUX_NATIVE_REPLY_NACK: + DRM_DEBUG_KMS("aux_ch native nack\n"); + return -EREMOTEIO; + case DP_AUX_NATIVE_REPLY_DEFER: + udelay(100); + continue; + default: + DRM_ERROR("aux_ch invalid native reply 0x%02x\n", + reply[0]); + return -EREMOTEIO; + } + + switch ((reply[0] >> 4) & DP_AUX_I2C_REPLY_MASK) { + case DP_AUX_I2C_REPLY_ACK: + if (mode == MODE_I2C_READ) { + *read_byte = reply[1]; + } + return reply_bytes - 1; + case DP_AUX_I2C_REPLY_NACK: + DRM_DEBUG_KMS("aux_i2c nack\n"); + return -EREMOTEIO; + case DP_AUX_I2C_REPLY_DEFER: + DRM_DEBUG_KMS("aux_i2c defer\n"); + udelay(100); + break; + default: + DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]); + return -EREMOTEIO; + } + } + + DRM_ERROR("too many retries, giving up\n"); + return -EREMOTEIO; +} + +static int +cdv_intel_dp_i2c_init(struct gma_connector *connector, + struct gma_encoder *encoder, const char *name) +{ + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + int ret; + + DRM_DEBUG_KMS("i2c_init %s\n", name); + + intel_dp->algo.running = false; + intel_dp->algo.address = 0; + intel_dp->algo.aux_ch = cdv_intel_dp_i2c_aux_ch; + + memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter)); + intel_dp->adapter.owner = THIS_MODULE; + intel_dp->adapter.class = I2C_CLASS_DDC; + strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); + intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0'; + intel_dp->adapter.algo_data = &intel_dp->algo; + intel_dp->adapter.dev.parent = connector->base.kdev; + + if (is_edp(encoder)) + cdv_intel_edp_panel_vdd_on(encoder); + ret = i2c_dp_aux_add_bus(&intel_dp->adapter); + if (is_edp(encoder)) + cdv_intel_edp_panel_vdd_off(encoder); + + return ret; +} + +static void cdv_intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, + struct drm_display_mode *adjusted_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); +} + +static bool +cdv_intel_dp_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_psb_private *dev_priv = encoder->dev->dev_private; + struct gma_encoder *intel_encoder = to_gma_encoder(encoder); + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + int lane_count, clock; + int max_lane_count = cdv_intel_dp_max_lane_count(intel_encoder); + int max_clock = cdv_intel_dp_max_link_bw(intel_encoder) == DP_LINK_BW_2_7 ? 1 : 0; + static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; + int refclock = mode->clock; + int bpp = 24; + + if (is_edp(intel_encoder) && intel_dp->panel_fixed_mode) { + cdv_intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode); + refclock = intel_dp->panel_fixed_mode->clock; + bpp = dev_priv->edp.bpp; + } + + for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { + for (clock = max_clock; clock >= 0; clock--) { + int link_avail = cdv_intel_dp_max_data_rate(cdv_intel_dp_link_clock(bws[clock]), lane_count); + + if (cdv_intel_dp_link_required(refclock, bpp) <= link_avail) { + intel_dp->link_bw = bws[clock]; + intel_dp->lane_count = lane_count; + adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw); + DRM_DEBUG_KMS("Display port link bw %02x lane " + "count %d clock %d\n", + intel_dp->link_bw, intel_dp->lane_count, + adjusted_mode->clock); + return true; + } + } + } + if (is_edp(intel_encoder)) { + /* okay we failed just pick the highest */ + intel_dp->lane_count = max_lane_count; + intel_dp->link_bw = bws[max_clock]; + adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw); + DRM_DEBUG_KMS("Force picking display port link bw %02x lane " + "count %d clock %d\n", + intel_dp->link_bw, intel_dp->lane_count, + adjusted_mode->clock); + + return true; + } + return false; +} + +struct cdv_intel_dp_m_n { + uint32_t tu; + uint32_t gmch_m; + uint32_t gmch_n; + uint32_t link_m; + uint32_t link_n; +}; + +static void +cdv_intel_reduce_ratio(uint32_t *num, uint32_t *den) +{ + /* + while (*num > 0xffffff || *den > 0xffffff) { + *num >>= 1; + *den >>= 1; + }*/ + uint64_t value, m; + m = *num; + value = m * (0x800000); + m = do_div(value, *den); + *num = value; + *den = 0x800000; +} + +static void +cdv_intel_dp_compute_m_n(int bpp, + int nlanes, + int pixel_clock, + int link_clock, + struct cdv_intel_dp_m_n *m_n) +{ + m_n->tu = 64; + m_n->gmch_m = (pixel_clock * bpp + 7) >> 3; + m_n->gmch_n = link_clock * nlanes; + cdv_intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); + m_n->link_m = pixel_clock; + m_n->link_n = link_clock; + cdv_intel_reduce_ratio(&m_n->link_m, &m_n->link_n); +} + +void +cdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_encoder *encoder; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int lane_count = 4, bpp = 24; + struct cdv_intel_dp_m_n m_n; + int pipe = gma_crtc->pipe; + + /* + * Find the lane count in the intel_encoder private + */ + list_for_each_entry(encoder, &mode_config->encoder_list, head) { + struct gma_encoder *intel_encoder; + struct cdv_intel_dp *intel_dp; + + if (encoder->crtc != crtc) + continue; + + intel_encoder = to_gma_encoder(encoder); + intel_dp = intel_encoder->dev_priv; + if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { + lane_count = intel_dp->lane_count; + break; + } else if (is_edp(intel_encoder)) { + lane_count = intel_dp->lane_count; + bpp = dev_priv->edp.bpp; + break; + } + } + + /* + * Compute the GMCH and Link ratios. The '3' here is + * the number of bytes_per_pixel post-LUT, which we always + * set up for 8-bits of R/G/B, or 3 bytes total. + */ + cdv_intel_dp_compute_m_n(bpp, lane_count, + mode->clock, adjusted_mode->clock, &m_n); + + { + REG_WRITE(PIPE_GMCH_DATA_M(pipe), + ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) | + m_n.gmch_m); + REG_WRITE(PIPE_GMCH_DATA_N(pipe), m_n.gmch_n); + REG_WRITE(PIPE_DP_LINK_M(pipe), m_n.link_m); + REG_WRITE(PIPE_DP_LINK_N(pipe), m_n.link_n); + } +} + +static void +cdv_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct gma_encoder *intel_encoder = to_gma_encoder(encoder); + struct drm_crtc *crtc = encoder->crtc; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + struct drm_device *dev = encoder->dev; + + intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; + intel_dp->DP |= intel_dp->color_range; + + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + intel_dp->DP |= DP_SYNC_HS_HIGH; + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + intel_dp->DP |= DP_SYNC_VS_HIGH; + + intel_dp->DP |= DP_LINK_TRAIN_OFF; + + switch (intel_dp->lane_count) { + case 1: + intel_dp->DP |= DP_PORT_WIDTH_1; + break; + case 2: + intel_dp->DP |= DP_PORT_WIDTH_2; + break; + case 4: + intel_dp->DP |= DP_PORT_WIDTH_4; + break; + } + if (intel_dp->has_audio) + intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; + + memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE); + intel_dp->link_configuration[0] = intel_dp->link_bw; + intel_dp->link_configuration[1] = intel_dp->lane_count; + + /* + * Check for DPCD version > 1.1 and enhanced framing support + */ + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && + (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) { + intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + intel_dp->DP |= DP_ENHANCED_FRAMING; + } + + /* CPT DP's pipe select is decided in TRANS_DP_CTL */ + if (gma_crtc->pipe == 1) + intel_dp->DP |= DP_PIPEB_SELECT; + + REG_WRITE(intel_dp->output_reg, (intel_dp->DP | DP_PORT_EN)); + DRM_DEBUG_KMS("DP expected reg is %x\n", intel_dp->DP); + if (is_edp(intel_encoder)) { + uint32_t pfit_control; + cdv_intel_edp_panel_on(intel_encoder); + + if (mode->hdisplay != adjusted_mode->hdisplay || + mode->vdisplay != adjusted_mode->vdisplay) + pfit_control = PFIT_ENABLE; + else + pfit_control = 0; + + pfit_control |= gma_crtc->pipe << PFIT_PIPE_SHIFT; + + REG_WRITE(PFIT_CONTROL, pfit_control); + } +} + + +/* If the sink supports it, try to set the power state appropriately */ +static void cdv_intel_dp_sink_dpms(struct gma_encoder *encoder, int mode) +{ + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + int ret, i; + + /* Should have a valid DPCD by this point */ + if (intel_dp->dpcd[DP_DPCD_REV] < 0x11) + return; + + if (mode != DRM_MODE_DPMS_ON) { + ret = cdv_intel_dp_aux_native_write_1(encoder, DP_SET_POWER, + DP_SET_POWER_D3); + if (ret != 1) + DRM_DEBUG_DRIVER("failed to write sink power state\n"); + } else { + /* + * When turning on, we need to retry for 1ms to give the sink + * time to wake up. + */ + for (i = 0; i < 3; i++) { + ret = cdv_intel_dp_aux_native_write_1(encoder, + DP_SET_POWER, + DP_SET_POWER_D0); + if (ret == 1) + break; + udelay(1000); + } + } +} + +static void cdv_intel_dp_prepare(struct drm_encoder *encoder) +{ + struct gma_encoder *intel_encoder = to_gma_encoder(encoder); + int edp = is_edp(intel_encoder); + + if (edp) { + cdv_intel_edp_backlight_off(intel_encoder); + cdv_intel_edp_panel_off(intel_encoder); + cdv_intel_edp_panel_vdd_on(intel_encoder); + } + /* Wake up the sink first */ + cdv_intel_dp_sink_dpms(intel_encoder, DRM_MODE_DPMS_ON); + cdv_intel_dp_link_down(intel_encoder); + if (edp) + cdv_intel_edp_panel_vdd_off(intel_encoder); +} + +static void cdv_intel_dp_commit(struct drm_encoder *encoder) +{ + struct gma_encoder *intel_encoder = to_gma_encoder(encoder); + int edp = is_edp(intel_encoder); + + if (edp) + cdv_intel_edp_panel_on(intel_encoder); + cdv_intel_dp_start_link_train(intel_encoder); + cdv_intel_dp_complete_link_train(intel_encoder); + if (edp) + cdv_intel_edp_backlight_on(intel_encoder); +} + +static void +cdv_intel_dp_dpms(struct drm_encoder *encoder, int mode) +{ + struct gma_encoder *intel_encoder = to_gma_encoder(encoder); + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + struct drm_device *dev = encoder->dev; + uint32_t dp_reg = REG_READ(intel_dp->output_reg); + int edp = is_edp(intel_encoder); + + if (mode != DRM_MODE_DPMS_ON) { + if (edp) { + cdv_intel_edp_backlight_off(intel_encoder); + cdv_intel_edp_panel_vdd_on(intel_encoder); + } + cdv_intel_dp_sink_dpms(intel_encoder, mode); + cdv_intel_dp_link_down(intel_encoder); + if (edp) { + cdv_intel_edp_panel_vdd_off(intel_encoder); + cdv_intel_edp_panel_off(intel_encoder); + } + } else { + if (edp) + cdv_intel_edp_panel_on(intel_encoder); + cdv_intel_dp_sink_dpms(intel_encoder, mode); + if (!(dp_reg & DP_PORT_EN)) { + cdv_intel_dp_start_link_train(intel_encoder); + cdv_intel_dp_complete_link_train(intel_encoder); + } + if (edp) + cdv_intel_edp_backlight_on(intel_encoder); + } +} + +/* + * Native read with retry for link status and receiver capability reads for + * cases where the sink may still be asleep. + */ +static bool +cdv_intel_dp_aux_native_read_retry(struct gma_encoder *encoder, uint16_t address, + uint8_t *recv, int recv_bytes) +{ + int ret, i; + + /* + * Sinks are *supposed* to come up within 1ms from an off state, + * but we're also supposed to retry 3 times per the spec. + */ + for (i = 0; i < 3; i++) { + ret = cdv_intel_dp_aux_native_read(encoder, address, recv, + recv_bytes); + if (ret == recv_bytes) + return true; + udelay(1000); + } + + return false; +} + +/* + * Fetch AUX CH registers 0x202 - 0x207 which contain + * link status information + */ +static bool +cdv_intel_dp_get_link_status(struct gma_encoder *encoder) +{ + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + return cdv_intel_dp_aux_native_read_retry(encoder, + DP_LANE0_1_STATUS, + intel_dp->link_status, + DP_LINK_STATUS_SIZE); +} + +static uint8_t +cdv_intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], + int r) +{ + return link_status[r - DP_LANE0_1_STATUS]; +} + +static uint8_t +cdv_intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : + DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); + uint8_t l = cdv_intel_dp_link_status(link_status, i); + + return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; +} + +static uint8_t +cdv_intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : + DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); + uint8_t l = cdv_intel_dp_link_status(link_status, i); + + return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; +} + + +#if 0 +static char *voltage_names[] = { + "0.4V", "0.6V", "0.8V", "1.2V" +}; +static char *pre_emph_names[] = { + "0dB", "3.5dB", "6dB", "9.5dB" +}; +static char *link_train_names[] = { + "pattern 1", "pattern 2", "idle", "off" +}; +#endif + +#define CDV_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 +/* +static uint8_t +cdv_intel_dp_pre_emphasis_max(uint8_t voltage_swing) +{ + switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + return DP_TRAIN_PRE_EMPHASIS_6; + case DP_TRAIN_VOLTAGE_SWING_600: + return DP_TRAIN_PRE_EMPHASIS_6; + case DP_TRAIN_VOLTAGE_SWING_800: + return DP_TRAIN_PRE_EMPHASIS_3_5; + case DP_TRAIN_VOLTAGE_SWING_1200: + default: + return DP_TRAIN_PRE_EMPHASIS_0; + } +} +*/ +static void +cdv_intel_get_adjust_train(struct gma_encoder *encoder) +{ + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + uint8_t v = 0; + uint8_t p = 0; + int lane; + + for (lane = 0; lane < intel_dp->lane_count; lane++) { + uint8_t this_v = cdv_intel_get_adjust_request_voltage(intel_dp->link_status, lane); + uint8_t this_p = cdv_intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane); + + if (this_v > v) + v = this_v; + if (this_p > p) + p = this_p; + } + + if (v >= CDV_DP_VOLTAGE_MAX) + v = CDV_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED; + + if (p == DP_TRAIN_PRE_EMPHASIS_MASK) + p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + for (lane = 0; lane < 4; lane++) + intel_dp->train_set[lane] = v | p; +} + + +static uint8_t +cdv_intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + uint8_t l = cdv_intel_dp_link_status(link_status, i); + + return (l >> s) & 0xf; +} + +/* Check for clock recovery is done on all channels */ +static bool +cdv_intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) +{ + int lane; + uint8_t lane_status; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = cdv_intel_get_lane_status(link_status, lane); + if ((lane_status & DP_LANE_CR_DONE) == 0) + return false; + } + return true; +} + +/* Check to see if channel eq is done on all channels */ +#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\ + DP_LANE_CHANNEL_EQ_DONE|\ + DP_LANE_SYMBOL_LOCKED) +static bool +cdv_intel_channel_eq_ok(struct gma_encoder *encoder) +{ + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + uint8_t lane_align; + uint8_t lane_status; + int lane; + + lane_align = cdv_intel_dp_link_status(intel_dp->link_status, + DP_LANE_ALIGN_STATUS_UPDATED); + if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) + return false; + for (lane = 0; lane < intel_dp->lane_count; lane++) { + lane_status = cdv_intel_get_lane_status(intel_dp->link_status, lane); + if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) + return false; + } + return true; +} + +static bool +cdv_intel_dp_set_link_train(struct gma_encoder *encoder, + uint32_t dp_reg_value, + uint8_t dp_train_pat) +{ + + struct drm_device *dev = encoder->base.dev; + int ret; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + + REG_WRITE(intel_dp->output_reg, dp_reg_value); + REG_READ(intel_dp->output_reg); + + ret = cdv_intel_dp_aux_native_write_1(encoder, + DP_TRAINING_PATTERN_SET, + dp_train_pat); + + if (ret != 1) { + DRM_DEBUG_KMS("Failure in setting link pattern %x\n", + dp_train_pat); + return false; + } + + return true; +} + + +static bool +cdv_intel_dplink_set_level(struct gma_encoder *encoder, + uint8_t dp_train_pat) +{ + + int ret; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + + ret = cdv_intel_dp_aux_native_write(encoder, + DP_TRAINING_LANE0_SET, + intel_dp->train_set, + intel_dp->lane_count); + + if (ret != intel_dp->lane_count) { + DRM_DEBUG_KMS("Failure in setting level %d, lane_cnt= %d\n", + intel_dp->train_set[0], intel_dp->lane_count); + return false; + } + return true; +} + +static void +cdv_intel_dp_set_vswing_premph(struct gma_encoder *encoder, uint8_t signal_level) +{ + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + struct ddi_regoff *ddi_reg; + int vswing, premph, index; + + if (intel_dp->output_reg == DP_B) + ddi_reg = &ddi_DP_train_table[0]; + else + ddi_reg = &ddi_DP_train_table[1]; + + vswing = (signal_level & DP_TRAIN_VOLTAGE_SWING_MASK); + premph = ((signal_level & DP_TRAIN_PRE_EMPHASIS_MASK)) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT; + + if (vswing + premph > 3) + return; +#ifdef CDV_FAST_LINK_TRAIN + return; +#endif + DRM_DEBUG_KMS("Test2\n"); + //return ; + cdv_sb_reset(dev); + /* ;Swing voltage programming + ;gfx_dpio_set_reg(0xc058, 0x0505313A) */ + cdv_sb_write(dev, ddi_reg->VSwing5, 0x0505313A); + + /* ;gfx_dpio_set_reg(0x8154, 0x43406055) */ + cdv_sb_write(dev, ddi_reg->VSwing1, 0x43406055); + + /* ;gfx_dpio_set_reg(0x8148, 0x55338954) + * The VSwing_PreEmph table is also considered based on the vswing/premp + */ + index = (vswing + premph) * 2; + if (premph == 1 && vswing == 1) { + cdv_sb_write(dev, ddi_reg->VSwing2, 0x055738954); + } else + cdv_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]); + + /* ;gfx_dpio_set_reg(0x814c, 0x40802040) */ + if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_1200) + cdv_sb_write(dev, ddi_reg->VSwing3, 0x70802040); + else + cdv_sb_write(dev, ddi_reg->VSwing3, 0x40802040); + + /* ;gfx_dpio_set_reg(0x8150, 0x2b405555) */ + /* cdv_sb_write(dev, ddi_reg->VSwing4, 0x2b405555); */ + + /* ;gfx_dpio_set_reg(0x8154, 0xc3406055) */ + cdv_sb_write(dev, ddi_reg->VSwing1, 0xc3406055); + + /* ;Pre emphasis programming + * ;gfx_dpio_set_reg(0xc02c, 0x1f030040) + */ + cdv_sb_write(dev, ddi_reg->PreEmph1, 0x1f030040); + + /* ;gfx_dpio_set_reg(0x8124, 0x00004000) */ + index = 2 * premph + 1; + cdv_sb_write(dev, ddi_reg->PreEmph2, dp_vswing_premph_table[index]); + return; +} + + +/* Enable corresponding port and start training pattern 1 */ +static void +cdv_intel_dp_start_link_train(struct gma_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + int i; + uint8_t voltage; + bool clock_recovery = false; + int tries; + u32 reg; + uint32_t DP = intel_dp->DP; + + DP |= DP_PORT_EN; + DP &= ~DP_LINK_TRAIN_MASK; + + reg = DP; + reg |= DP_LINK_TRAIN_PAT_1; + /* Enable output, wait for it to become active */ + REG_WRITE(intel_dp->output_reg, reg); + REG_READ(intel_dp->output_reg); + gma_wait_for_vblank(dev); + + DRM_DEBUG_KMS("Link config\n"); + /* Write the link configuration data */ + cdv_intel_dp_aux_native_write(encoder, DP_LINK_BW_SET, + intel_dp->link_configuration, + 2); + + memset(intel_dp->train_set, 0, 4); + voltage = 0; + tries = 0; + clock_recovery = false; + + DRM_DEBUG_KMS("Start train\n"); + reg = DP | DP_LINK_TRAIN_PAT_1; + + + for (;;) { + /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ + DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n", + intel_dp->train_set[0], + intel_dp->link_configuration[0], + intel_dp->link_configuration[1]); + + if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_1)) { + DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 1\n"); + } + cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]); + /* Set training pattern 1 */ + + cdv_intel_dplink_set_level(encoder, DP_TRAINING_PATTERN_1); + + udelay(200); + if (!cdv_intel_dp_get_link_status(encoder)) + break; + + DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n", + intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2], + intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]); + + if (cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + DRM_DEBUG_KMS("PT1 train is done\n"); + clock_recovery = true; + break; + } + + /* Check to see if we've tried the max voltage */ + for (i = 0; i < intel_dp->lane_count; i++) + if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) + break; + if (i == intel_dp->lane_count) + break; + + /* Check to see if we've tried the same voltage 5 times */ + if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { + ++tries; + if (tries == 5) + break; + } else + tries = 0; + voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + + /* Compute new intel_dp->train_set as requested by target */ + cdv_intel_get_adjust_train(encoder); + + } + + if (!clock_recovery) { + DRM_DEBUG_KMS("failure in DP patter 1 training, train set %x\n", intel_dp->train_set[0]); + } + + intel_dp->DP = DP; +} + +static void +cdv_intel_dp_complete_link_train(struct gma_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + bool channel_eq = false; + int tries, cr_tries; + u32 reg; + uint32_t DP = intel_dp->DP; + + /* channel equalization */ + tries = 0; + cr_tries = 0; + channel_eq = false; + + DRM_DEBUG_KMS("\n"); + reg = DP | DP_LINK_TRAIN_PAT_2; + + for (;;) { + + DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n", + intel_dp->train_set[0], + intel_dp->link_configuration[0], + intel_dp->link_configuration[1]); + /* channel eq pattern */ + + if (!cdv_intel_dp_set_link_train(encoder, reg, + DP_TRAINING_PATTERN_2)) { + DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 2\n"); + } + /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ + + if (cr_tries > 5) { + DRM_ERROR("failed to train DP, aborting\n"); + cdv_intel_dp_link_down(encoder); + break; + } + + cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]); + + cdv_intel_dplink_set_level(encoder, DP_TRAINING_PATTERN_2); + + udelay(1000); + if (!cdv_intel_dp_get_link_status(encoder)) + break; + + DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n", + intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2], + intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]); + + /* Make sure clock is still ok */ + if (!cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + cdv_intel_dp_start_link_train(encoder); + cr_tries++; + continue; + } + + if (cdv_intel_channel_eq_ok(encoder)) { + DRM_DEBUG_KMS("PT2 train is done\n"); + channel_eq = true; + break; + } + + /* Try 5 times, then try clock recovery if that fails */ + if (tries > 5) { + cdv_intel_dp_link_down(encoder); + cdv_intel_dp_start_link_train(encoder); + tries = 0; + cr_tries++; + continue; + } + + /* Compute new intel_dp->train_set as requested by target */ + cdv_intel_get_adjust_train(encoder); + ++tries; + + } + + reg = DP | DP_LINK_TRAIN_OFF; + + REG_WRITE(intel_dp->output_reg, reg); + REG_READ(intel_dp->output_reg); + cdv_intel_dp_aux_native_write_1(encoder, + DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); +} + +static void +cdv_intel_dp_link_down(struct gma_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + uint32_t DP = intel_dp->DP; + + if ((REG_READ(intel_dp->output_reg) & DP_PORT_EN) == 0) + return; + + DRM_DEBUG_KMS("\n"); + + + { + DP &= ~DP_LINK_TRAIN_MASK; + REG_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE); + } + REG_READ(intel_dp->output_reg); + + msleep(17); + + REG_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); + REG_READ(intel_dp->output_reg); +} + +static enum drm_connector_status cdv_dp_detect(struct gma_encoder *encoder) +{ + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + enum drm_connector_status status; + + status = connector_status_disconnected; + if (cdv_intel_dp_aux_native_read(encoder, 0x000, intel_dp->dpcd, + sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd)) + { + if (intel_dp->dpcd[DP_DPCD_REV] != 0) + status = connector_status_connected; + } + if (status == connector_status_connected) + DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n", + intel_dp->dpcd[0], intel_dp->dpcd[1], + intel_dp->dpcd[2], intel_dp->dpcd[3]); + return status; +} + +/** + * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection. + * + * \return true if DP port is connected. + * \return false if DP port is disconnected. + */ +static enum drm_connector_status +cdv_intel_dp_detect(struct drm_connector *connector, bool force) +{ + struct gma_encoder *encoder = gma_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + enum drm_connector_status status; + struct edid *edid = NULL; + int edp = is_edp(encoder); + + intel_dp->has_audio = false; + + if (edp) + cdv_intel_edp_panel_vdd_on(encoder); + status = cdv_dp_detect(encoder); + if (status != connector_status_connected) { + if (edp) + cdv_intel_edp_panel_vdd_off(encoder); + return status; + } + + if (intel_dp->force_audio) { + intel_dp->has_audio = intel_dp->force_audio > 0; + } else { + edid = drm_get_edid(connector, &intel_dp->adapter); + if (edid) { + intel_dp->has_audio = drm_detect_monitor_audio(edid); + kfree(edid); + } + } + if (edp) + cdv_intel_edp_panel_vdd_off(encoder); + + return connector_status_connected; +} + +static int cdv_intel_dp_get_modes(struct drm_connector *connector) +{ + struct gma_encoder *intel_encoder = gma_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + struct edid *edid = NULL; + int ret = 0; + int edp = is_edp(intel_encoder); + + + edid = drm_get_edid(connector, &intel_dp->adapter); + if (edid) { + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + kfree(edid); + } + + if (is_edp(intel_encoder)) { + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + cdv_intel_edp_panel_vdd_off(intel_encoder); + if (ret) { + if (edp && !intel_dp->panel_fixed_mode) { + struct drm_display_mode *newmode; + list_for_each_entry(newmode, &connector->probed_modes, + head) { + if (newmode->type & DRM_MODE_TYPE_PREFERRED) { + intel_dp->panel_fixed_mode = + drm_mode_duplicate(dev, newmode); + break; + } + } + } + + return ret; + } + if (!intel_dp->panel_fixed_mode && dev_priv->lfp_lvds_vbt_mode) { + intel_dp->panel_fixed_mode = + drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); + if (intel_dp->panel_fixed_mode) { + intel_dp->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + } + } + if (intel_dp->panel_fixed_mode != NULL) { + struct drm_display_mode *mode; + mode = drm_mode_duplicate(dev, intel_dp->panel_fixed_mode); + drm_mode_probed_add(connector, mode); + return 1; + } + } + + return ret; +} + +static bool +cdv_intel_dp_detect_audio(struct drm_connector *connector) +{ + struct gma_encoder *encoder = gma_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + struct edid *edid; + bool has_audio = false; + int edp = is_edp(encoder); + + if (edp) + cdv_intel_edp_panel_vdd_on(encoder); + + edid = drm_get_edid(connector, &intel_dp->adapter); + if (edid) { + has_audio = drm_detect_monitor_audio(edid); + kfree(edid); + } + if (edp) + cdv_intel_edp_panel_vdd_off(encoder); + + return has_audio; +} + +static int +cdv_intel_dp_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + struct drm_psb_private *dev_priv = connector->dev->dev_private; + struct gma_encoder *encoder = gma_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + int ret; + + ret = drm_object_property_set_value(&connector->base, property, val); + if (ret) + return ret; + + if (property == dev_priv->force_audio_property) { + int i = val; + bool has_audio; + + if (i == intel_dp->force_audio) + return 0; + + intel_dp->force_audio = i; + + if (i == 0) + has_audio = cdv_intel_dp_detect_audio(connector); + else + has_audio = i > 0; + + if (has_audio == intel_dp->has_audio) + return 0; + + intel_dp->has_audio = has_audio; + goto done; + } + + if (property == dev_priv->broadcast_rgb_property) { + if (val == !!intel_dp->color_range) + return 0; + + intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0; + goto done; + } + + return -EINVAL; + +done: + if (encoder->base.crtc) { + struct drm_crtc *crtc = encoder->base.crtc; + drm_crtc_helper_set_mode(crtc, &crtc->mode, + crtc->x, crtc->y, + crtc->primary->fb); + } + + return 0; +} + +static void +cdv_intel_dp_destroy(struct drm_connector *connector) +{ + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = gma_encoder->dev_priv; + + if (is_edp(gma_encoder)) { + /* cdv_intel_panel_destroy_backlight(connector->dev); */ + if (intel_dp->panel_fixed_mode) { + kfree(intel_dp->panel_fixed_mode); + intel_dp->panel_fixed_mode = NULL; + } + } + i2c_del_adapter(&intel_dp->adapter); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +static void cdv_intel_dp_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_helper_funcs cdv_intel_dp_helper_funcs = { + .dpms = cdv_intel_dp_dpms, + .mode_fixup = cdv_intel_dp_mode_fixup, + .prepare = cdv_intel_dp_prepare, + .mode_set = cdv_intel_dp_mode_set, + .commit = cdv_intel_dp_commit, +}; + +static const struct drm_connector_funcs cdv_intel_dp_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = cdv_intel_dp_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = cdv_intel_dp_set_property, + .destroy = cdv_intel_dp_destroy, +}; + +static const struct drm_connector_helper_funcs cdv_intel_dp_connector_helper_funcs = { + .get_modes = cdv_intel_dp_get_modes, + .mode_valid = cdv_intel_dp_mode_valid, + .best_encoder = gma_best_encoder, +}; + +static const struct drm_encoder_funcs cdv_intel_dp_enc_funcs = { + .destroy = cdv_intel_dp_encoder_destroy, +}; + + +static void cdv_intel_dp_add_properties(struct drm_connector *connector) +{ + cdv_intel_attach_force_audio_property(connector); + cdv_intel_attach_broadcast_rgb_property(connector); +} + +/* check the VBT to see whether the eDP is on DP-D port */ +static bool cdv_intel_dpc_is_edp(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct child_device_config *p_child; + int i; + + if (!dev_priv->child_dev_num) + return false; + + for (i = 0; i < dev_priv->child_dev_num; i++) { + p_child = dev_priv->child_dev + i; + + if (p_child->dvo_port == PORT_IDPC && + p_child->device_type == DEVICE_TYPE_eDP) + return true; + } + return false; +} + +/* Cedarview display clock gating + + We need this disable dot get correct behaviour while enabling + DP/eDP. TODO - investigate if we can turn it back to normality + after enabling */ +static void cdv_disable_intel_clock_gating(struct drm_device *dev) +{ + u32 reg_value; + reg_value = REG_READ(DSPCLK_GATE_D); + + reg_value |= (DPUNIT_PIPEB_GATE_DISABLE | + DPUNIT_PIPEA_GATE_DISABLE | + DPCUNIT_CLOCK_GATE_DISABLE | + DPLSUNIT_CLOCK_GATE_DISABLE | + DPOUNIT_CLOCK_GATE_DISABLE | + DPIOUNIT_CLOCK_GATE_DISABLE); + + REG_WRITE(DSPCLK_GATE_D, reg_value); + + udelay(500); +} + +void +cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) +{ + struct gma_encoder *gma_encoder; + struct gma_connector *gma_connector; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct cdv_intel_dp *intel_dp; + const char *name = NULL; + int type = DRM_MODE_CONNECTOR_DisplayPort; + + gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); + if (!gma_encoder) + return; + gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); + if (!gma_connector) + goto err_connector; + intel_dp = kzalloc(sizeof(struct cdv_intel_dp), GFP_KERNEL); + if (!intel_dp) + goto err_priv; + + if ((output_reg == DP_C) && cdv_intel_dpc_is_edp(dev)) + type = DRM_MODE_CONNECTOR_eDP; + + connector = &gma_connector->base; + encoder = &gma_encoder->base; + + drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, type); + drm_encoder_init(dev, encoder, &cdv_intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS); + + gma_connector_attach_encoder(gma_connector, gma_encoder); + + if (type == DRM_MODE_CONNECTOR_DisplayPort) + gma_encoder->type = INTEL_OUTPUT_DISPLAYPORT; + else + gma_encoder->type = INTEL_OUTPUT_EDP; + + + gma_encoder->dev_priv=intel_dp; + intel_dp->encoder = gma_encoder; + intel_dp->output_reg = output_reg; + + drm_encoder_helper_add(encoder, &cdv_intel_dp_helper_funcs); + drm_connector_helper_add(connector, &cdv_intel_dp_connector_helper_funcs); + + connector->polled = DRM_CONNECTOR_POLL_HPD; + connector->interlace_allowed = false; + connector->doublescan_allowed = false; + + drm_sysfs_connector_add(connector); + + /* Set up the DDC bus. */ + switch (output_reg) { + case DP_B: + name = "DPDDC-B"; + gma_encoder->ddi_select = (DP_MASK | DDI0_SELECT); + break; + case DP_C: + name = "DPDDC-C"; + gma_encoder->ddi_select = (DP_MASK | DDI1_SELECT); + break; + } + + cdv_disable_intel_clock_gating(dev); + + cdv_intel_dp_i2c_init(gma_connector, gma_encoder, name); + /* FIXME:fail check */ + cdv_intel_dp_add_properties(connector); + + if (is_edp(gma_encoder)) { + int ret; + struct edp_power_seq cur; + u32 pp_on, pp_off, pp_div; + u32 pwm_ctrl; + + pp_on = REG_READ(PP_CONTROL); + pp_on &= ~PANEL_UNLOCK_MASK; + pp_on |= PANEL_UNLOCK_REGS; + + REG_WRITE(PP_CONTROL, pp_on); + + pwm_ctrl = REG_READ(BLC_PWM_CTL2); + pwm_ctrl |= PWM_PIPE_B; + REG_WRITE(BLC_PWM_CTL2, pwm_ctrl); + + pp_on = REG_READ(PP_ON_DELAYS); + pp_off = REG_READ(PP_OFF_DELAYS); + pp_div = REG_READ(PP_DIVISOR); + + /* Pull timing values out of registers */ + cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >> + PANEL_POWER_UP_DELAY_SHIFT; + + cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >> + PANEL_LIGHT_ON_DELAY_SHIFT; + + cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >> + PANEL_LIGHT_OFF_DELAY_SHIFT; + + cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >> + PANEL_POWER_DOWN_DELAY_SHIFT; + + cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >> + PANEL_POWER_CYCLE_DELAY_SHIFT); + + DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); + + + intel_dp->panel_power_up_delay = cur.t1_t3 / 10; + intel_dp->backlight_on_delay = cur.t8 / 10; + intel_dp->backlight_off_delay = cur.t9 / 10; + intel_dp->panel_power_down_delay = cur.t10 / 10; + intel_dp->panel_power_cycle_delay = (cur.t11_t12 - 1) * 100; + + DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", + intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, + intel_dp->panel_power_cycle_delay); + + DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", + intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); + + + cdv_intel_edp_panel_vdd_on(gma_encoder); + ret = cdv_intel_dp_aux_native_read(gma_encoder, DP_DPCD_REV, + intel_dp->dpcd, + sizeof(intel_dp->dpcd)); + cdv_intel_edp_panel_vdd_off(gma_encoder); + if (ret == 0) { + /* if this fails, presume the device is a ghost */ + DRM_INFO("failed to retrieve link info, disabling eDP\n"); + cdv_intel_dp_encoder_destroy(encoder); + cdv_intel_dp_destroy(connector); + goto err_priv; + } else { + DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n", + intel_dp->dpcd[0], intel_dp->dpcd[1], + intel_dp->dpcd[2], intel_dp->dpcd[3]); + + } + /* The CDV reference driver moves pnale backlight setup into the displays that + have a backlight: this is a good idea and one we should probably adopt, however + we need to migrate all the drivers before we can do that */ + /*cdv_intel_panel_setup_backlight(dev); */ + } + return; + +err_priv: + kfree(gma_connector); +err_connector: + kfree(gma_encoder); +} diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index de25560e629..b99084b3f70 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 */ @@ -63,11 +64,11 @@ static void cdv_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; - struct psb_intel_encoder *psb_intel_encoder = to_psb_intel_encoder(encoder); - struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv; + struct gma_encoder *gma_encoder = to_gma_encoder(encoder); + struct mid_intel_hdmi_priv *hdmi_priv = gma_encoder->dev_priv; u32 hdmib; struct drm_crtc *crtc = encoder->crtc; - struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); hdmib = (2 << 10); @@ -76,7 +77,7 @@ static void cdv_hdmi_mode_set(struct drm_encoder *encoder, if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) hdmib |= HDMI_HSYNC_ACTIVE_HIGH; - if (intel_crtc->pipe == 1) + if (gma_crtc->pipe == 1) hdmib |= HDMIB_PIPE_B_SELECT; if (hdmi_priv->has_hdmi_audio) { @@ -88,19 +89,11 @@ static void cdv_hdmi_mode_set(struct drm_encoder *encoder, REG_READ(hdmi_priv->hdmi_reg); } -static bool cdv_hdmi_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - static void cdv_hdmi_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; - struct psb_intel_encoder *psb_intel_encoder = - to_psb_intel_encoder(encoder); - struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv; + struct gma_encoder *gma_encoder = to_gma_encoder(encoder); + struct mid_intel_hdmi_priv *hdmi_priv = gma_encoder->dev_priv; u32 hdmib; hdmib = REG_READ(hdmi_priv->hdmi_reg); @@ -115,9 +108,8 @@ static void cdv_hdmi_dpms(struct drm_encoder *encoder, int mode) static void cdv_hdmi_save(struct drm_connector *connector) { struct drm_device *dev = connector->dev; - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); - struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv; + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + struct mid_intel_hdmi_priv *hdmi_priv = gma_encoder->dev_priv; hdmi_priv->save_HDMIB = REG_READ(hdmi_priv->hdmi_reg); } @@ -125,9 +117,8 @@ static void cdv_hdmi_save(struct drm_connector *connector) static void cdv_hdmi_restore(struct drm_connector *connector) { struct drm_device *dev = connector->dev; - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); - struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv; + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + struct mid_intel_hdmi_priv *hdmi_priv = gma_encoder->dev_priv; REG_WRITE(hdmi_priv->hdmi_reg, hdmi_priv->save_HDMIB); REG_READ(hdmi_priv->hdmi_reg); @@ -136,15 +127,12 @@ static void cdv_hdmi_restore(struct drm_connector *connector) static enum drm_connector_status cdv_hdmi_detect( struct drm_connector *connector, bool force) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); - struct psb_intel_connector *psb_intel_connector = - to_psb_intel_connector(connector); - struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv; + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + struct mid_intel_hdmi_priv *hdmi_priv = gma_encoder->dev_priv; struct edid *edid = NULL; enum drm_connector_status status = connector_status_disconnected; - edid = drm_get_edid(connector, &psb_intel_encoder->i2c_bus->adapter); + edid = drm_get_edid(connector, &gma_encoder->i2c_bus->adapter); hdmi_priv->has_hdmi_sink = false; hdmi_priv->has_hdmi_audio = false; @@ -156,8 +144,6 @@ static enum drm_connector_status cdv_hdmi_detect( hdmi_priv->has_hdmi_audio = drm_detect_monitor_audio(edid); } - - psb_intel_connector->base.display_info.raw_edid = NULL; kfree(edid); } return status; @@ -170,7 +156,7 @@ static int cdv_hdmi_set_property(struct drm_connector *connector, struct drm_encoder *encoder = connector->encoder; if (!strcmp(property->name, "scaling mode") && encoder) { - struct psb_intel_crtc *crtc = to_psb_intel_crtc(encoder->crtc); + struct gma_crtc *crtc = to_gma_crtc(encoder->crtc); bool centre; uint64_t curValue; @@ -188,14 +174,14 @@ static int cdv_hdmi_set_property(struct drm_connector *connector, return -1; } - if (drm_connector_property_get_value(connector, + if (drm_object_property_get_value(&connector->base, property, &curValue)) return -1; if (curValue == value) return 0; - if (drm_connector_property_set_value(connector, + if (drm_object_property_set_value(&connector->base, property, value)) return -1; @@ -206,7 +192,7 @@ static int cdv_hdmi_set_property(struct drm_connector *connector, crtc->saved_mode.vdisplay != 0) { if (centre) { if (!drm_crtc_helper_set_mode(encoder->crtc, &crtc->saved_mode, - encoder->crtc->x, encoder->crtc->y, encoder->crtc->fb)) + encoder->crtc->x, encoder->crtc->y, encoder->crtc->primary->fb)) return -1; } else { struct drm_encoder_helper_funcs *helpers @@ -224,12 +210,11 @@ static int cdv_hdmi_set_property(struct drm_connector *connector, */ static int cdv_hdmi_get_modes(struct drm_connector *connector) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); struct edid *edid = NULL; int ret = 0; - edid = drm_get_edid(connector, &psb_intel_encoder->i2c_bus->adapter); + edid = drm_get_edid(connector, &gma_encoder->i2c_bus->adapter); if (edid) { drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); @@ -241,8 +226,6 @@ static int cdv_hdmi_get_modes(struct drm_connector *connector) static int cdv_hdmi_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct drm_psb_private *dev_priv = connector->dev->dev_private; - if (mode->clock > 165000) return MODE_CLOCK_HIGH; if (mode->clock < 20000) @@ -256,21 +239,15 @@ static int cdv_hdmi_mode_valid(struct drm_connector *connector, if (mode->flags & DRM_MODE_FLAG_INTERLACE) return MODE_NO_INTERLACE; - /* We assume worst case scenario of 32 bpp here, since we don't know */ - if ((ALIGN(mode->hdisplay * 4, 64) * mode->vdisplay) > - dev_priv->vram_stolen_size) - return MODE_MEM; - return MODE_OK; } static void cdv_hdmi_destroy(struct drm_connector *connector) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); - if (psb_intel_encoder->i2c_bus) - psb_intel_i2c_destroy(psb_intel_encoder->i2c_bus); + if (gma_encoder->i2c_bus) + psb_intel_i2c_destroy(gma_encoder->i2c_bus); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); kfree(connector); @@ -278,17 +255,17 @@ static void cdv_hdmi_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs cdv_hdmi_helper_funcs = { .dpms = cdv_hdmi_dpms, - .mode_fixup = cdv_hdmi_mode_fixup, - .prepare = psb_intel_encoder_prepare, + .mode_fixup = gma_encoder_mode_fixup, + .prepare = gma_encoder_prepare, .mode_set = cdv_hdmi_mode_set, - .commit = psb_intel_encoder_commit, + .commit = gma_encoder_commit, }; static const struct drm_connector_helper_funcs cdv_hdmi_connector_helper_funcs = { .get_modes = cdv_hdmi_get_modes, .mode_valid = cdv_hdmi_mode_valid, - .best_encoder = psb_intel_best_encoder, + .best_encoder = gma_best_encoder, }; static const struct drm_connector_funcs cdv_hdmi_connector_funcs = { @@ -304,23 +281,22 @@ static const struct drm_connector_funcs cdv_hdmi_connector_funcs = { void cdv_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int reg) { - struct psb_intel_encoder *psb_intel_encoder; - struct psb_intel_connector *psb_intel_connector; + struct gma_encoder *gma_encoder; + struct gma_connector *gma_connector; struct drm_connector *connector; struct drm_encoder *encoder; struct mid_intel_hdmi_priv *hdmi_priv; int ddc_bus; - psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), - GFP_KERNEL); + gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); - if (!psb_intel_encoder) + if (!gma_encoder) return; - psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), + gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); - if (!psb_intel_connector) + if (!gma_connector) goto err_connector; hdmi_priv = kzalloc(sizeof(struct mid_intel_hdmi_priv), GFP_KERNEL); @@ -328,8 +304,9 @@ void cdv_hdmi_init(struct drm_device *dev, if (!hdmi_priv) goto err_priv; - connector = &psb_intel_connector->base; - encoder = &psb_intel_encoder->base; + connector = &gma_connector->base; + connector->polled = DRM_CONNECTOR_POLL_HPD; + encoder = &gma_encoder->base; drm_connector_init(dev, connector, &cdv_hdmi_connector_funcs, DRM_MODE_CONNECTOR_DVID); @@ -337,12 +314,11 @@ void cdv_hdmi_init(struct drm_device *dev, drm_encoder_init(dev, encoder, &psb_intel_lvds_enc_funcs, DRM_MODE_ENCODER_TMDS); - psb_intel_connector_attach_encoder(psb_intel_connector, - psb_intel_encoder); - psb_intel_encoder->type = INTEL_OUTPUT_HDMI; + gma_connector_attach_encoder(gma_connector, gma_encoder); + gma_encoder->type = INTEL_OUTPUT_HDMI; hdmi_priv->hdmi_reg = reg; hdmi_priv->has_hdmi_sink = false; - psb_intel_encoder->dev_priv = hdmi_priv; + gma_encoder->dev_priv = hdmi_priv; drm_encoder_helper_add(encoder, &cdv_hdmi_helper_funcs); drm_connector_helper_add(connector, @@ -351,16 +327,18 @@ void cdv_hdmi_init(struct drm_device *dev, connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_connector_attach_property(connector, + drm_object_attach_property(&connector->base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); switch (reg) { case SDVOB: ddc_bus = GPIOE; + gma_encoder->ddi_select = DDI0_SELECT; break; case SDVOC: ddc_bus = GPIOD; + gma_encoder->ddi_select = DDI1_SELECT; break; default: DRM_ERROR("unknown reg 0x%x for HDMI\n", reg); @@ -368,16 +346,15 @@ void cdv_hdmi_init(struct drm_device *dev, break; } - psb_intel_encoder->i2c_bus = psb_intel_i2c_create(dev, + gma_encoder->i2c_bus = psb_intel_i2c_create(dev, ddc_bus, (reg == SDVOB) ? "HDMIB" : "HDMIC"); - if (!psb_intel_encoder->i2c_bus) { + if (!gma_encoder->i2c_bus) { dev_err(dev->dev, "No ddc adapter available!\n"); goto failed_ddc; } - hdmi_priv->hdmi_i2c_adapter = - &(psb_intel_encoder->i2c_bus->adapter); + hdmi_priv->hdmi_i2c_adapter = &(gma_encoder->i2c_bus->adapter); hdmi_priv->dev = dev; drm_sysfs_connector_add(connector); return; @@ -386,7 +363,7 @@ failed_ddc: drm_encoder_cleanup(encoder); drm_connector_cleanup(connector); err_priv: - kfree(psb_intel_connector); + kfree(gma_connector); err_connector: - kfree(psb_intel_encoder); + kfree(gma_encoder); } diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index 50e744be985..8ecc920fc26 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,8 +269,8 @@ int cdv_intel_lvds_mode_valid(struct drm_connector *connector, return MODE_OK; } -bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, +static bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; @@ -354,6 +356,7 @@ static void cdv_intel_lvds_mode_set(struct drm_encoder *encoder, { struct drm_device *dev = encoder->dev; struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(encoder->crtc); u32 pfit_control; /* @@ -375,6 +378,8 @@ static void cdv_intel_lvds_mode_set(struct drm_encoder *encoder, else pfit_control = 0; + pfit_control |= gma_crtc->pipe << PFIT_PIPE_SHIFT; + if (dev_priv->lvds_dither) pfit_control |= PANEL_8TO6_DITHER_ENABLE; @@ -401,12 +406,11 @@ static int cdv_intel_lvds_get_modes(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; int ret; - ret = psb_intel_ddc_get_modes(connector, &psb_intel_encoder->i2c_bus->adapter); + ret = psb_intel_ddc_get_modes(connector, &gma_encoder->i2c_bus->adapter); if (ret) return ret; @@ -436,27 +440,25 @@ 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); + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); - if (psb_intel_encoder->i2c_bus) - psb_intel_i2c_destroy(psb_intel_encoder->i2c_bus); + if (gma_encoder->i2c_bus) + psb_intel_i2c_destroy(gma_encoder->i2c_bus); drm_sysfs_connector_remove(connector); drm_connector_cleanup(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) { struct drm_encoder *encoder = connector->encoder; if (!strcmp(property->name, "scaling mode") && encoder) { - struct psb_intel_crtc *crtc = - to_psb_intel_crtc(encoder->crtc); + struct gma_crtc *crtc = to_gma_crtc(encoder->crtc); uint64_t curValue; if (!crtc) @@ -473,7 +475,7 @@ int cdv_intel_lvds_set_property(struct drm_connector *connector, return -1; } - if (drm_connector_property_get_value(connector, + if (drm_object_property_get_value(&connector->base, property, &curValue)) return -1; @@ -481,7 +483,7 @@ int cdv_intel_lvds_set_property(struct drm_connector *connector, if (curValue == value) return 0; - if (drm_connector_property_set_value(connector, + if (drm_object_property_set_value(&connector->base, property, value)) return -1; @@ -492,24 +494,16 @@ int cdv_intel_lvds_set_property(struct drm_connector *connector, &crtc->saved_mode, encoder->crtc->x, encoder->crtc->y, - encoder->crtc->fb)) + encoder->crtc->primary->fb)) return -1; } } else if (!strcmp(property->name, "backlight") && encoder) { - if (drm_connector_property_set_value(connector, + if (drm_object_property_set_value(&connector->base, property, value)) return -1; - else { -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct drm_psb_private *dev_priv = - encoder->dev->dev_private; - struct backlight_device *bd = - dev_priv->backlight_device; - bd->props.brightness = value; - backlight_update_status(bd); -#endif - } + else + gma_backlight_set(encoder->dev, value); } else if (!strcmp(property->name, "DPMS") && encoder) { struct drm_encoder_helper_funcs *helpers = encoder->helper_private; @@ -531,7 +525,7 @@ static const struct drm_connector_helper_funcs cdv_intel_lvds_connector_helper_funcs = { .get_modes = cdv_intel_lvds_get_modes, .mode_valid = cdv_intel_lvds_mode_valid, - .best_encoder = psb_intel_best_encoder, + .best_encoder = gma_best_encoder, }; static const struct drm_connector_funcs cdv_intel_lvds_connector_funcs = { @@ -550,10 +544,60 @@ static void cdv_intel_lvds_enc_destroy(struct drm_encoder *encoder) drm_encoder_cleanup(encoder); } -const struct drm_encoder_funcs cdv_intel_lvds_enc_funcs = { +static const struct drm_encoder_funcs cdv_intel_lvds_enc_funcs = { .destroy = cdv_intel_lvds_enc_destroy, }; +/* + * Enumerate the child dev array parsed from VBT to check whether + * the LVDS is present. + * If it is present, return 1. + * If it is not present, return false. + * If no child dev is parsed from VBT, it assumes that the LVDS is present. + */ +static bool lvds_is_present_in_vbt(struct drm_device *dev, + u8 *i2c_pin) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + int i; + + if (!dev_priv->child_dev_num) + return true; + + for (i = 0; i < dev_priv->child_dev_num; i++) { + struct child_device_config *child = dev_priv->child_dev + i; + + /* If the device type is not LFP, continue. + * We have to check both the new identifiers as well as the + * old for compatibility with some BIOSes. + */ + if (child->device_type != DEVICE_TYPE_INT_LFP && + child->device_type != DEVICE_TYPE_LFP) + continue; + + if (child->i2c_pin) + *i2c_pin = child->i2c_pin; + + /* However, we cannot trust the BIOS writers to populate + * the VBT correctly. Since LVDS requires additional + * information from AIM blocks, a non-zero addin offset is + * a good indicator that the LVDS is actually present. + */ + if (child->addin_offset) + return true; + + /* But even then some BIOS writers perform some black magic + * and instantiate the device without reference to any + * additional data. Trust that if the VBT was written into + * the OpRegion then they have validated the LVDS's existence. + */ + if (dev_priv->opregion.vbt) + return true; + } + + return false; +} + /** * cdv_intel_lvds_init - setup LVDS connectors on this device * @dev: drm device @@ -564,8 +608,8 @@ const struct drm_encoder_funcs cdv_intel_lvds_enc_funcs = { void cdv_intel_lvds_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev) { - struct psb_intel_encoder *psb_intel_encoder; - struct psb_intel_connector *psb_intel_connector; + struct gma_encoder *gma_encoder; + struct gma_connector *gma_connector; struct cdv_intel_lvds_priv *lvds_priv; struct drm_connector *connector; struct drm_encoder *encoder; @@ -574,25 +618,32 @@ void cdv_intel_lvds_init(struct drm_device *dev, struct drm_psb_private *dev_priv = dev->dev_private; u32 lvds; int pipe; + u8 pin; + + pin = GMBUS_PORT_PANEL; + if (!lvds_is_present_in_vbt(dev, &pin)) { + DRM_DEBUG_KMS("LVDS is not present in VBT\n"); + return; + } - psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), + gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); - if (!psb_intel_encoder) + if (!gma_encoder) return; - psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), + gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); - if (!psb_intel_connector) + if (!gma_connector) goto failed_connector; lvds_priv = kzalloc(sizeof(struct cdv_intel_lvds_priv), GFP_KERNEL); if (!lvds_priv) goto failed_lvds_priv; - psb_intel_encoder->dev_priv = lvds_priv; + gma_encoder->dev_priv = lvds_priv; - connector = &psb_intel_connector->base; - encoder = &psb_intel_encoder->base; + connector = &gma_connector->base; + encoder = &gma_encoder->base; drm_connector_init(dev, connector, @@ -604,9 +655,8 @@ void cdv_intel_lvds_init(struct drm_device *dev, DRM_MODE_ENCODER_LVDS); - psb_intel_connector_attach_encoder(psb_intel_connector, - psb_intel_encoder); - psb_intel_encoder->type = INTEL_OUTPUT_LVDS; + gma_connector_attach_encoder(gma_connector, gma_encoder); + gma_encoder->type = INTEL_OUTPUT_LVDS; drm_encoder_helper_add(encoder, &cdv_intel_lvds_helper_funcs); drm_connector_helper_add(connector, @@ -616,10 +666,10 @@ void cdv_intel_lvds_init(struct drm_device *dev, connector->doublescan_allowed = false; /*Attach connector properties*/ - drm_connector_attach_property(connector, + drm_object_attach_property(&connector->base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); - drm_connector_attach_property(connector, + drm_object_attach_property(&connector->base, dev_priv->backlight_property, BRIGHTNESS_MAX_LEVEL); @@ -627,16 +677,16 @@ void cdv_intel_lvds_init(struct drm_device *dev, * Set up I2C bus * FIXME: distroy i2c_bus when exit */ - psb_intel_encoder->i2c_bus = psb_intel_i2c_create(dev, + gma_encoder->i2c_bus = psb_intel_i2c_create(dev, GPIOB, "LVDSBLC_B"); - if (!psb_intel_encoder->i2c_bus) { + if (!gma_encoder->i2c_bus) { dev_printk(KERN_ERR, &dev->pdev->dev, "I2C bus registration failed.\n"); goto failed_blc_i2c; } - psb_intel_encoder->i2c_bus->slave_addr = 0x2C; - dev_priv->lvds_i2c_bus = psb_intel_encoder->i2c_bus; + gma_encoder->i2c_bus->slave_addr = 0x2C; + dev_priv->lvds_i2c_bus = gma_encoder->i2c_bus; /* * LVDS discovery: @@ -649,10 +699,10 @@ void cdv_intel_lvds_init(struct drm_device *dev, */ /* Set up the DDC bus. */ - psb_intel_encoder->ddc_bus = psb_intel_i2c_create(dev, + gma_encoder->ddc_bus = psb_intel_i2c_create(dev, GPIOC, "LVDSDDC_C"); - if (!psb_intel_encoder->ddc_bus) { + if (!gma_encoder->ddc_bus) { dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " "failed.\n"); goto failed_ddc; @@ -662,8 +712,9 @@ void cdv_intel_lvds_init(struct drm_device *dev, * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ + mutex_lock(&dev->mode_config.mutex); psb_intel_ddc_get_modes(connector, - &psb_intel_encoder->ddc_bus->adapter); + &gma_encoder->ddc_bus->adapter); list_for_each_entry(scan, &connector->probed_modes, head) { if (scan->type & DRM_MODE_TYPE_PREFERRED) { mode_dev->panel_fixed_mode = @@ -708,25 +759,40 @@ void cdv_intel_lvds_init(struct drm_device *dev, goto failed_find; } + /* setup PWM */ + { + u32 pwm; + + pwm = REG_READ(BLC_PWM_CTL2); + if (pipe == 1) + pwm |= PWM_PIPE_B; + else + pwm &= ~PWM_PIPE_B; + pwm |= PWM_ENABLE; + REG_WRITE(BLC_PWM_CTL2, pwm); + } + out: + mutex_unlock(&dev->mode_config.mutex); drm_sysfs_connector_add(connector); return; failed_find: + mutex_unlock(&dev->mode_config.mutex); printk(KERN_ERR "Failed find\n"); - if (psb_intel_encoder->ddc_bus) - psb_intel_i2c_destroy(psb_intel_encoder->ddc_bus); + if (gma_encoder->ddc_bus) + psb_intel_i2c_destroy(gma_encoder->ddc_bus); failed_ddc: printk(KERN_ERR "Failed DDC\n"); - if (psb_intel_encoder->i2c_bus) - psb_intel_i2c_destroy(psb_intel_encoder->i2c_bus); + if (gma_encoder->i2c_bus) + psb_intel_i2c_destroy(gma_encoder->i2c_bus); failed_blc_i2c: printk(KERN_ERR "Failed BLC\n"); drm_encoder_cleanup(encoder); drm_connector_cleanup(connector); kfree(lvds_priv); failed_lvds_priv: - kfree(psb_intel_connector); + kfree(gma_connector); failed_connector: - kfree(psb_intel_encoder); + kfree(gma_encoder); } diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 830dfdd6bf1..e7fcc148f33 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; @@ -154,11 +121,11 @@ static int psbfb_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) unsigned long address; int ret; unsigned long pfn; - /* FIXME: assumes fb at stolen base which may not be true */ - unsigned long phys_addr = (unsigned long)dev_priv->stolen_base; + unsigned long phys_addr = (unsigned long)dev_priv->stolen_base + + psbfb->gtt->offset; 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); @@ -186,7 +153,7 @@ static void psbfb_vm_close(struct vm_area_struct *vma) { } -static struct vm_operations_struct psbfb_vm_ops = { +static const struct vm_operations_struct psbfb_vm_ops = { .fault = psbfb_vm_fault, .open = psbfb_vm_open, .close = psbfb_vm_close @@ -211,8 +178,7 @@ static int psbfb_mmap(struct fb_info *info, struct vm_area_struct *vma) */ vma->vm_ops = &psbfb_vm_ops; vma->vm_private_data = (void *)psbfb; - vma->vm_flags |= VM_RESERVED | VM_IO | - VM_MIXEDMAP | VM_DONTEXPAND; + vma->vm_flags |= VM_IO | VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTDUMP; return 0; } @@ -247,7 +213,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, }; @@ -295,13 +260,13 @@ static int psb_framebuffer_init(struct drm_device *dev, default: return -EINVAL; } + drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); + fb->gtt = gt; ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs); if (ret) { dev_err(dev->dev, "framebuffer init failed: %d\n", ret); return ret; } - drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); - fb->gtt = gt; return 0; } @@ -354,12 +319,10 @@ static struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size) { struct gtt_range *backing; /* Begin by trying to use stolen memory backing */ - backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1); + backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1, PAGE_SIZE); if (backing) { - if (drm_gem_private_object_init(dev, - &backing->gem, aligned_size) == 0) - return backing; - psb_gtt_free_range(dev, backing); + drm_gem_private_object_init(dev, &backing->gem, aligned_size); + return backing; } return NULL; } @@ -391,6 +354,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 +367,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); @@ -442,6 +405,8 @@ static int psbfb_create(struct psb_fbdev *fbdev, return -ENOMEM; } + memset(dev_priv->vram_addr + backing->offset, 0, size); + mutex_lock(&dev->struct_mutex); info = framebuffer_alloc(0, device); @@ -463,7 +428,8 @@ static int psbfb_create(struct psb_fbdev *fbdev, fbdev->psb_fb_helper.fb = fb; fbdev->psb_fb_helper.fbdev = info; - strcpy(info->fix.id, "psbfb"); + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); + strcpy(info->fix.id, "psbdrmfb"); info->flags = FBINFO_DEFAULT; if (dev_priv->ops->accel_2d && pitch_lines > 8) /* 2D engine */ @@ -486,8 +452,7 @@ static int psbfb_create(struct psb_fbdev *fbdev, info->fix.ypanstep = 0; /* Accessed stolen memory directly */ - info->screen_base = (char *)dev_priv->vram_addr + - backing->offset; + info->screen_base = dev_priv->vram_addr + backing->offset; info->screen_size = size; if (dev_priv->gtt.stolen_size) { @@ -500,20 +465,15 @@ 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", + dev_dbg(dev->dev, "allocated %dx%d fb\n", psbfb->base.width, psbfb->base.height); mutex_unlock(&dev->struct_mutex); @@ -560,36 +520,54 @@ 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 gma_crtc *gma_crtc = to_gma_crtc(crtc); + + gma_crtc->lut_r[regno] = red >> 8; + gma_crtc->lut_g[regno] = green >> 8; + gma_crtc->lut_b[regno] = blue >> 8; } static void psbfb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, int regno) { + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + + *red = gma_crtc->lut_r[regno] << 8; + *green = gma_crtc->lut_g[regno] << 8; + *blue = gma_crtc->lut_b[regno] << 8; } static int psbfb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { struct psb_fbdev *psb_fbdev = (struct psb_fbdev *)helper; - int new_fb = 0; - int ret; - - if (!helper->fb) { - ret = psbfb_create(psb_fbdev, sizes); - if (ret) - return ret; - new_fb = 1; - } - return new_fb; + struct drm_device *dev = psb_fbdev->psb_fb_helper.dev; + struct drm_psb_private *dev_priv = dev->dev_private; + int bytespp; + + bytespp = sizes->surface_bpp / 8; + if (bytespp == 3) /* no 24bit packed */ + bytespp = 4; + + /* If the mode will not fit in 32bit then switch to 16bit to get + a console on full resolution. The X mode setting server will + allocate its own 32bit GEM framebuffer */ + if (ALIGN(sizes->fb_width * bytespp, 64) * sizes->fb_height > + dev_priv->vram_stolen_size) { + sizes->surface_bpp = 16; + sizes->surface_depth = 16; + } + + return psbfb_create(psb_fbdev, sizes); } -struct drm_fb_helper_funcs psb_fb_helper_funcs = { +static struct drm_fb_helper_funcs psb_fb_helper_funcs = { .gamma_set = psbfb_gamma_set, .gamma_get = psbfb_gamma_get, .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; @@ -602,6 +580,7 @@ int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev) framebuffer_release(info); } drm_fb_helper_fini(&fbdev->psb_fb_helper); + drm_framebuffer_unregister_private(&psbfb->base); drm_framebuffer_cleanup(&psbfb->base); if (psbfb->gtt) @@ -627,11 +606,15 @@ int psb_fbdev_init(struct drm_device *dev) INTELFB_CONN_LIMIT); drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32); 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; @@ -680,30 +663,6 @@ static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb) { struct psb_framebuffer *psbfb = to_psb_fb(fb); struct gtt_range *r = psbfb->gtt; - struct drm_device *dev = fb->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_fbdev *fbdev = dev_priv->fbdev; - struct drm_crtc *crtc; - int reset = 0; - - /* Should never get stolen memory for a user fb */ - WARN_ON(r->stolen); - - /* Check if we are erroneously live */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) - if (crtc->fb == fb) - reset = 1; - - if (reset) - /* - * Now force a sane response before we permit the DRM CRTC - * layer to do stupid things like blank the display. Instead - * we reset this framebuffer as if the user had forced a reset. - * We must do this before the cleanup so that the DRM layer - * doesn't get a chance to stick its oar in where it isn't - * wanted. - */ - drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper); /* Let DRM do its clean up */ drm_framebuffer_cleanup(fb); @@ -725,10 +684,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; @@ -747,26 +703,22 @@ static void psb_setup_outputs(struct drm_device *dev) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); - struct drm_encoder *encoder = &psb_intel_encoder->base; + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + struct drm_encoder *encoder = &gma_encoder->base; int crtc_mask = 0, clone_mask = 0; /* valid crtcs */ - switch (psb_intel_encoder->type) { + switch (gma_encoder->type) { case INTEL_OUTPUT_ANALOG: crtc_mask = (1 << 0); clone_mask = (1 << INTEL_OUTPUT_ANALOG); break; case INTEL_OUTPUT_SDVO: - crtc_mask = ((1 << 0) | (1 << 1)); + crtc_mask = dev_priv->ops->sdvo_mask; clone_mask = (1 << INTEL_OUTPUT_SDVO); break; case INTEL_OUTPUT_LVDS: - if (IS_MRST(dev)) - crtc_mask = (1 << 0); - else - crtc_mask = (1 << 1); + crtc_mask = dev_priv->ops->lvds_mask; clone_mask = (1 << INTEL_OUTPUT_LVDS); break; case INTEL_OUTPUT_MIPI: @@ -778,16 +730,20 @@ static void psb_setup_outputs(struct drm_device *dev) clone_mask = (1 << INTEL_OUTPUT_MIPI2); break; case INTEL_OUTPUT_HDMI: - if (IS_MFLD(dev)) - crtc_mask = (1 << 1); - else - crtc_mask = (1 << 0); + crtc_mask = dev_priv->ops->hdmi_mask; clone_mask = (1 << INTEL_OUTPUT_HDMI); break; + case INTEL_OUTPUT_DISPLAYPORT: + crtc_mask = (1 << 0) | (1 << 1); + clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT); + break; + case INTEL_OUTPUT_EDP: + crtc_mask = (1 << 1); + clone_mask = (1 << INTEL_OUTPUT_EDP); } encoder->possible_crtcs = crtc_mask; encoder->possible_clones = - psb_intel_connector_clones(dev, clone_mask); + gma_connector_clones(dev, clone_mask); } } @@ -802,7 +758,7 @@ void psb_modeset_init(struct drm_device *dev) dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; - dev->mode_config.funcs = (void *) &psb_mode_funcs; + dev->mode_config.funcs = &psb_mode_funcs; /* set memory base */ /* Oaktrail and Poulsbo should use BAR 2*/ @@ -813,19 +769,27 @@ void psb_modeset_init(struct drm_device *dev) for (i = 0; i < dev_priv->num_pipe; i++) psb_intel_crtc_init(dev, i, mode_dev); - dev->mode_config.max_width = 2048; - dev->mode_config.max_height = 2048; + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; psb_setup_outputs(dev); + + if (dev_priv->ops->errata) + dev_priv->ops->errata(dev); + + dev_priv->modeset = true; } void psb_modeset_cleanup(struct drm_device *dev) { - mutex_lock(&dev->struct_mutex); + struct drm_psb_private *dev_priv = dev->dev_private; + if (dev_priv->modeset) { + mutex_lock(&dev->struct_mutex); - drm_kms_helper_poll_fini(dev); - psb_fbdev_fini(dev); - drm_mode_config_cleanup(dev); + drm_kms_helper_poll_fini(dev); + psb_fbdev_fini(dev); + drm_mode_config_cleanup(dev); - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev->struct_mutex); + } } diff --git a/drivers/gpu/drm/gma500/framebuffer.h b/drivers/gpu/drm/gma500/framebuffer.h index 989558a9e6e..395f20b07aa 100644 --- a/drivers/gpu/drm/gma500/framebuffer.h +++ b/drivers/gpu/drm/gma500/framebuffer.h @@ -41,7 +41,7 @@ struct psb_fbdev { #define to_psb_fb(x) container_of(x, struct psb_framebuffer, base) -extern int psb_intel_connector_clones(struct drm_device *dev, int type_mask); +extern int gma_connector_clones(struct drm_device *dev, int type_mask); #endif diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index 9fbb86868e2..c707fa6fca8 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -25,18 +25,18 @@ #include <drm/drmP.h> #include <drm/drm.h> -#include "gma_drm.h" +#include <drm/gma_drm.h> +#include <drm/drm_vma_manager.h> #include "psb_drv.h" -int psb_gem_init_object(struct drm_gem_object *obj) -{ - return -EINVAL; -} - void psb_gem_free_object(struct drm_gem_object *obj) { struct gtt_range *gtt = container_of(obj, struct gtt_range, gem); - drm_gem_object_release_wrap(obj); + + /* Remove the list map if one is present */ + drm_gem_free_mmap_offset(obj); + drm_gem_object_release(obj); + /* This must occur last as it frees up the memory of the GEM object */ psb_gtt_free_range(obj->dev, gtt); } @@ -62,9 +62,6 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, int ret = 0; struct drm_gem_object *obj; - if (!(dev->driver->driver_features & DRIVER_GEM)) - return -ENODEV; - mutex_lock(&dev->struct_mutex); /* GEM does all our handle to object mapping */ @@ -76,13 +73,10 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, /* What validation is needed here ? */ /* Make it mmapable */ - if (!obj->map_list.map) { - ret = gem_create_mmap_offset(obj); - if (ret) - goto out; - } - /* GEM should really work out the hash offsets for us */ - *offset = (u64)obj->map_list.hash.key << PAGE_SHIFT; + ret = drm_gem_create_mmap_offset(obj); + if (ret) + goto out; + *offset = drm_vma_node_offset_addr(&obj->vma_node); out: drm_gem_object_unreference(obj); unlock: @@ -101,8 +95,8 @@ unlock: * it so that userspace can speak about it. This does the core work * for the various methods that do/will create GEM objects for things */ -static int psb_gem_create(struct drm_file *file, - struct drm_device *dev, uint64_t size, uint32_t *handlep) +int psb_gem_create(struct drm_file *file, struct drm_device *dev, u64 size, + u32 *handlep, int stolen, u32 align) { struct gtt_range *r; int ret; @@ -112,7 +106,7 @@ static int psb_gem_create(struct drm_file *file, /* Allocate our object - for now a direct gtt range which is not stolen memory backed */ - r = psb_gtt_alloc_range(dev, size, "gem", 0); + r = psb_gtt_alloc_range(dev, size, "gem", 0, PAGE_SIZE); if (r == NULL) { dev_err(dev->dev, "no memory for %lld byte GEM object\n", size); return -ENOSPC; @@ -124,6 +118,8 @@ static int psb_gem_create(struct drm_file *file, dev_err(dev->dev, "GEM init failed for %lld\n", size); return -ENOMEM; } + /* Limit the object to 32bit mappings */ + mapping_set_gfp_mask(r->gem.filp->f_mapping, GFP_KERNEL | __GFP_DMA32); /* Give the object a handle so we can carry it more easily */ ret = drm_gem_handle_create(file, &r->gem, &handle); if (ret) { @@ -154,24 +150,8 @@ int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, { args->pitch = ALIGN(args->width * ((args->bpp + 7) / 8), 64); args->size = args->pitch * args->height; - return psb_gem_create(file, dev, args->size, &args->handle); -} - -/** - * psb_gem_dumb_destroy - destroy a dumb buffer - * @file: client file - * @dev: our DRM device - * @handle: the object handle - * - * Destroy a handle that was created via psb_gem_dumb_create, at least - * we hope it was created that way. i915 seems to assume the caller - * does the checking but that might be worth review ! FIXME - */ -int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, - uint32_t handle) -{ - /* No special work needed, drop the reference and see what falls out */ - return drm_gem_handle_delete(file, handle); + return psb_gem_create(file, dev, args->size, &args->handle, 0, + PAGE_SIZE); } /** @@ -247,46 +227,3 @@ fail: return VM_FAULT_SIGBUS; } } - -static int psb_gem_create_stolen(struct drm_file *file, struct drm_device *dev, - int size, u32 *handle) -{ - struct gtt_range *gtt = psb_gtt_alloc_range(dev, size, "gem", 1); - if (gtt == NULL) - return -ENOMEM; - if (drm_gem_private_object_init(dev, >t->gem, size) != 0) - goto free_gtt; - if (drm_gem_handle_create(file, >t->gem, handle) == 0) - return 0; -free_gtt: - psb_gtt_free_range(dev, gtt); - return -ENOMEM; -} - -/* - * GEM interfaces for our specific client - */ -int psb_gem_create_ioctl(struct drm_device *dev, void *data, - struct drm_file *file) -{ - struct drm_psb_gem_create *args = data; - int ret; - if (args->flags & GMA_GEM_CREATE_STOLEN) { - ret = psb_gem_create_stolen(file, dev, args->size, - &args->handle); - if (ret == 0) - return 0; - /* Fall throguh */ - args->flags &= ~GMA_GEM_CREATE_STOLEN; - } - return psb_gem_create(file, dev, args->size, &args->handle); -} - -int psb_gem_mmap_ioctl(struct drm_device *dev, void *data, - struct drm_file *file) -{ - struct drm_psb_gem_mmap *args = data; - return dev->driver->dumb_map_offset(file, dev, - args->handle, &args->offset); -} - diff --git a/drivers/gpu/drm/gma500/gem.h b/drivers/gpu/drm/gma500/gem.h new file mode 100644 index 00000000000..1381c5190f4 --- /dev/null +++ b/drivers/gpu/drm/gma500/gem.h @@ -0,0 +1,21 @@ +/************************************************************************** + * Copyright (c) 2014 Patrik Jakobsson + * 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. + * + **************************************************************************/ + +#ifndef _GEM_H +#define _GEM_H + +extern int psb_gem_create(struct drm_file *file, struct drm_device *dev, + u64 size, u32 *handlep, int stolen, u32 align); +#endif diff --git a/drivers/gpu/drm/gma500/gem_glue.c b/drivers/gpu/drm/gma500/gem_glue.c deleted file mode 100644 index daac1212065..00000000000 --- a/drivers/gpu/drm/gma500/gem_glue.c +++ /dev/null @@ -1,89 +0,0 @@ -/************************************************************************** - * 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 <drm/drmP.h> -#include <drm/drm.h> - -void drm_gem_object_release_wrap(struct drm_gem_object *obj) -{ - /* Remove the list map if one is present */ - if (obj->map_list.map) { - struct drm_gem_mm *mm = obj->dev->mm_private; - struct drm_map_list *list = &obj->map_list; - drm_ht_remove_item(&mm->offset_hash, &list->hash); - drm_mm_put_block(list->file_offset_node); - kfree(list->map); - list->map = NULL; - } - drm_gem_object_release(obj); -} - -/** - * gem_create_mmap_offset - invent an mmap offset - * @obj: our object - * - * Standard implementation of offset generation for mmap as is - * duplicated in several drivers. This belongs in GEM. - */ -int gem_create_mmap_offset(struct drm_gem_object *obj) -{ - struct drm_device *dev = obj->dev; - struct drm_gem_mm *mm = dev->mm_private; - struct drm_map_list *list; - struct drm_local_map *map; - int ret; - - list = &obj->map_list; - list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL); - if (list->map == NULL) - return -ENOMEM; - map = list->map; - map->type = _DRM_GEM; - map->size = obj->size; - map->handle = obj; - - list->file_offset_node = drm_mm_search_free(&mm->offset_manager, - obj->size / PAGE_SIZE, 0, 0); - if (!list->file_offset_node) { - dev_err(dev->dev, "failed to allocate offset for bo %d\n", - obj->name); - ret = -ENOSPC; - goto free_it; - } - list->file_offset_node = drm_mm_get_block(list->file_offset_node, - obj->size / PAGE_SIZE, 0); - if (!list->file_offset_node) { - ret = -ENOMEM; - goto free_it; - } - list->hash.key = list->file_offset_node->start; - ret = drm_ht_insert_item(&mm->offset_hash, &list->hash); - if (ret) { - dev_err(dev->dev, "failed to add to map hash\n"); - goto free_mm; - } - return 0; - -free_mm: - drm_mm_put_block(list->file_offset_node); -free_it: - kfree(list->map); - list->map = NULL; - return ret; -} diff --git a/drivers/gpu/drm/gma500/gem_glue.h b/drivers/gpu/drm/gma500/gem_glue.h deleted file mode 100644 index ce5ce30f74d..00000000000 --- a/drivers/gpu/drm/gma500/gem_glue.h +++ /dev/null @@ -1,2 +0,0 @@ -extern void drm_gem_object_release_wrap(struct drm_gem_object *obj); -extern int gem_create_mmap_offset(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/gma500/gma_device.c b/drivers/gpu/drm/gma500/gma_device.c new file mode 100644 index 00000000000..4a295f9ba06 --- /dev/null +++ b/drivers/gpu/drm/gma500/gma_device.c @@ -0,0 +1,56 @@ +/************************************************************************** + * 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. + * + **************************************************************************/ + +#include <drm/drmP.h> +#include "psb_drv.h" + +void gma_get_core_freq(struct drm_device *dev) +{ + uint32_t clock; + struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); + struct drm_psb_private *dev_priv = dev->dev_private; + + /*pci_write_config_dword(pci_root, 0xD4, 0x00C32004);*/ + /*pci_write_config_dword(pci_root, 0xD0, 0xE0033000);*/ + + pci_write_config_dword(pci_root, 0xD0, 0xD0050300); + pci_read_config_dword(pci_root, 0xD4, &clock); + pci_dev_put(pci_root); + + switch (clock & 0x07) { + case 0: + dev_priv->core_freq = 100; + break; + case 1: + dev_priv->core_freq = 133; + break; + case 2: + dev_priv->core_freq = 150; + break; + case 3: + dev_priv->core_freq = 178; + break; + case 4: + dev_priv->core_freq = 200; + break; + case 5: + case 6: + case 7: + dev_priv->core_freq = 266; + break; + default: + dev_priv->core_freq = 0; + } +} diff --git a/drivers/gpu/drm/gma500/gma_device.h b/drivers/gpu/drm/gma500/gma_device.h new file mode 100644 index 00000000000..e1dbb007b82 --- /dev/null +++ b/drivers/gpu/drm/gma500/gma_device.h @@ -0,0 +1,21 @@ +/************************************************************************** + * 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. + * + **************************************************************************/ + +#ifndef _GMA_DEVICE_H +#define _GMA_DEVICE_H + +extern void gma_get_core_freq(struct drm_device *dev); + +#endif diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c new file mode 100644 index 00000000000..9bb9bddd881 --- /dev/null +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -0,0 +1,791 @@ +/* + * Copyright © 2006-2011 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> + * Patrik Jakobsson <patrik.r.jakobsson@gmail.com> + */ + +#include <drm/drmP.h> +#include "gma_display.h" +#include "psb_intel_drv.h" +#include "psb_intel_reg.h" +#include "psb_drv.h" +#include "framebuffer.h" + +/** + * Returns whether any output on the specified pipe is of the specified type + */ +bool gma_pipe_has_type(struct drm_crtc *crtc, int type) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_connector *l_entry; + + list_for_each_entry(l_entry, &mode_config->connector_list, head) { + if (l_entry->encoder && l_entry->encoder->crtc == crtc) { + struct gma_encoder *gma_encoder = + gma_attached_encoder(l_entry); + if (gma_encoder->type == type) + return true; + } + } + + return false; +} + +void gma_wait_for_vblank(struct drm_device *dev) +{ + /* Wait for 20ms, i.e. one cycle at 50hz. */ + mdelay(20); +} + +int gma_pipe_set_base(struct drm_crtc *crtc, 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 gma_crtc *gma_crtc = to_gma_crtc(crtc); + struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb); + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + unsigned long start, offset; + u32 dspcntr; + int ret = 0; + + if (!gma_power_begin(dev, true)) + return 0; + + /* no fb bound */ + if (!crtc->primary->fb) { + dev_err(dev->dev, "No FB bound\n"); + goto gma_pipe_cleaner; + } + + /* We are displaying this buffer, make sure it is actually loaded + into the GTT */ + ret = psb_gtt_pin(psbfb->gtt); + if (ret < 0) + goto gma_pipe_set_base_exit; + start = psbfb->gtt->offset; + offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8); + + REG_WRITE(map->stride, crtc->primary->fb->pitches[0]); + + dspcntr = REG_READ(map->cntr); + dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + + switch (crtc->primary->fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (crtc->primary->fb->depth == 15) + dspcntr |= DISPPLANE_15_16BPP; + else + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + break; + default: + dev_err(dev->dev, "Unknown color depth\n"); + ret = -EINVAL; + goto gma_pipe_set_base_exit; + } + REG_WRITE(map->cntr, dspcntr); + + dev_dbg(dev->dev, + "Writing base %08lX %08lX %d %d\n", start, offset, x, y); + + /* FIXME: Investigate whether this really is the base for psb and why + the linear offset is named base for the other chips. map->surf + should be the base and map->linoff the offset for all chips */ + if (IS_PSB(dev)) { + REG_WRITE(map->base, offset + start); + REG_READ(map->base); + } else { + REG_WRITE(map->base, offset); + REG_READ(map->base); + REG_WRITE(map->surf, start); + REG_READ(map->surf); + } + +gma_pipe_cleaner: + /* If there was a previous display we can now unpin it */ + if (old_fb) + psb_gtt_unpin(to_psb_fb(old_fb)->gtt); + +gma_pipe_set_base_exit: + gma_power_end(dev); + return ret; +} + +/* Loads the palette/gamma unit for the CRTC with the prepared values */ +void gma_crtc_load_lut(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + const struct psb_offset *map = &dev_priv->regmap[gma_crtc->pipe]; + int palreg = map->palette; + int i; + + /* The clocks have to be on to load the palette. */ + if (!crtc->enabled) + return; + + if (gma_power_begin(dev, false)) { + for (i = 0; i < 256; i++) { + REG_WRITE(palreg + 4 * i, + ((gma_crtc->lut_r[i] + + gma_crtc->lut_adj[i]) << 16) | + ((gma_crtc->lut_g[i] + + gma_crtc->lut_adj[i]) << 8) | + (gma_crtc->lut_b[i] + + gma_crtc->lut_adj[i])); + } + gma_power_end(dev); + } else { + for (i = 0; i < 256; i++) { + /* FIXME: Why pipe[0] and not pipe[..._crtc->pipe]? */ + dev_priv->regs.pipe[0].palette[i] = + ((gma_crtc->lut_r[i] + + gma_crtc->lut_adj[i]) << 16) | + ((gma_crtc->lut_g[i] + + gma_crtc->lut_adj[i]) << 8) | + (gma_crtc->lut_b[i] + + gma_crtc->lut_adj[i]); + } + + } +} + +void gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, + u32 start, u32 size) +{ + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int i; + int end = (start + size > 256) ? 256 : start + size; + + for (i = start; i < end; i++) { + gma_crtc->lut_r[i] = red[i] >> 8; + gma_crtc->lut_g[i] = green[i] >> 8; + gma_crtc->lut_b[i] = blue[i] >> 8; + } + + gma_crtc_load_lut(crtc); +} + +/** + * 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. + */ +void gma_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + u32 temp; + + /* 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. + */ + + if (IS_CDV(dev)) + dev_priv->ops->disable_sr(dev); + + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + if (gma_crtc->active) + break; + + gma_crtc->active = true; + + /* Enable the DPLL */ + temp = REG_READ(map->dpll); + if ((temp & DPLL_VCO_ENABLE) == 0) { + REG_WRITE(map->dpll, temp); + REG_READ(map->dpll); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); + REG_READ(map->dpll); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); + REG_READ(map->dpll); + /* Wait for the clocks to stabilize. */ + udelay(150); + } + + /* Enable the plane */ + temp = REG_READ(map->cntr); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + REG_WRITE(map->cntr, + temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(map->base, REG_READ(map->base)); + } + + udelay(150); + + /* Enable the pipe */ + temp = REG_READ(map->conf); + if ((temp & PIPEACONF_ENABLE) == 0) + REG_WRITE(map->conf, temp | PIPEACONF_ENABLE); + + temp = REG_READ(map->status); + temp &= ~(0xFFFF); + temp |= PIPE_FIFO_UNDERRUN; + REG_WRITE(map->status, temp); + REG_READ(map->status); + + gma_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: + if (!gma_crtc->active) + break; + + gma_crtc->active = false; + + /* Give the overlay scaler a chance to disable + * if it's on this pipe */ + /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ + + /* Disable the VGA plane that we never use */ + REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + + /* Turn off vblank interrupts */ + drm_vblank_off(dev, pipe); + + /* Wait for vblank for the disable to take effect */ + gma_wait_for_vblank(dev); + + /* Disable plane */ + temp = REG_READ(map->cntr); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(map->cntr, + temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(map->base, REG_READ(map->base)); + REG_READ(map->base); + } + + /* Disable pipe */ + temp = REG_READ(map->conf); + if ((temp & PIPEACONF_ENABLE) != 0) { + REG_WRITE(map->conf, temp & ~PIPEACONF_ENABLE); + REG_READ(map->conf); + } + + /* Wait for vblank for the disable to take effect. */ + gma_wait_for_vblank(dev); + + udelay(150); + + /* Disable DPLL */ + temp = REG_READ(map->dpll); + if ((temp & DPLL_VCO_ENABLE) != 0) { + REG_WRITE(map->dpll, temp & ~DPLL_VCO_ENABLE); + REG_READ(map->dpll); + } + + /* Wait for the clocks to turn off. */ + udelay(150); + break; + } + + if (IS_CDV(dev)) + dev_priv->ops->update_wm(dev, crtc); + + /* Set FIFO watermarks */ + REG_WRITE(DSPARB, 0x3F3E); +} + +int gma_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, uint32_t height) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int pipe = gma_crtc->pipe; + uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; + uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; + uint32_t temp; + size_t addr = 0; + struct gtt_range *gt; + struct gtt_range *cursor_gt = gma_crtc->cursor_gt; + struct drm_gem_object *obj; + void *tmp_dst, *tmp_src; + int ret = 0, i, cursor_pages; + + /* If we didn't get a handle then turn the cursor off */ + if (!handle) { + temp = CURSOR_MODE_DISABLE; + mutex_lock(&dev->struct_mutex); + + if (gma_power_begin(dev, false)) { + REG_WRITE(control, temp); + REG_WRITE(base, 0); + gma_power_end(dev); + } + + /* Unpin the old GEM object */ + if (gma_crtc->cursor_obj) { + gt = container_of(gma_crtc->cursor_obj, + struct gtt_range, gem); + psb_gtt_unpin(gt); + drm_gem_object_unreference(gma_crtc->cursor_obj); + gma_crtc->cursor_obj = NULL; + } + + mutex_unlock(&dev->struct_mutex); + return 0; + } + + /* Currently we only support 64x64 cursors */ + if (width != 64 || height != 64) { + dev_dbg(dev->dev, "We currently only support 64x64 cursors\n"); + return -EINVAL; + } + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(dev, file_priv, handle); + if (!obj) { + ret = -ENOENT; + goto unlock; + } + + if (obj->size < width * height * 4) { + dev_dbg(dev->dev, "Buffer is too small\n"); + ret = -ENOMEM; + goto unref_cursor; + } + + gt = container_of(obj, struct gtt_range, gem); + + /* Pin the memory into the GTT */ + ret = psb_gtt_pin(gt); + if (ret) { + dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); + goto unref_cursor; + } + + if (dev_priv->ops->cursor_needs_phys) { + if (cursor_gt == NULL) { + dev_err(dev->dev, "No hardware cursor mem available"); + ret = -ENOMEM; + goto unref_cursor; + } + + /* Prevent overflow */ + if (gt->npage > 4) + cursor_pages = 4; + else + cursor_pages = gt->npage; + + /* Copy the cursor to cursor mem */ + tmp_dst = dev_priv->vram_addr + cursor_gt->offset; + for (i = 0; i < cursor_pages; i++) { + tmp_src = kmap(gt->pages[i]); + memcpy(tmp_dst, tmp_src, PAGE_SIZE); + kunmap(gt->pages[i]); + tmp_dst += PAGE_SIZE; + } + + addr = gma_crtc->cursor_addr; + } else { + addr = gt->offset; + gma_crtc->cursor_addr = addr; + } + + temp = 0; + /* set the pipe for the cursor */ + temp |= (pipe << 28); + temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + + if (gma_power_begin(dev, false)) { + REG_WRITE(control, temp); + REG_WRITE(base, addr); + gma_power_end(dev); + } + + /* unpin the old bo */ + if (gma_crtc->cursor_obj) { + gt = container_of(gma_crtc->cursor_obj, struct gtt_range, gem); + psb_gtt_unpin(gt); + drm_gem_object_unreference(gma_crtc->cursor_obj); + } + + gma_crtc->cursor_obj = obj; +unlock: + mutex_unlock(&dev->struct_mutex); + return ret; + +unref_cursor: + drm_gem_object_unreference(obj); + mutex_unlock(&dev->struct_mutex); + return ret; +} + +int gma_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct drm_device *dev = crtc->dev; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int pipe = gma_crtc->pipe; + uint32_t temp = 0; + uint32_t addr; + + if (x < 0) { + temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); + x = -x; + } + if (y < 0) { + temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); + y = -y; + } + + temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); + temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); + + addr = gma_crtc->cursor_addr; + + if (gma_power_begin(dev, false)) { + REG_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); + REG_WRITE((pipe == 0) ? CURABASE : CURBBASE, addr); + gma_power_end(dev); + } + return 0; +} + +bool gma_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +bool gma_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +void gma_crtc_prepare(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); +} + +void gma_crtc_commit(struct drm_crtc *crtc) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); +} + +void gma_crtc_disable(struct drm_crtc *crtc) +{ + struct gtt_range *gt; + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); + + if (crtc->primary->fb) { + gt = to_psb_fb(crtc->primary->fb)->gtt; + psb_gtt_unpin(gt); + } +} + +void gma_crtc_destroy(struct drm_crtc *crtc) +{ + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + + kfree(gma_crtc->crtc_state); + drm_crtc_cleanup(crtc); + kfree(gma_crtc); +} + +int gma_crtc_set_config(struct drm_mode_set *set) +{ + struct drm_device *dev = set->crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + int ret; + + if (!dev_priv->rpm_enabled) + return drm_crtc_helper_set_config(set); + + pm_runtime_forbid(&dev->pdev->dev); + ret = drm_crtc_helper_set_config(set); + pm_runtime_allow(&dev->pdev->dev); + + return ret; +} + +/** + * Save HW states of given crtc + */ +void gma_crtc_save(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + struct psb_intel_crtc_state *crtc_state = gma_crtc->crtc_state; + const struct psb_offset *map = &dev_priv->regmap[gma_crtc->pipe]; + uint32_t palette_reg; + int i; + + if (!crtc_state) { + dev_err(dev->dev, "No CRTC state found\n"); + return; + } + + crtc_state->saveDSPCNTR = REG_READ(map->cntr); + crtc_state->savePIPECONF = REG_READ(map->conf); + crtc_state->savePIPESRC = REG_READ(map->src); + crtc_state->saveFP0 = REG_READ(map->fp0); + crtc_state->saveFP1 = REG_READ(map->fp1); + crtc_state->saveDPLL = REG_READ(map->dpll); + crtc_state->saveHTOTAL = REG_READ(map->htotal); + crtc_state->saveHBLANK = REG_READ(map->hblank); + crtc_state->saveHSYNC = REG_READ(map->hsync); + crtc_state->saveVTOTAL = REG_READ(map->vtotal); + crtc_state->saveVBLANK = REG_READ(map->vblank); + crtc_state->saveVSYNC = REG_READ(map->vsync); + crtc_state->saveDSPSTRIDE = REG_READ(map->stride); + + /* NOTE: DSPSIZE DSPPOS only for psb */ + crtc_state->saveDSPSIZE = REG_READ(map->size); + crtc_state->saveDSPPOS = REG_READ(map->pos); + + crtc_state->saveDSPBASE = REG_READ(map->base); + + palette_reg = map->palette; + for (i = 0; i < 256; ++i) + crtc_state->savePalette[i] = REG_READ(palette_reg + (i << 2)); +} + +/** + * Restore HW states of given crtc + */ +void gma_crtc_restore(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + struct psb_intel_crtc_state *crtc_state = gma_crtc->crtc_state; + const struct psb_offset *map = &dev_priv->regmap[gma_crtc->pipe]; + uint32_t palette_reg; + int i; + + if (!crtc_state) { + dev_err(dev->dev, "No crtc state\n"); + return; + } + + if (crtc_state->saveDPLL & DPLL_VCO_ENABLE) { + REG_WRITE(map->dpll, + crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); + REG_READ(map->dpll); + udelay(150); + } + + REG_WRITE(map->fp0, crtc_state->saveFP0); + REG_READ(map->fp0); + + REG_WRITE(map->fp1, crtc_state->saveFP1); + REG_READ(map->fp1); + + REG_WRITE(map->dpll, crtc_state->saveDPLL); + REG_READ(map->dpll); + udelay(150); + + REG_WRITE(map->htotal, crtc_state->saveHTOTAL); + REG_WRITE(map->hblank, crtc_state->saveHBLANK); + REG_WRITE(map->hsync, crtc_state->saveHSYNC); + REG_WRITE(map->vtotal, crtc_state->saveVTOTAL); + REG_WRITE(map->vblank, crtc_state->saveVBLANK); + REG_WRITE(map->vsync, crtc_state->saveVSYNC); + REG_WRITE(map->stride, crtc_state->saveDSPSTRIDE); + + REG_WRITE(map->size, crtc_state->saveDSPSIZE); + REG_WRITE(map->pos, crtc_state->saveDSPPOS); + + REG_WRITE(map->src, crtc_state->savePIPESRC); + REG_WRITE(map->base, crtc_state->saveDSPBASE); + REG_WRITE(map->conf, crtc_state->savePIPECONF); + + gma_wait_for_vblank(dev); + + REG_WRITE(map->cntr, crtc_state->saveDSPCNTR); + REG_WRITE(map->base, crtc_state->saveDSPBASE); + + gma_wait_for_vblank(dev); + + palette_reg = map->palette; + for (i = 0; i < 256; ++i) + REG_WRITE(palette_reg + (i << 2), crtc_state->savePalette[i]); +} + +void gma_encoder_prepare(struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *encoder_funcs = + encoder->helper_private; + /* lvds has its own version of prepare see psb_intel_lvds_prepare */ + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); +} + +void gma_encoder_commit(struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *encoder_funcs = + encoder->helper_private; + /* lvds has its own version of commit see psb_intel_lvds_commit */ + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); +} + +void gma_encoder_destroy(struct drm_encoder *encoder) +{ + struct gma_encoder *intel_encoder = to_gma_encoder(encoder); + + drm_encoder_cleanup(encoder); + kfree(intel_encoder); +} + +/* Currently there is only a 1:1 mapping of encoders and connectors */ +struct drm_encoder *gma_best_encoder(struct drm_connector *connector) +{ + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + + return &gma_encoder->base; +} + +void gma_connector_attach_encoder(struct gma_connector *connector, + struct gma_encoder *encoder) +{ + connector->encoder = encoder; + drm_mode_connector_attach_encoder(&connector->base, + &encoder->base); +} + +#define GMA_PLL_INVALID(s) { /* DRM_ERROR(s); */ return false; } + +bool gma_pll_is_valid(struct drm_crtc *crtc, + const struct gma_limit_t *limit, + struct gma_clock_t *clock) +{ + if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) + GMA_PLL_INVALID("p1 out of range"); + if (clock->p < limit->p.min || limit->p.max < clock->p) + GMA_PLL_INVALID("p out of range"); + if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) + GMA_PLL_INVALID("m2 out of range"); + if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) + GMA_PLL_INVALID("m1 out of range"); + /* On CDV m1 is always 0 */ + if (clock->m1 <= clock->m2 && clock->m1 != 0) + GMA_PLL_INVALID("m1 <= m2 && m1 != 0"); + if (clock->m < limit->m.min || limit->m.max < clock->m) + GMA_PLL_INVALID("m out of range"); + if (clock->n < limit->n.min || limit->n.max < clock->n) + GMA_PLL_INVALID("n out of range"); + if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) + GMA_PLL_INVALID("vco out of range"); + /* XXX: We may need to be checking "Dot clock" + * depending on the multiplier, connector, etc., + * rather than just a single range. + */ + if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) + GMA_PLL_INVALID("dot out of range"); + + return true; +} + +bool gma_find_best_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, int refclk, + struct gma_clock_t *best_clock) +{ + struct drm_device *dev = crtc->dev; + const struct gma_clock_funcs *clock_funcs = + to_gma_crtc(crtc)->clock_funcs; + struct gma_clock_t clock; + int err = target; + + if (gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && + (REG_READ(LVDS) & LVDS_PORT_EN) != 0) { + /* + * For LVDS, if the panel is on, just rely on its current + * settings for dual-channel. We haven't figured out how to + * reliably set up different single/dual channel state, if we + * even can. + */ + if ((REG_READ(LVDS) & LVDS_CLKB_POWER_MASK) == + LVDS_CLKB_POWER_UP) + clock.p2 = limit->p2.p2_fast; + else + clock.p2 = limit->p2.p2_slow; + } else { + if (target < limit->p2.dot_limit) + clock.p2 = limit->p2.p2_slow; + else + clock.p2 = limit->p2.p2_fast; + } + + memset(best_clock, 0, sizeof(*best_clock)); + + /* m1 is always 0 on CDV so the outmost loop will run just once */ + for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) { + for (clock.m2 = limit->m2.min; + (clock.m2 < clock.m1 || clock.m1 == 0) && + clock.m2 <= limit->m2.max; clock.m2++) { + for (clock.n = limit->n.min; + clock.n <= limit->n.max; clock.n++) { + for (clock.p1 = limit->p1.min; + clock.p1 <= limit->p1.max; + clock.p1++) { + int this_err; + + clock_funcs->clock(refclk, &clock); + + if (!clock_funcs->pll_is_valid(crtc, + limit, &clock)) + continue; + + this_err = abs(clock.dot - target); + if (this_err < err) { + *best_clock = clock; + err = this_err; + } + } + } + } + } + + return err != target; +} diff --git a/drivers/gpu/drm/gma500/gma_display.h b/drivers/gpu/drm/gma500/gma_display.h new file mode 100644 index 00000000000..ed569d8a6af --- /dev/null +++ b/drivers/gpu/drm/gma500/gma_display.h @@ -0,0 +1,106 @@ +/* + * Copyright © 2006-2011 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> + * Patrik Jakobsson <patrik.r.jakobsson@gmail.com> + */ + +#ifndef _GMA_DISPLAY_H_ +#define _GMA_DISPLAY_H_ + +#include <linux/pm_runtime.h> + +struct gma_clock_t { + /* given values */ + int n; + int m1, m2; + int p1, p2; + /* derived values */ + int dot; + int vco; + int m; + int p; +}; + +struct gma_range_t { + int min, max; +}; + +struct gma_p2_t { + int dot_limit; + int p2_slow, p2_fast; +}; + +struct gma_limit_t { + struct gma_range_t dot, vco, n, m, m1, m2, p, p1; + struct gma_p2_t p2; + bool (*find_pll)(const struct gma_limit_t *, struct drm_crtc *, + int target, int refclk, + struct gma_clock_t *best_clock); +}; + +struct gma_clock_funcs { + void (*clock)(int refclk, struct gma_clock_t *clock); + const struct gma_limit_t *(*limit)(struct drm_crtc *crtc, int refclk); + bool (*pll_is_valid)(struct drm_crtc *crtc, + const struct gma_limit_t *limit, + struct gma_clock_t *clock); +}; + +/* Common pipe related functions */ +extern bool gma_pipe_has_type(struct drm_crtc *crtc, int type); +extern void gma_wait_for_vblank(struct drm_device *dev); +extern int gma_pipe_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb); +extern int gma_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file_priv, + uint32_t handle, + uint32_t width, uint32_t height); +extern int gma_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); +extern void gma_crtc_load_lut(struct drm_crtc *crtc); +extern void gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, u32 start, u32 size); +extern void gma_crtc_dpms(struct drm_crtc *crtc, int mode); +extern bool gma_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +extern void gma_crtc_prepare(struct drm_crtc *crtc); +extern void gma_crtc_commit(struct drm_crtc *crtc); +extern void gma_crtc_disable(struct drm_crtc *crtc); +extern void gma_crtc_destroy(struct drm_crtc *crtc); +extern int gma_crtc_set_config(struct drm_mode_set *set); + +extern void gma_crtc_save(struct drm_crtc *crtc); +extern void gma_crtc_restore(struct drm_crtc *crtc); + +extern void gma_encoder_prepare(struct drm_encoder *encoder); +extern void gma_encoder_commit(struct drm_encoder *encoder); +extern void gma_encoder_destroy(struct drm_encoder *encoder); +extern bool gma_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + +/* Common clock related functions */ +extern const struct gma_limit_t *gma_limit(struct drm_crtc *crtc, int refclk); +extern void gma_clock(int refclk, struct gma_clock_t *clock); +extern bool gma_pll_is_valid(struct drm_crtc *crtc, + const struct gma_limit_t *limit, + struct gma_clock_t *clock); +extern bool gma_find_best_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, int refclk, + struct gma_clock_t *best_clock); +#endif diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c index 5d5330f667f..592d205a008 100644 --- a/drivers/gpu/drm/gma500/gtt.c +++ b/drivers/gpu/drm/gma500/gtt.c @@ -22,6 +22,7 @@ #include <drm/drmP.h> #include <linux/shmem_fs.h> #include "psb_drv.h" +#include "blitter.h" /* @@ -39,6 +40,10 @@ static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type) { uint32_t mask = PSB_PTE_VALID; + /* Ensure we explode rather than put an invalid low mapping of + a high mapping page into the gtt */ + BUG_ON(pfn & ~(0xFFFFFFFF >> PAGE_SHIFT)); + if (type & PSB_MMU_CACHED_MEMORY) mask |= PSB_PTE_CACHED; if (type & PSB_MMU_RO_MEMORY) @@ -57,7 +62,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 __iomem *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r) { struct drm_psb_private *dev_priv = dev->dev_private; unsigned long offset; @@ -76,9 +81,11 @@ u32 *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r) * the GTT. This is protected via the gtt mutex which the caller * must hold. */ -static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r) +static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r, + int resume) { - u32 *gtt_slot, pte; + u32 __iomem *gtt_slot; + u32 pte; struct page **pages; int i; @@ -92,16 +99,20 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r) gtt_slot = psb_gtt_entry(dev, r); pages = r->pages; - /* Make sure changes are visible to the GPU */ - set_pages_array_uc(pages, r->npage); + if (!resume) { + /* Make sure changes are visible to the GPU */ + set_pages_array_wc(pages, r->npage); + } /* Write our page entries into the GTT itself */ for (i = r->roll; i < r->npage; i++) { - pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), + PSB_MMU_CACHED_MEMORY); iowrite32(pte, gtt_slot++); } for (i = 0; i < r->roll; i++) { - pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), + PSB_MMU_CACHED_MEMORY); iowrite32(pte, gtt_slot++); } /* Make sure all the entries are set before we return */ @@ -119,16 +130,18 @@ static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r) * page table entries with the dummy page. This is protected via the gtt * mutex which the caller must hold. */ -static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) +void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) { struct drm_psb_private *dev_priv = dev->dev_private; - u32 *gtt_slot, pte; + u32 __iomem *gtt_slot; + u32 pte; int i; WARN_ON(r->stolen); gtt_slot = psb_gtt_entry(dev, r); - pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page), 0); + pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page), + PSB_MMU_CACHED_MEMORY); for (i = 0; i < r->npage; i++) iowrite32(pte, gtt_slot++); @@ -148,7 +161,8 @@ static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r) */ void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll) { - u32 *gtt_slot, pte; + u32 __iomem *gtt_slot; + u32 pte; int i; if (roll >= r->npage) { @@ -166,11 +180,13 @@ void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll) gtt_slot = psb_gtt_entry(dev, r); for (i = r->roll; i < r->npage; i++) { - pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), + PSB_MMU_CACHED_MEMORY); iowrite32(pte, gtt_slot++); } for (i = 0; i < r->roll; i++) { - pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0); + pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), + PSB_MMU_CACHED_MEMORY); iowrite32(pte, gtt_slot++); } ioread32(gtt_slot - 1); @@ -186,37 +202,18 @@ void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll) */ static int psb_gtt_attach_pages(struct gtt_range *gt) { - struct inode *inode; - struct address_space *mapping; - int i; - struct page *p; - int pages = gt->gem.size / PAGE_SIZE; + struct page **pages; WARN_ON(gt->pages); - /* This is the shared memory object that backs the GEM resource */ - inode = gt->gem.filp->f_path.dentry->d_inode; - mapping = inode->i_mapping; + pages = drm_gem_get_pages(>->gem, 0); + if (IS_ERR(pages)) + return PTR_ERR(pages); - gt->pages = kmalloc(pages * sizeof(struct page *), GFP_KERNEL); - if (gt->pages == NULL) - return -ENOMEM; - gt->npage = pages; + gt->npage = gt->gem.size / PAGE_SIZE; + gt->pages = pages; - for (i = 0; i < pages; i++) { - p = shmem_read_mapping_page(mapping, i); - if (IS_ERR(p)) - goto err; - gt->pages[i] = p; - } return 0; - -err: - while (i--) - page_cache_release(gt->pages[i]); - kfree(gt->pages); - gt->pages = NULL; - return PTR_ERR(p); } /** @@ -230,13 +227,7 @@ err: */ static void psb_gtt_detach_pages(struct gtt_range *gt) { - int i; - for (i = 0; i < gt->npage; i++) { - /* FIXME: do we need to force dirty */ - set_page_dirty(gt->pages[i]); - page_cache_release(gt->pages[i]); - } - kfree(gt->pages); + drm_gem_put_pages(>->gem, gt->pages, true, false); gt->pages = NULL; } @@ -255,6 +246,7 @@ int psb_gtt_pin(struct gtt_range *gt) int ret = 0; struct drm_device *dev = gt->gem.dev; struct drm_psb_private *dev_priv = dev->dev_private; + u32 gpu_base = dev_priv->gtt.gatt_start; mutex_lock(&dev_priv->gtt_mutex); @@ -262,11 +254,14 @@ int psb_gtt_pin(struct gtt_range *gt) ret = psb_gtt_attach_pages(gt); if (ret < 0) goto out; - ret = psb_gtt_insert(dev, gt); + ret = psb_gtt_insert(dev, gt, 0); if (ret < 0) { psb_gtt_detach_pages(gt); goto out; } + psb_mmu_insert_pages(psb_mmu_get_default_pd(dev_priv->mmu), + gt->pages, (gpu_base + gt->offset), + gt->npage, 0, 0, PSB_MMU_CACHED_MEMORY); } gt->in_gart++; out: @@ -289,16 +284,30 @@ void psb_gtt_unpin(struct gtt_range *gt) { struct drm_device *dev = gt->gem.dev; struct drm_psb_private *dev_priv = dev->dev_private; + u32 gpu_base = dev_priv->gtt.gatt_start; + int ret; + /* While holding the gtt_mutex no new blits can be initiated */ mutex_lock(&dev_priv->gtt_mutex); + /* Wait for any possible usage of the memory to be finished */ + ret = gma_blt_wait_idle(dev_priv); + if (ret) { + DRM_ERROR("Failed to idle the blitter, unpin failed!"); + goto out; + } + WARN_ON(!gt->in_gart); gt->in_gart--; if (gt->in_gart == 0 && gt->stolen == 0) { + psb_mmu_remove_pages(psb_mmu_get_default_pd(dev_priv->mmu), + (gpu_base + gt->offset), gt->npage, 0, 0); psb_gtt_remove(dev, gt); psb_gtt_detach_pages(gt); } + +out: mutex_unlock(&dev_priv->gtt_mutex); } @@ -321,7 +330,7 @@ void psb_gtt_unpin(struct gtt_range *gt) * as in use. */ struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, - const char *name, int backed) + const char *name, int backed, u32 align) { struct drm_psb_private *dev_priv = dev->dev_private; struct gtt_range *gt; @@ -349,7 +358,7 @@ struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, /* Ensure this is set for non GEM objects */ gt->gem.dev = dev; ret = allocate_resource(dev_priv->gtt_mem, >->resource, - len, start, end, PAGE_SIZE, NULL, NULL); + len, start, end, align, NULL, NULL); if (ret == 0) { gt->offset = gt->resource.start - r->start; return gt; @@ -378,7 +387,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); @@ -409,16 +418,16 @@ int psb_gtt_init(struct drm_device *dev, int resume) unsigned long stolen_size, vram_stolen_size; unsigned i, num_pages; unsigned pfn_base; - uint32_t vram_pages; - uint32_t dvmt_mode = 0; struct psb_gtt *pg; int ret = 0; uint32_t pte; - mutex_init(&dev_priv->gtt_mutex); + if (!resume) { + mutex_init(&dev_priv->gtt_mutex); + psb_gtt_alloc(dev); + } - psb_gtt_alloc(dev); pg = &dev_priv->gtt; /* Enable the GTT */ @@ -446,10 +455,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 +469,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 @@ -484,13 +492,8 @@ int psb_gtt_init(struct drm_device *dev, int resume) stolen_size = vram_stolen_size; - printk(KERN_INFO "Stolen memory information\n"); - printk(KERN_INFO " base in RAM: 0x%x\n", dev_priv->stolen_base); - printk(KERN_INFO " size: %luK, calculated by (GTT RAM base) - (Stolen base), seems wrong\n", - vram_stolen_size/1024); - dvmt_mode = (dev_priv->gmch_ctrl >> 4) & 0x7; - printk(KERN_INFO " the correct size should be: %dM(dvmt mode=%d)\n", - (dvmt_mode == 1) ? 1 : (2 << (dvmt_mode - 1)), dvmt_mode); + dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", + dev_priv->stolen_base, vram_stolen_size / 1024); if (resume && (gtt_pages != pg->gtt_pages) && (stolen_size != pg->stolen_size)) { @@ -506,7 +509,8 @@ int psb_gtt_init(struct drm_device *dev, int resume) /* * Map the GTT and the stolen memory area */ - dev_priv->gtt_map = ioremap_nocache(pg->gtt_phys_start, + if (!resume) + dev_priv->gtt_map = ioremap_nocache(pg->gtt_phys_start, gtt_pages << PAGE_SHIFT); if (!dev_priv->gtt_map) { dev_err(dev->dev, "Failure to map gtt.\n"); @@ -514,7 +518,10 @@ int psb_gtt_init(struct drm_device *dev, int resume) goto out_err; } - dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size); + if (!resume) + dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, + stolen_size); + if (!dev_priv->vram_addr) { dev_err(dev->dev, "Failure to map stolen base.\n"); ret = -ENOMEM; @@ -526,11 +533,11 @@ int psb_gtt_init(struct drm_device *dev, int resume) */ pfn_base = dev_priv->stolen_base >> PAGE_SHIFT; - vram_pages = num_pages = vram_stolen_size >> PAGE_SHIFT; - printk(KERN_INFO"Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n", + num_pages = vram_stolen_size >> PAGE_SHIFT; + dev_dbg(dev->dev, "Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n", num_pages, pfn_base << PAGE_SHIFT, 0); for (i = 0; i < num_pages; ++i) { - pte = psb_gtt_mask_pte(pfn_base + i, 0); + pte = psb_gtt_mask_pte(pfn_base + i, PSB_MMU_CACHED_MEMORY); iowrite32(pte, dev_priv->gtt_map + i); } @@ -539,7 +546,7 @@ int psb_gtt_init(struct drm_device *dev, int resume) */ pfn_base = page_to_pfn(dev_priv->scratch_page); - pte = psb_gtt_mask_pte(pfn_base, 0); + pte = psb_gtt_mask_pte(pfn_base, PSB_MMU_CACHED_MEMORY); for (; i < gtt_pages; ++i) iowrite32(pte, dev_priv->gtt_map + i); @@ -550,3 +557,31 @@ out_err: psb_gtt_takedown(dev); return ret; } + +int psb_gtt_restore(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct resource *r = dev_priv->gtt_mem->child; + struct gtt_range *range; + unsigned int restored = 0, total = 0, size = 0; + + /* On resume, the gtt_mutex is already initialized */ + mutex_lock(&dev_priv->gtt_mutex); + psb_gtt_init(dev, 1); + + while (r != NULL) { + range = container_of(r, struct gtt_range, resource); + if (range->pages) { + psb_gtt_insert(dev, range, 1); + size += range->resource.end - range->resource.start; + restored++; + } + r = r->sibling; + total++; + } + mutex_unlock(&dev_priv->gtt_mutex); + DRM_DEBUG_DRIVER("Restored %u of %u gtt ranges (%u KB)", restored, + total, (size / 1024)); + + return 0; +} diff --git a/drivers/gpu/drm/gma500/gtt.h b/drivers/gpu/drm/gma500/gtt.h index aa1742387f5..f5860a739bd 100644 --- a/drivers/gpu/drm/gma500/gtt.h +++ b/drivers/gpu/drm/gma500/gtt.h @@ -53,12 +53,13 @@ struct gtt_range { }; extern struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len, - const char *name, int backed); + const char *name, int backed, + u32 align); extern void psb_gtt_kref_put(struct gtt_range *gt); extern void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt); extern int psb_gtt_pin(struct gtt_range *gt); extern void psb_gtt_unpin(struct gtt_range *gt); extern void psb_gtt_roll(struct drm_device *dev, struct gtt_range *gt, int roll); - +extern int psb_gtt_restore(struct drm_device *dev); #endif diff --git a/drivers/gpu/drm/gma500/intel_bios.c b/drivers/gpu/drm/gma500/intel_bios.c index d4d0c5b8bf9..d3497348c4d 100644 --- a/drivers/gpu/drm/gma500/intel_bios.c +++ b/drivers/gpu/drm/gma500/intel_bios.c @@ -20,12 +20,14 @@ */ #include <drm/drmP.h> #include <drm/drm.h> -#include "gma_drm.h" +#include <drm/gma_drm.h> #include "psb_drv.h" #include "psb_intel_drv.h" #include "psb_intel_reg.h" #include "intel_bios.h" +#define SLAVE_ADDR1 0x70 +#define SLAVE_ADDR2 0x72 static void *find_section(struct bdb_header *bdb, int section_id) { @@ -52,6 +54,108 @@ static void *find_section(struct bdb_header *bdb, int section_id) return NULL; } +static void +parse_edp(struct drm_psb_private *dev_priv, struct bdb_header *bdb) +{ + struct bdb_edp *edp; + struct edp_power_seq *edp_pps; + struct edp_link_params *edp_link_params; + uint8_t panel_type; + + edp = find_section(bdb, BDB_EDP); + + dev_priv->edp.bpp = 18; + if (!edp) { + if (dev_priv->edp.support) { + DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported, assume %dbpp panel color depth.\n", + dev_priv->edp.bpp); + } + return; + } + + panel_type = dev_priv->panel_type; + switch ((edp->color_depth >> (panel_type * 2)) & 3) { + case EDP_18BPP: + dev_priv->edp.bpp = 18; + break; + case EDP_24BPP: + dev_priv->edp.bpp = 24; + break; + case EDP_30BPP: + dev_priv->edp.bpp = 30; + break; + } + + /* Get the eDP sequencing and link info */ + edp_pps = &edp->power_seqs[panel_type]; + edp_link_params = &edp->link_params[panel_type]; + + dev_priv->edp.pps = *edp_pps; + + DRM_DEBUG_KMS("EDP timing in vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + dev_priv->edp.pps.t1_t3, dev_priv->edp.pps.t8, + dev_priv->edp.pps.t9, dev_priv->edp.pps.t10, + dev_priv->edp.pps.t11_t12); + + dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 : + DP_LINK_BW_1_62; + switch (edp_link_params->lanes) { + case 0: + dev_priv->edp.lanes = 1; + break; + case 1: + dev_priv->edp.lanes = 2; + break; + case 3: + default: + dev_priv->edp.lanes = 4; + break; + } + DRM_DEBUG_KMS("VBT reports EDP: Lane_count %d, Lane_rate %d, Bpp %d\n", + dev_priv->edp.lanes, dev_priv->edp.rate, dev_priv->edp.bpp); + + switch (edp_link_params->preemphasis) { + case 0: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0; + break; + case 1: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; + break; + case 2: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6; + break; + case 3: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; + break; + } + switch (edp_link_params->vswing) { + case 0: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_400; + break; + case 1: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600; + break; + case 2: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800; + break; + case 3: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200; + break; + } + DRM_DEBUG_KMS("VBT reports EDP: VSwing %d, Preemph %d\n", + dev_priv->edp.vswing, dev_priv->edp.preemphasis); +} + +static u16 +get_blocksize(void *p) +{ + u16 *block_ptr, block_size; + + block_ptr = (u16 *)((char *)p - 2); + block_size = *block_ptr; + return block_size; +} + static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, struct lvds_dvo_timing *dvo_timing) { @@ -75,6 +179,16 @@ static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, panel_fixed_mode->clock = dvo_timing->clock * 10; panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; + if (dvo_timing->hsync_positive) + panel_fixed_mode->flags |= DRM_MODE_FLAG_PHSYNC; + else + panel_fixed_mode->flags |= DRM_MODE_FLAG_NHSYNC; + + if (dvo_timing->vsync_positive) + panel_fixed_mode->flags |= DRM_MODE_FLAG_PVSYNC; + else + panel_fixed_mode->flags |= DRM_MODE_FLAG_NVSYNC; + /* Some VBTs have bogus h/vtotal values */ if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1; @@ -104,12 +218,11 @@ static void parse_backlight_data(struct drm_psb_private *dev_priv, bl_start = find_section(bdb, BDB_LVDS_BACKLIGHT); vbt_lvds_bl = (struct bdb_lvds_backlight *)(bl_start + 1) + p_type; - lvds_bl = kzalloc(sizeof(*vbt_lvds_bl), GFP_KERNEL); + lvds_bl = kmemdup(vbt_lvds_bl, sizeof(*vbt_lvds_bl), GFP_KERNEL); if (!lvds_bl) { dev_err(dev_priv->dev->dev, "out of memory for backlight data\n"); return; } - memcpy(lvds_bl, vbt_lvds_bl, sizeof(*vbt_lvds_bl)); dev_priv->lvds_bl = lvds_bl; } @@ -132,6 +245,8 @@ static void parse_lfp_panel_data(struct drm_psb_private *dev_priv, return; dev_priv->lvds_dither = lvds_options->pixel_dither; + dev_priv->panel_type = lvds_options->panel_type; + if (lvds_options->panel_type == 0xff) return; @@ -217,6 +332,183 @@ static void parse_general_features(struct drm_psb_private *dev_priv, } } +static void +parse_sdvo_device_mapping(struct drm_psb_private *dev_priv, + struct bdb_header *bdb) +{ + struct sdvo_device_mapping *p_mapping; + struct bdb_general_definitions *p_defs; + struct child_device_config *p_child; + int i, child_device_num, count; + u16 block_size; + + p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); + if (!p_defs) { + DRM_DEBUG_KMS("No general definition block is found, unable to construct sdvo mapping.\n"); + return; + } + /* judge whether the size of child device meets the requirements. + * If the child device size obtained from general definition block + * is different with sizeof(struct child_device_config), skip the + * parsing of sdvo device info + */ + if (p_defs->child_dev_size != sizeof(*p_child)) { + /* different child dev size . Ignore it */ + DRM_DEBUG_KMS("different child size is found. Invalid.\n"); + return; + } + /* get the block size of general definitions */ + block_size = get_blocksize(p_defs); + /* get the number of child device */ + child_device_num = (block_size - sizeof(*p_defs)) / + sizeof(*p_child); + count = 0; + for (i = 0; i < child_device_num; i++) { + p_child = &(p_defs->devices[i]); + if (!p_child->device_type) { + /* skip the device block if device type is invalid */ + continue; + } + if (p_child->slave_addr != SLAVE_ADDR1 && + p_child->slave_addr != SLAVE_ADDR2) { + /* + * If the slave address is neither 0x70 nor 0x72, + * it is not a SDVO device. Skip it. + */ + continue; + } + if (p_child->dvo_port != DEVICE_PORT_DVOB && + p_child->dvo_port != DEVICE_PORT_DVOC) { + /* skip the incorrect SDVO port */ + DRM_DEBUG_KMS("Incorrect SDVO port. Skip it\n"); + continue; + } + DRM_DEBUG_KMS("the SDVO device with slave addr %2x is found on" + " %s port\n", + p_child->slave_addr, + (p_child->dvo_port == DEVICE_PORT_DVOB) ? + "SDVOB" : "SDVOC"); + p_mapping = &(dev_priv->sdvo_mappings[p_child->dvo_port - 1]); + if (!p_mapping->initialized) { + p_mapping->dvo_port = p_child->dvo_port; + p_mapping->slave_addr = p_child->slave_addr; + p_mapping->dvo_wiring = p_child->dvo_wiring; + p_mapping->ddc_pin = p_child->ddc_pin; + p_mapping->i2c_pin = p_child->i2c_pin; + p_mapping->initialized = 1; + DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d\n", + p_mapping->dvo_port, + p_mapping->slave_addr, + p_mapping->dvo_wiring, + p_mapping->ddc_pin, + p_mapping->i2c_pin); + } else { + DRM_DEBUG_KMS("Maybe one SDVO port is shared by " + "two SDVO device.\n"); + } + if (p_child->slave2_addr) { + /* Maybe this is a SDVO device with multiple inputs */ + /* And the mapping info is not added */ + DRM_DEBUG_KMS("there exists the slave2_addr. Maybe this" + " is a SDVO device with multiple inputs.\n"); + } + count++; + } + + if (!count) { + /* No SDVO device info is found */ + DRM_DEBUG_KMS("No SDVO device info is found in VBT\n"); + } + return; +} + + +static void +parse_driver_features(struct drm_psb_private *dev_priv, + struct bdb_header *bdb) +{ + struct bdb_driver_features *driver; + + driver = find_section(bdb, BDB_DRIVER_FEATURES); + if (!driver) + return; + + if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP) + dev_priv->edp.support = 1; + + /* This bit means to use 96Mhz for DPLL_A or not */ + if (driver->primary_lfp_id) + dev_priv->dplla_96mhz = true; + else + dev_priv->dplla_96mhz = false; +} + +static void +parse_device_mapping(struct drm_psb_private *dev_priv, + struct bdb_header *bdb) +{ + struct bdb_general_definitions *p_defs; + struct child_device_config *p_child, *child_dev_ptr; + int i, child_device_num, count; + u16 block_size; + + p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); + if (!p_defs) { + DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n"); + return; + } + /* judge whether the size of child device meets the requirements. + * If the child device size obtained from general definition block + * is different with sizeof(struct child_device_config), skip the + * parsing of sdvo device info + */ + if (p_defs->child_dev_size != sizeof(*p_child)) { + /* different child dev size . Ignore it */ + DRM_DEBUG_KMS("different child size is found. Invalid.\n"); + return; + } + /* get the block size of general definitions */ + block_size = get_blocksize(p_defs); + /* get the number of child device */ + child_device_num = (block_size - sizeof(*p_defs)) / + sizeof(*p_child); + count = 0; + /* get the number of child devices that are present */ + for (i = 0; i < child_device_num; i++) { + p_child = &(p_defs->devices[i]); + if (!p_child->device_type) { + /* skip the device block if device type is invalid */ + continue; + } + count++; + } + if (!count) { + DRM_DEBUG_KMS("no child dev is parsed from VBT\n"); + return; + } + dev_priv->child_dev = kcalloc(count, sizeof(*p_child), GFP_KERNEL); + if (!dev_priv->child_dev) { + DRM_DEBUG_KMS("No memory space for child devices\n"); + return; + } + + dev_priv->child_dev_num = count; + count = 0; + for (i = 0; i < child_device_num; i++) { + p_child = &(p_defs->devices[i]); + if (!p_child->device_type) { + /* skip the device block if device type is invalid */ + continue; + } + child_dev_ptr = dev_priv->child_dev + count; + count++; + memcpy((void *)child_dev_ptr, (void *)p_child, + sizeof(*p_child)); + } + return; +} + + /** * psb_intel_init_bios - initialize VBIOS settings & find VBT * @dev: DRM device @@ -231,43 +523,63 @@ static void parse_general_features(struct drm_psb_private *dev_priv, * * Returns 0 on success, nonzero on failure. */ -bool psb_intel_init_bios(struct drm_device *dev) +int psb_intel_init_bios(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; struct pci_dev *pdev = dev->pdev; struct vbt_header *vbt = NULL; - struct bdb_header *bdb; - u8 __iomem *bios; + struct bdb_header *bdb = NULL; + u8 __iomem *bios = NULL; size_t size; int i; - bios = pci_map_rom(pdev, &size); - if (!bios) - return -1; - /* Scour memory looking for the VBT signature */ - for (i = 0; i + 4 < size; i++) { - if (!memcmp(bios + i, "$VBT", 4)) { - vbt = (struct vbt_header *)(bios + i); - break; - } - } + dev_priv->panel_type = 0xff; - if (!vbt) { - dev_err(dev->dev, "VBT signature missing\n"); - pci_unmap_rom(pdev, bios); - return -1; + /* XXX Should this validation be moved to intel_opregion.c? */ + if (dev_priv->opregion.vbt) { + struct vbt_header *vbt = dev_priv->opregion.vbt; + if (memcmp(vbt->signature, "$VBT", 4) == 0) { + DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n", + vbt->signature); + bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset); + } else + dev_priv->opregion.vbt = NULL; } - bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); + if (bdb == NULL) { + bios = pci_map_rom(pdev, &size); + if (!bios) + return -1; + + /* Scour memory looking for the VBT signature */ + for (i = 0; i + 4 < size; i++) { + if (!memcmp(bios + i, "$VBT", 4)) { + vbt = (struct vbt_header *)(bios + i); + break; + } + } - /* Grab useful general definitions */ + if (!vbt) { + dev_err(dev->dev, "VBT signature missing\n"); + pci_unmap_rom(pdev, bios); + return -1; + } + bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); + } + + /* Grab useful general dxefinitions */ parse_general_features(dev_priv, bdb); + parse_driver_features(dev_priv, bdb); parse_lfp_panel_data(dev_priv, bdb); parse_sdvo_panel_data(dev_priv, bdb); + parse_sdvo_device_mapping(dev_priv, bdb); + parse_device_mapping(dev_priv, bdb); parse_backlight_data(dev_priv, bdb); + parse_edp(dev_priv, bdb); - pci_unmap_rom(pdev, bios); + if (bios) + pci_unmap_rom(pdev, bios); return 0; } @@ -278,26 +590,8 @@ bool psb_intel_init_bios(struct drm_device *dev) void psb_intel_destroy_bios(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_display_mode *sdvo_lvds_vbt_mode = - dev_priv->sdvo_lvds_vbt_mode; - struct drm_display_mode *lfp_lvds_vbt_mode = - dev_priv->lfp_lvds_vbt_mode; - struct bdb_lvds_backlight *lvds_bl = - dev_priv->lvds_bl; - - /*free sdvo panel mode*/ - if (sdvo_lvds_vbt_mode) { - dev_priv->sdvo_lvds_vbt_mode = NULL; - kfree(sdvo_lvds_vbt_mode); - } - - if (lfp_lvds_vbt_mode) { - dev_priv->lfp_lvds_vbt_mode = NULL; - kfree(lfp_lvds_vbt_mode); - } - if (lvds_bl) { - dev_priv->lvds_bl = NULL; - kfree(lvds_bl); - } + kfree(dev_priv->sdvo_lvds_vbt_mode); + kfree(dev_priv->lfp_lvds_vbt_mode); + kfree(dev_priv->lvds_bl); } diff --git a/drivers/gpu/drm/gma500/intel_bios.h b/drivers/gpu/drm/gma500/intel_bios.h index 70f1bf01818..978ae4b25e8 100644 --- a/drivers/gpu/drm/gma500/intel_bios.h +++ b/drivers/gpu/drm/gma500/intel_bios.h @@ -19,10 +19,11 @@ * */ -#ifndef _I830_BIOS_H_ -#define _I830_BIOS_H_ +#ifndef _INTEL_BIOS_H_ +#define _INTEL_BIOS_H_ #include <drm/drmP.h> +#include <drm/drm_dp_helper.h> struct vbt_header { u8 signature[20]; /**< Always starts with 'VBT$' */ @@ -93,6 +94,7 @@ struct vbios_data { #define BDB_SDVO_LVDS_PNP_IDS 24 #define BDB_SDVO_LVDS_POWER_SEQ 25 #define BDB_TV_OPTIONS 26 +#define BDB_EDP 27 #define BDB_LVDS_OPTIONS 40 #define BDB_LVDS_LFP_DATA_PTRS 41 #define BDB_LVDS_LFP_DATA 42 @@ -127,9 +129,93 @@ struct bdb_general_features { /* bits 5 */ u8 int_crt_support:1; u8 int_tv_support:1; - u8 rsvd11:6; /* finish byte */ + u8 int_efp_support:1; + u8 dp_ssc_enb:1; /* PCH attached eDP supports SSC */ + u8 dp_ssc_freq:1; /* SSC freq for PCH attached eDP */ + u8 rsvd11:3; /* finish byte */ } __attribute__((packed)); +/* pre-915 */ +#define GPIO_PIN_DVI_LVDS 0x03 /* "DVI/LVDS DDC GPIO pins" */ +#define GPIO_PIN_ADD_I2C 0x05 /* "ADDCARD I2C GPIO pins" */ +#define GPIO_PIN_ADD_DDC 0x04 /* "ADDCARD DDC GPIO pins" */ +#define GPIO_PIN_ADD_DDC_I2C 0x06 /* "ADDCARD DDC/I2C GPIO pins" */ + +/* Pre 915 */ +#define DEVICE_TYPE_NONE 0x00 +#define DEVICE_TYPE_CRT 0x01 +#define DEVICE_TYPE_TV 0x09 +#define DEVICE_TYPE_EFP 0x12 +#define DEVICE_TYPE_LFP 0x22 +/* On 915+ */ +#define DEVICE_TYPE_CRT_DPMS 0x6001 +#define DEVICE_TYPE_CRT_DPMS_HOTPLUG 0x4001 +#define DEVICE_TYPE_TV_COMPOSITE 0x0209 +#define DEVICE_TYPE_TV_MACROVISION 0x0289 +#define DEVICE_TYPE_TV_RF_COMPOSITE 0x020c +#define DEVICE_TYPE_TV_SVIDEO_COMPOSITE 0x0609 +#define DEVICE_TYPE_TV_SCART 0x0209 +#define DEVICE_TYPE_TV_CODEC_HOTPLUG_PWR 0x6009 +#define DEVICE_TYPE_EFP_HOTPLUG_PWR 0x6012 +#define DEVICE_TYPE_EFP_DVI_HOTPLUG_PWR 0x6052 +#define DEVICE_TYPE_EFP_DVI_I 0x6053 +#define DEVICE_TYPE_EFP_DVI_D_DUAL 0x6152 +#define DEVICE_TYPE_EFP_DVI_D_HDCP 0x60d2 +#define DEVICE_TYPE_OPENLDI_HOTPLUG_PWR 0x6062 +#define DEVICE_TYPE_OPENLDI_DUALPIX 0x6162 +#define DEVICE_TYPE_LFP_PANELLINK 0x5012 +#define DEVICE_TYPE_LFP_CMOS_PWR 0x5042 +#define DEVICE_TYPE_LFP_LVDS_PWR 0x5062 +#define DEVICE_TYPE_LFP_LVDS_DUAL 0x5162 +#define DEVICE_TYPE_LFP_LVDS_DUAL_HDCP 0x51e2 + +#define DEVICE_CFG_NONE 0x00 +#define DEVICE_CFG_12BIT_DVOB 0x01 +#define DEVICE_CFG_12BIT_DVOC 0x02 +#define DEVICE_CFG_24BIT_DVOBC 0x09 +#define DEVICE_CFG_24BIT_DVOCB 0x0a +#define DEVICE_CFG_DUAL_DVOB 0x11 +#define DEVICE_CFG_DUAL_DVOC 0x12 +#define DEVICE_CFG_DUAL_DVOBC 0x13 +#define DEVICE_CFG_DUAL_LINK_DVOBC 0x19 +#define DEVICE_CFG_DUAL_LINK_DVOCB 0x1a + +#define DEVICE_WIRE_NONE 0x00 +#define DEVICE_WIRE_DVOB 0x01 +#define DEVICE_WIRE_DVOC 0x02 +#define DEVICE_WIRE_DVOBC 0x03 +#define DEVICE_WIRE_DVOBB 0x05 +#define DEVICE_WIRE_DVOCC 0x06 +#define DEVICE_WIRE_DVOB_MASTER 0x0d +#define DEVICE_WIRE_DVOC_MASTER 0x0e + +#define DEVICE_PORT_DVOA 0x00 /* none on 845+ */ +#define DEVICE_PORT_DVOB 0x01 +#define DEVICE_PORT_DVOC 0x02 + +struct child_device_config { + u16 handle; + u16 device_type; + u8 device_id[10]; /* ascii string */ + u16 addin_offset; + u8 dvo_port; /* See Device_PORT_* above */ + u8 i2c_pin; + u8 slave_addr; + u8 ddc_pin; + u16 edid_ptr; + u8 dvo_cfg; /* See DEVICE_CFG_* above */ + u8 dvo2_port; + u8 i2c2_pin; + u8 slave2_addr; + u8 ddc2_pin; + u8 capabilities; + u8 dvo_wiring;/* See DEVICE_WIRE_* above */ + u8 dvo2_wiring; + u16 extended_type; + u8 dvo_function; +} __attribute__((packed)); + + struct bdb_general_definitions { /* DDC GPIO */ u8 crt_ddc_gmbus_pin; @@ -144,13 +230,18 @@ struct bdb_general_definitions { u8 boot_display[2]; u8 child_dev_size; - /* device info */ - u8 tv_or_lvds_info[33]; - u8 dev1[33]; - u8 dev2[33]; - u8 dev3[33]; - u8 dev4[33]; - /* may be another device block here on some platforms */ + /* + * Device info: + * If TV is present, it'll be at devices[0]. + * LVDS will be next, either devices[0] or [1], if present. + * On some platforms the number of device is 6. But could be as few as + * 4 if both TV and LVDS are missing. + * And the device num is related with the size of general definition + * block. It is obtained by using the following formula: + * number = (block_size - sizeof(bdb_general_definitions))/ + * sizeof(child_device_config); + */ + struct child_device_config devices[0]; }; struct bdb_lvds_options { @@ -302,8 +393,91 @@ struct bdb_sdvo_lvds_options { u8 panel_misc_bits_4; } __attribute__((packed)); +#define BDB_DRIVER_FEATURE_NO_LVDS 0 +#define BDB_DRIVER_FEATURE_INT_LVDS 1 +#define BDB_DRIVER_FEATURE_SDVO_LVDS 2 +#define BDB_DRIVER_FEATURE_EDP 3 + +struct bdb_driver_features { + u8 boot_dev_algorithm:1; + u8 block_display_switch:1; + u8 allow_display_switch:1; + u8 hotplug_dvo:1; + u8 dual_view_zoom:1; + u8 int15h_hook:1; + u8 sprite_in_clone:1; + u8 primary_lfp_id:1; + + u16 boot_mode_x; + u16 boot_mode_y; + u8 boot_mode_bpp; + u8 boot_mode_refresh; + + u16 enable_lfp_primary:1; + u16 selective_mode_pruning:1; + u16 dual_frequency:1; + u16 render_clock_freq:1; /* 0: high freq; 1: low freq */ + u16 nt_clone_support:1; + u16 power_scheme_ui:1; /* 0: CUI; 1: 3rd party */ + u16 sprite_display_assign:1; /* 0: secondary; 1: primary */ + u16 cui_aspect_scaling:1; + u16 preserve_aspect_ratio:1; + u16 sdvo_device_power_down:1; + u16 crt_hotplug:1; + u16 lvds_config:2; + u16 tv_hotplug:1; + u16 hdmi_config:2; + + u8 static_display:1; + u8 reserved2:7; + u16 legacy_crt_max_x; + u16 legacy_crt_max_y; + u8 legacy_crt_max_refresh; + + u8 hdmi_termination; + u8 custom_vbt_version; +} __attribute__((packed)); -extern bool psb_intel_init_bios(struct drm_device *dev); +#define EDP_18BPP 0 +#define EDP_24BPP 1 +#define EDP_30BPP 2 +#define EDP_RATE_1_62 0 +#define EDP_RATE_2_7 1 +#define EDP_LANE_1 0 +#define EDP_LANE_2 1 +#define EDP_LANE_4 3 +#define EDP_PREEMPHASIS_NONE 0 +#define EDP_PREEMPHASIS_3_5dB 1 +#define EDP_PREEMPHASIS_6dB 2 +#define EDP_PREEMPHASIS_9_5dB 3 +#define EDP_VSWING_0_4V 0 +#define EDP_VSWING_0_6V 1 +#define EDP_VSWING_0_8V 2 +#define EDP_VSWING_1_2V 3 + +struct edp_power_seq { + u16 t1_t3; + u16 t8; + u16 t9; + u16 t10; + u16 t11_t12; +} __attribute__ ((packed)); + +struct edp_link_params { + u8 rate:4; + u8 lanes:4; + u8 preemphasis:4; + u8 vswing:4; +} __attribute__ ((packed)); + +struct bdb_edp { + struct edp_power_seq power_seqs[16]; + u32 color_depth; + u32 sdrrs_msa_timing_delay; + struct edp_link_params link_params[16]; +} __attribute__ ((packed)); + +extern int psb_intel_init_bios(struct drm_device *dev); extern void psb_intel_destroy_bios(struct drm_device *dev); /* @@ -427,4 +601,21 @@ extern void psb_intel_destroy_bios(struct drm_device *dev); #define SWF14_APM_STANDBY 0x1 #define SWF14_APM_RESTORE 0x0 -#endif /* _I830_BIOS_H_ */ +/* Add the device class for LFP, TV, HDMI */ +#define DEVICE_TYPE_INT_LFP 0x1022 +#define DEVICE_TYPE_INT_TV 0x1009 +#define DEVICE_TYPE_HDMI 0x60D2 +#define DEVICE_TYPE_DP 0x68C6 +#define DEVICE_TYPE_eDP 0x78C6 + +/* define the DVO port for HDMI output type */ +#define DVO_B 1 +#define DVO_C 2 +#define DVO_D 3 + +/* define the PORT for DP output type */ +#define PORT_IDPB 7 +#define PORT_IDPC 8 +#define PORT_IDPD 9 + +#endif /* _INTEL_BIOS_H_ */ diff --git a/drivers/gpu/drm/gma500/intel_gmbus.c b/drivers/gpu/drm/gma500/intel_gmbus.c index 147584ac8d0..566d330aaee 100644 --- a/drivers/gpu/drm/gma500/intel_gmbus.c +++ b/drivers/gpu/drm/gma500/intel_gmbus.c @@ -29,10 +29,9 @@ #include <linux/module.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> -#include "drmP.h" -#include "drm.h" +#include <drm/drmP.h> #include "psb_intel_drv.h" -#include "gma_drm.h" +#include <drm/gma_drm.h> #include "psb_drv.h" #include "psb_intel_reg.h" @@ -52,6 +51,9 @@ #define wait_for(COND, MS) _wait_for(COND, MS, 1) #define wait_for_atomic(COND, MS) _wait_for(COND, MS, 0) +#define GMBUS_REG_READ(reg) ioread32(dev_priv->gmbus_reg + (reg)) +#define GMBUS_REG_WRITE(reg, val) iowrite32((val), dev_priv->gmbus_reg + (reg)) + /* Intel GPIO access functions */ #define I2C_RISEFALL_TIME 20 @@ -72,7 +74,8 @@ struct intel_gpio { void gma_intel_i2c_reset(struct drm_device *dev) { - REG_WRITE(GMBUS0, 0); + struct drm_psb_private *dev_priv = dev->dev_private; + GMBUS_REG_WRITE(GMBUS0, 0); } static void intel_i2c_quirk_set(struct drm_psb_private *dev_priv, bool enable) @@ -99,11 +102,10 @@ static void intel_i2c_quirk_set(struct drm_psb_private *dev_priv, bool enable) static u32 get_reserved(struct intel_gpio *gpio) { struct drm_psb_private *dev_priv = gpio->dev_priv; - struct drm_device *dev = dev_priv->dev; u32 reserved = 0; /* On most chips, these bits must be preserved in software. */ - reserved = REG_READ(gpio->reg) & + reserved = GMBUS_REG_READ(gpio->reg) & (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE); @@ -114,29 +116,26 @@ static int get_clock(void *data) { struct intel_gpio *gpio = data; struct drm_psb_private *dev_priv = gpio->dev_priv; - struct drm_device *dev = dev_priv->dev; u32 reserved = get_reserved(gpio); - REG_WRITE(gpio->reg, reserved | GPIO_CLOCK_DIR_MASK); - REG_WRITE(gpio->reg, reserved); - return (REG_READ(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0; + GMBUS_REG_WRITE(gpio->reg, reserved | GPIO_CLOCK_DIR_MASK); + GMBUS_REG_WRITE(gpio->reg, reserved); + return (GMBUS_REG_READ(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0; } static int get_data(void *data) { struct intel_gpio *gpio = data; struct drm_psb_private *dev_priv = gpio->dev_priv; - struct drm_device *dev = dev_priv->dev; u32 reserved = get_reserved(gpio); - REG_WRITE(gpio->reg, reserved | GPIO_DATA_DIR_MASK); - REG_WRITE(gpio->reg, reserved); - return (REG_READ(gpio->reg) & GPIO_DATA_VAL_IN) != 0; + GMBUS_REG_WRITE(gpio->reg, reserved | GPIO_DATA_DIR_MASK); + GMBUS_REG_WRITE(gpio->reg, reserved); + return (GMBUS_REG_READ(gpio->reg) & GPIO_DATA_VAL_IN) != 0; } static void set_clock(void *data, int state_high) { struct intel_gpio *gpio = data; struct drm_psb_private *dev_priv = gpio->dev_priv; - struct drm_device *dev = dev_priv->dev; u32 reserved = get_reserved(gpio); u32 clock_bits; @@ -146,15 +145,14 @@ static void set_clock(void *data, int state_high) clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | GPIO_CLOCK_VAL_MASK; - REG_WRITE(gpio->reg, reserved | clock_bits); - REG_READ(gpio->reg); /* Posting */ + GMBUS_REG_WRITE(gpio->reg, reserved | clock_bits); + GMBUS_REG_READ(gpio->reg); /* Posting */ } static void set_data(void *data, int state_high) { struct intel_gpio *gpio = data; struct drm_psb_private *dev_priv = gpio->dev_priv; - struct drm_device *dev = dev_priv->dev; u32 reserved = get_reserved(gpio); u32 data_bits; @@ -164,8 +162,8 @@ static void set_data(void *data, int state_high) data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK | GPIO_DATA_VAL_MASK; - REG_WRITE(gpio->reg, reserved | data_bits); - REG_READ(gpio->reg); + GMBUS_REG_WRITE(gpio->reg, reserved | data_bits); + GMBUS_REG_READ(gpio->reg); } static struct i2c_adapter * @@ -252,7 +250,6 @@ gmbus_xfer(struct i2c_adapter *adapter, struct intel_gmbus, adapter); struct drm_psb_private *dev_priv = adapter->algo_data; - struct drm_device *dev = dev_priv->dev; int i, reg_offset; if (bus->force_bit) @@ -261,28 +258,30 @@ gmbus_xfer(struct i2c_adapter *adapter, reg_offset = 0; - REG_WRITE(GMBUS0 + reg_offset, bus->reg0); + GMBUS_REG_WRITE(GMBUS0 + reg_offset, bus->reg0); for (i = 0; i < num; i++) { u16 len = msgs[i].len; u8 *buf = msgs[i].buf; if (msgs[i].flags & I2C_M_RD) { - REG_WRITE(GMBUS1 + reg_offset, - GMBUS_CYCLE_WAIT | (i + 1 == num ? GMBUS_CYCLE_STOP : 0) | - (len << GMBUS_BYTE_COUNT_SHIFT) | - (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) | - GMBUS_SLAVE_READ | GMBUS_SW_RDY); - REG_READ(GMBUS2+reg_offset); + GMBUS_REG_WRITE(GMBUS1 + reg_offset, + GMBUS_CYCLE_WAIT | + (i + 1 == num ? GMBUS_CYCLE_STOP : 0) | + (len << GMBUS_BYTE_COUNT_SHIFT) | + (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) | + GMBUS_SLAVE_READ | GMBUS_SW_RDY); + GMBUS_REG_READ(GMBUS2+reg_offset); do { u32 val, loop = 0; - if (wait_for(REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50)) + if (wait_for(GMBUS_REG_READ(GMBUS2 + reg_offset) & + (GMBUS_SATOER | GMBUS_HW_RDY), 50)) goto timeout; - if (REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) + if (GMBUS_REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) goto clear_err; - val = REG_READ(GMBUS3 + reg_offset); + val = GMBUS_REG_READ(GMBUS3 + reg_offset); do { *buf++ = val & 0xff; val >>= 8; @@ -296,18 +295,20 @@ gmbus_xfer(struct i2c_adapter *adapter, val |= *buf++ << (8 * loop); } while (--len && ++loop < 4); - REG_WRITE(GMBUS3 + reg_offset, val); - REG_WRITE(GMBUS1 + reg_offset, + GMBUS_REG_WRITE(GMBUS3 + reg_offset, val); + GMBUS_REG_WRITE(GMBUS1 + reg_offset, (i + 1 == num ? GMBUS_CYCLE_STOP : GMBUS_CYCLE_WAIT) | (msgs[i].len << GMBUS_BYTE_COUNT_SHIFT) | (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) | GMBUS_SLAVE_WRITE | GMBUS_SW_RDY); - REG_READ(GMBUS2+reg_offset); + GMBUS_REG_READ(GMBUS2+reg_offset); while (len) { - if (wait_for(REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50)) + if (wait_for(GMBUS_REG_READ(GMBUS2 + reg_offset) & + (GMBUS_SATOER | GMBUS_HW_RDY), 50)) goto timeout; - if (REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) + if (GMBUS_REG_READ(GMBUS2 + reg_offset) & + GMBUS_SATOER) goto clear_err; val = loop = 0; @@ -315,14 +316,14 @@ gmbus_xfer(struct i2c_adapter *adapter, val |= *buf++ << (8 * loop); } while (--len && ++loop < 4); - REG_WRITE(GMBUS3 + reg_offset, val); - REG_READ(GMBUS2+reg_offset); + GMBUS_REG_WRITE(GMBUS3 + reg_offset, val); + GMBUS_REG_READ(GMBUS2+reg_offset); } } - if (i + 1 < num && wait_for(REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), 50)) + if (i + 1 < num && wait_for(GMBUS_REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), 50)) goto timeout; - if (REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) + if (GMBUS_REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) goto clear_err; } @@ -333,20 +334,20 @@ clear_err: * of resetting the GMBUS controller and so clearing the * BUS_ERROR raised by the slave's NAK. */ - REG_WRITE(GMBUS1 + reg_offset, GMBUS_SW_CLR_INT); - REG_WRITE(GMBUS1 + reg_offset, 0); + GMBUS_REG_WRITE(GMBUS1 + reg_offset, GMBUS_SW_CLR_INT); + GMBUS_REG_WRITE(GMBUS1 + reg_offset, 0); done: /* Mark the GMBUS interface as disabled. We will re-enable it at the * start of the next xfer, till then let it sleep. */ - REG_WRITE(GMBUS0 + reg_offset, 0); + GMBUS_REG_WRITE(GMBUS0 + reg_offset, 0); return i; timeout: DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n", bus->reg0 & 0xff, bus->adapter.name); - REG_WRITE(GMBUS0 + reg_offset, 0); + GMBUS_REG_WRITE(GMBUS0 + reg_offset, 0); /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */ bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff); @@ -395,11 +396,16 @@ 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; + if (IS_MRST(dev)) + dev_priv->gmbus_reg = dev_priv->aux_reg; + else + dev_priv->gmbus_reg = dev_priv->vdc_reg; + for (i = 0; i < GMBUS_NUM_PORTS; i++) { struct intel_gmbus *bus = &dev_priv->gmbus[i]; @@ -488,6 +494,7 @@ void gma_intel_teardown_gmbus(struct drm_device *dev) i2c_del_adapter(&bus->adapter); } + dev_priv->gmbus_reg = NULL; /* iounmap is done in driver_unload */ kfree(dev_priv->gmbus); dev_priv->gmbus = NULL; } diff --git a/drivers/gpu/drm/gma500/mdfld_device.c b/drivers/gpu/drm/gma500/mdfld_device.c new file mode 100644 index 00000000000..265ad0de44a --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_device.c @@ -0,0 +1,551 @@ +/************************************************************************** + * Copyright (c) 2011, Intel Corporation. + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + **************************************************************************/ + +#include "psb_drv.h" +#include "mid_bios.h" +#include "mdfld_output.h" +#include "mdfld_dsi_output.h" +#include "tc35876x-dsi-lvds.h" + +#include <asm/intel_scu_ipc.h> + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + +#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF +#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */ +#define BLC_PWM_FREQ_CALC_CONSTANT 32 +#define MHz 1000000 +#define BRIGHTNESS_MIN_LEVEL 1 +#define BRIGHTNESS_MAX_LEVEL 100 +#define BRIGHTNESS_MASK 0xFF +#define BLC_POLARITY_NORMAL 0 +#define BLC_POLARITY_INVERSE 1 +#define BLC_ADJUSTMENT_MAX 100 + +#define MDFLD_BLC_PWM_PRECISION_FACTOR 10 +#define MDFLD_BLC_MAX_PWM_REG_FREQ 0xFFFE +#define MDFLD_BLC_MIN_PWM_REG_FREQ 0x2 + +#define MDFLD_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) +#define MDFLD_BACKLIGHT_PWM_CTL_SHIFT (16) + +static struct backlight_device *mdfld_backlight_device; + +int mdfld_set_brightness(struct backlight_device *bd) +{ + struct drm_device *dev = + (struct drm_device *)bl_get_data(mdfld_backlight_device); + struct drm_psb_private *dev_priv = dev->dev_private; + int level = bd->props.brightness; + + DRM_DEBUG_DRIVER("backlight level set to %d\n", level); + + /* Perform value bounds checking */ + if (level < BRIGHTNESS_MIN_LEVEL) + level = BRIGHTNESS_MIN_LEVEL; + + if (gma_power_begin(dev, false)) { + u32 adjusted_level = 0; + + /* + * Adjust the backlight level with the percent in + * dev_priv->blc_adj2 + */ + adjusted_level = level * dev_priv->blc_adj2; + adjusted_level = adjusted_level / BLC_ADJUSTMENT_MAX; + dev_priv->brightness_adjusted = adjusted_level; + + if (mdfld_get_panel_type(dev, 0) == TC35876X) { + if (dev_priv->dpi_panel_on[0] || + dev_priv->dpi_panel_on[2]) + tc35876x_brightness_control(dev, + dev_priv->brightness_adjusted); + } else { + if (dev_priv->dpi_panel_on[0]) + mdfld_dsi_brightness_control(dev, 0, + dev_priv->brightness_adjusted); + } + + if (dev_priv->dpi_panel_on[2]) + mdfld_dsi_brightness_control(dev, 2, + dev_priv->brightness_adjusted); + gma_power_end(dev); + } + + /* cache the brightness for later use */ + dev_priv->brightness = level; + return 0; +} + +static int mdfld_get_brightness(struct backlight_device *bd) +{ + struct drm_device *dev = + (struct drm_device *)bl_get_data(mdfld_backlight_device); + struct drm_psb_private *dev_priv = dev->dev_private; + + DRM_DEBUG_DRIVER("brightness = 0x%x \n", dev_priv->brightness); + + /* return locally cached var instead of HW read (due to DPST etc.) */ + return dev_priv->brightness; +} + +static const struct backlight_ops mdfld_ops = { + .get_brightness = mdfld_get_brightness, + .update_status = mdfld_set_brightness, +}; + +static int device_backlight_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = (struct drm_psb_private *) + dev->dev_private; + + dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX; + dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX; + + return 0; +} + +static int mdfld_backlight_init(struct drm_device *dev) +{ + struct backlight_properties props; + int ret = 0; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = BRIGHTNESS_MAX_LEVEL; + props.type = BACKLIGHT_PLATFORM; + mdfld_backlight_device = backlight_device_register("mdfld-bl", + NULL, (void *)dev, &mdfld_ops, &props); + + if (IS_ERR(mdfld_backlight_device)) + return PTR_ERR(mdfld_backlight_device); + + ret = device_backlight_init(dev); + if (ret) + return ret; + + mdfld_backlight_device->props.brightness = BRIGHTNESS_MAX_LEVEL; + mdfld_backlight_device->props.max_brightness = BRIGHTNESS_MAX_LEVEL; + backlight_update_status(mdfld_backlight_device); + return 0; +} +#endif + +struct backlight_device *mdfld_get_backlight_device(void) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + return mdfld_backlight_device; +#else + return NULL; +#endif +} + +/* + * mdfld_save_display_registers + * + * Description: We are going to suspend so save current display + * register state. + * + * Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio + */ +static int mdfld_save_display_registers(struct drm_device *dev, int pipenum) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct medfield_state *regs = &dev_priv->regs.mdfld; + struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum]; + const struct psb_offset *map = &dev_priv->regmap[pipenum]; + int i; + u32 *mipi_val; + + /* register */ + u32 mipi_reg = MIPI; + + switch (pipenum) { + case 0: + mipi_val = ®s->saveMIPI; + break; + case 1: + mipi_val = ®s->saveMIPI; + break; + case 2: + /* register */ + mipi_reg = MIPI_C; + /* pointer to values */ + mipi_val = ®s->saveMIPI_C; + break; + default: + DRM_ERROR("%s, invalid pipe number.\n", __func__); + return -EINVAL; + } + + /* Pipe & plane A info */ + pipe->dpll = PSB_RVDC32(map->dpll); + pipe->fp0 = PSB_RVDC32(map->fp0); + pipe->conf = PSB_RVDC32(map->conf); + pipe->htotal = PSB_RVDC32(map->htotal); + pipe->hblank = PSB_RVDC32(map->hblank); + pipe->hsync = PSB_RVDC32(map->hsync); + pipe->vtotal = PSB_RVDC32(map->vtotal); + pipe->vblank = PSB_RVDC32(map->vblank); + pipe->vsync = PSB_RVDC32(map->vsync); + pipe->src = PSB_RVDC32(map->src); + pipe->stride = PSB_RVDC32(map->stride); + pipe->linoff = PSB_RVDC32(map->linoff); + pipe->tileoff = PSB_RVDC32(map->tileoff); + pipe->size = PSB_RVDC32(map->size); + pipe->pos = PSB_RVDC32(map->pos); + pipe->surf = PSB_RVDC32(map->surf); + pipe->cntr = PSB_RVDC32(map->cntr); + pipe->status = PSB_RVDC32(map->status); + + /*save palette (gamma) */ + for (i = 0; i < 256; i++) + pipe->palette[i] = PSB_RVDC32(map->palette + (i << 2)); + + if (pipenum == 1) { + regs->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL); + regs->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS); + + regs->saveHDMIPHYMISCCTL = PSB_RVDC32(HDMIPHYMISCCTL); + regs->saveHDMIB_CONTROL = PSB_RVDC32(HDMIB_CONTROL); + return 0; + } + + *mipi_val = PSB_RVDC32(mipi_reg); + return 0; +} + +/* + * mdfld_restore_display_registers + * + * Description: We are going to resume so restore display register state. + * + * Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio + */ +static int mdfld_restore_display_registers(struct drm_device *dev, int pipenum) +{ + /* To get panel out of ULPS mode. */ + u32 temp = 0; + u32 device_ready_reg = DEVICE_READY_REG; + struct drm_psb_private *dev_priv = dev->dev_private; + struct mdfld_dsi_config *dsi_config = NULL; + struct medfield_state *regs = &dev_priv->regs.mdfld; + struct psb_pipe *pipe = &dev_priv->regs.pipe[pipenum]; + const struct psb_offset *map = &dev_priv->regmap[pipenum]; + u32 i; + u32 dpll; + u32 timeout = 0; + + /* register */ + u32 mipi_reg = MIPI; + + /* values */ + u32 dpll_val = pipe->dpll; + u32 mipi_val = regs->saveMIPI; + + switch (pipenum) { + case 0: + dpll_val &= ~DPLL_VCO_ENABLE; + dsi_config = dev_priv->dsi_configs[0]; + break; + case 1: + dpll_val &= ~DPLL_VCO_ENABLE; + break; + case 2: + mipi_reg = MIPI_C; + mipi_val = regs->saveMIPI_C; + dsi_config = dev_priv->dsi_configs[1]; + break; + default: + DRM_ERROR("%s, invalid pipe number.\n", __func__); + return -EINVAL; + } + + /*make sure VGA plane is off. it initializes to on after reset!*/ + PSB_WVDC32(0x80000000, VGACNTRL); + + if (pipenum == 1) { + PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, map->dpll); + PSB_RVDC32(map->dpll); + + PSB_WVDC32(pipe->fp0, map->fp0); + } else { + + dpll = PSB_RVDC32(map->dpll); + + if (!(dpll & DPLL_VCO_ENABLE)) { + + /* When ungating power of DPLL, needs to wait 0.5us + before enable the VCO */ + if (dpll & MDFLD_PWR_GATE_EN) { + dpll &= ~MDFLD_PWR_GATE_EN; + PSB_WVDC32(dpll, map->dpll); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + } + + PSB_WVDC32(pipe->fp0, map->fp0); + PSB_WVDC32(dpll_val, map->dpll); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + dpll_val |= DPLL_VCO_ENABLE; + PSB_WVDC32(dpll_val, map->dpll); + PSB_RVDC32(map->dpll); + + /* wait for DSI PLL to lock */ + while (timeout < 20000 && + !(PSB_RVDC32(map->conf) & PIPECONF_DSIPLL_LOCK)) { + udelay(150); + timeout++; + } + + if (timeout == 20000) { + DRM_ERROR("%s, can't lock DSIPLL.\n", + __func__); + return -EINVAL; + } + } + } + /* Restore mode */ + PSB_WVDC32(pipe->htotal, map->htotal); + PSB_WVDC32(pipe->hblank, map->hblank); + PSB_WVDC32(pipe->hsync, map->hsync); + PSB_WVDC32(pipe->vtotal, map->vtotal); + PSB_WVDC32(pipe->vblank, map->vblank); + PSB_WVDC32(pipe->vsync, map->vsync); + PSB_WVDC32(pipe->src, map->src); + PSB_WVDC32(pipe->status, map->status); + + /*set up the plane*/ + PSB_WVDC32(pipe->stride, map->stride); + PSB_WVDC32(pipe->linoff, map->linoff); + PSB_WVDC32(pipe->tileoff, map->tileoff); + PSB_WVDC32(pipe->size, map->size); + PSB_WVDC32(pipe->pos, map->pos); + PSB_WVDC32(pipe->surf, map->surf); + + if (pipenum == 1) { + /* restore palette (gamma) */ + /*DRM_UDELAY(50000); */ + for (i = 0; i < 256; i++) + PSB_WVDC32(pipe->palette[i], map->palette + (i << 2)); + + PSB_WVDC32(regs->savePFIT_CONTROL, PFIT_CONTROL); + PSB_WVDC32(regs->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS); + + /*TODO: resume HDMI port */ + + /*TODO: resume pipe*/ + + /*enable the plane*/ + PSB_WVDC32(pipe->cntr & ~DISPLAY_PLANE_ENABLE, map->cntr); + + return 0; + } + + /*set up pipe related registers*/ + PSB_WVDC32(mipi_val, mipi_reg); + + /*setup MIPI adapter + MIPI IP registers*/ + if (dsi_config) + mdfld_dsi_controller_init(dsi_config, pipenum); + + if (in_atomic() || in_interrupt()) + mdelay(20); + else + msleep(20); + + /*enable the plane*/ + PSB_WVDC32(pipe->cntr, map->cntr); + + if (in_atomic() || in_interrupt()) + mdelay(20); + else + msleep(20); + + /* LP Hold Release */ + temp = REG_READ(mipi_reg); + temp |= LP_OUTPUT_HOLD_RELEASE; + REG_WRITE(mipi_reg, temp); + mdelay(1); + + + /* Set DSI host to exit from Utra Low Power State */ + temp = REG_READ(device_ready_reg); + temp &= ~ULPS_MASK; + temp |= 0x3; + temp |= EXIT_ULPS_DEV_READY; + REG_WRITE(device_ready_reg, temp); + mdelay(1); + + temp = REG_READ(device_ready_reg); + temp &= ~ULPS_MASK; + temp |= EXITING_ULPS; + REG_WRITE(device_ready_reg, temp); + mdelay(1); + + /*enable the pipe*/ + PSB_WVDC32(pipe->conf, map->conf); + + /* restore palette (gamma) */ + /*DRM_UDELAY(50000); */ + for (i = 0; i < 256; i++) + PSB_WVDC32(pipe->palette[i], map->palette + (i << 2)); + + return 0; +} + +static int mdfld_save_registers(struct drm_device *dev) +{ + /* mdfld_save_cursor_overlay_registers(dev); */ + mdfld_save_display_registers(dev, 0); + mdfld_save_display_registers(dev, 2); + mdfld_disable_crtc(dev, 0); + mdfld_disable_crtc(dev, 2); + + return 0; +} + +static int mdfld_restore_registers(struct drm_device *dev) +{ + mdfld_restore_display_registers(dev, 2); + mdfld_restore_display_registers(dev, 0); + /* mdfld_restore_cursor_overlay_registers(dev); */ + + return 0; +} + +static int mdfld_power_down(struct drm_device *dev) +{ + /* FIXME */ + return 0; +} + +static int mdfld_power_up(struct drm_device *dev) +{ + /* FIXME */ + return 0; +} + +/* Medfield */ +static const struct psb_offset mdfld_regmap[3] = { + { + .fp0 = MRST_FPA0, + .fp1 = MRST_FPA1, + .cntr = DSPACNTR, + .conf = PIPEACONF, + .src = PIPEASRC, + .dpll = MRST_DPLL_A, + .htotal = HTOTAL_A, + .hblank = HBLANK_A, + .hsync = HSYNC_A, + .vtotal = VTOTAL_A, + .vblank = VBLANK_A, + .vsync = VSYNC_A, + .stride = DSPASTRIDE, + .size = DSPASIZE, + .pos = DSPAPOS, + .surf = DSPASURF, + .addr = MRST_DSPABASE, + .status = PIPEASTAT, + .linoff = DSPALINOFF, + .tileoff = DSPATILEOFF, + .palette = PALETTE_A, + }, + { + .fp0 = MDFLD_DPLL_DIV0, + .cntr = DSPBCNTR, + .conf = PIPEBCONF, + .src = PIPEBSRC, + .dpll = MDFLD_DPLL_B, + .htotal = HTOTAL_B, + .hblank = HBLANK_B, + .hsync = HSYNC_B, + .vtotal = VTOTAL_B, + .vblank = VBLANK_B, + .vsync = VSYNC_B, + .stride = DSPBSTRIDE, + .size = DSPBSIZE, + .pos = DSPBPOS, + .surf = DSPBSURF, + .addr = MRST_DSPBBASE, + .status = PIPEBSTAT, + .linoff = DSPBLINOFF, + .tileoff = DSPBTILEOFF, + .palette = PALETTE_B, + }, + { + .fp0 = MRST_FPA0, /* This is what the old code did ?? */ + .cntr = DSPCCNTR, + .conf = PIPECCONF, + .src = PIPECSRC, + /* No DPLL_C */ + .dpll = MRST_DPLL_A, + .htotal = HTOTAL_C, + .hblank = HBLANK_C, + .hsync = HSYNC_C, + .vtotal = VTOTAL_C, + .vblank = VBLANK_C, + .vsync = VSYNC_C, + .stride = DSPCSTRIDE, + .size = DSPBSIZE, + .pos = DSPCPOS, + .surf = DSPCSURF, + .addr = MDFLD_DSPCBASE, + .status = PIPECSTAT, + .linoff = DSPCLINOFF, + .tileoff = DSPCTILEOFF, + .palette = PALETTE_C, + }, +}; + +static int mdfld_chip_setup(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + if (pci_enable_msi(dev->pdev)) + dev_warn(dev->dev, "Enabling MSI failed!\n"); + dev_priv->regmap = mdfld_regmap; + return mid_chip_setup(dev); +} + +const struct psb_ops mdfld_chip_ops = { + .name = "mdfld", + .accel_2d = 0, + .pipes = 3, + .crtcs = 3, + .lvds_mask = (1 << 1), + .hdmi_mask = (1 << 1), + .cursor_needs_phys = 0, + .sgx_offset = MRST_SGX_OFFSET, + + .chip_setup = mdfld_chip_setup, + .crtc_helper = &mdfld_helper_funcs, + .crtc_funcs = &psb_intel_crtc_funcs, + + .output_init = mdfld_output_init, + +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + .backlight_init = mdfld_backlight_init, +#endif + + .save_regs = mdfld_save_registers, + .restore_regs = mdfld_restore_registers, + .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 00000000000..d4813e03f5e --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_dpi.c @@ -0,0 +1,1016 @@ +/* + * 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, + const 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(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 00000000000..2b40663e169 --- /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, + const 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 00000000000..6e91b20ce2e --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c @@ -0,0 +1,614 @@ +/* + * 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; + struct drm_psb_private *dev_priv; + u32 gen_ctrl_val; + + if (!sender) { + DRM_ERROR("No sender found\n"); + return; + } + + dev = sender->dev; + dev_priv = dev->dev_private; + + /* 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 gma_crtc *gma_crtc = to_gma_crtc(encoder->crtc); + bool centerechange; + uint64_t val; + + if (!gma_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_object_property_get_value(&connector->base, property, &val)) + goto set_prop_error; + + if (val == value) + goto set_prop_done; + + if (drm_object_property_set_value(&connector->base, + property, value)) + goto set_prop_error; + + centerechange = (val == DRM_MODE_SCALE_NO_SCALE) || + (value == DRM_MODE_SCALE_NO_SCALE); + + if (gma_crtc->saved_mode.hdisplay != 0 && + gma_crtc->saved_mode.vdisplay != 0) { + if (centerechange) { + if (!drm_crtc_helper_set_mode(encoder->crtc, + &gma_crtc->saved_mode, + encoder->crtc->x, + encoder->crtc->y, + encoder->crtc->primary->fb)) + goto set_prop_error; + } else { + struct drm_encoder_helper_funcs *funcs = + encoder->helper_private; + funcs->mode_set(encoder, + &gma_crtc->saved_mode, + &gma_crtc->saved_adjusted_mode); + } + } + } else if (!strcmp(property->name, "backlight") && encoder) { + if (drm_object_property_set_value(&connector->base, property, + value)) + goto set_prop_error; + else + gma_backlight_set(encoder->dev, value); + } +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 (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_object_attach_property(&connector->base, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_FULLSCREEN); + drm_object_attach_property(&connector->base, + 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 00000000000..5b646c1f0c3 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.h @@ -0,0 +1,377 @@ +/* + * 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 <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/intel-mid.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 gma_connector base; + + int pipe; + void *private; + void *pkg_sender; + + /* Connection status */ + enum drm_connector_status status; +}; + +struct mdfld_dsi_encoder { + struct gma_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 gma_connector *gma_connector; + + gma_connector = to_gma_connector(connector); + + return container_of(gma_connector, struct mdfld_dsi_connector, base); +} + +static inline struct mdfld_dsi_encoder *mdfld_dsi_encoder( + struct drm_encoder *encoder) +{ + struct gma_encoder *gma_encoder; + + gma_encoder = to_gma_encoder(encoder); + + return container_of(gma_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 00000000000..87885d8c06e --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c @@ -0,0 +1,688 @@ +/* + * 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; + struct drm_psb_private *dev_priv = dev->dev_private; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + 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*/ + /* FIXME: should just copy the regmap ptr ? */ + pkg_sender->dpll_reg = map->dpll; + pkg_sender->dspcntr_reg = map->cntr; + pkg_sender->pipeconf_reg = map->conf; + pkg_sender->dsplinoff_reg = map->linoff; + pkg_sender->dspsurf_reg = map->surf; + pkg_sender->pipestat_reg = map->status; + + 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 00000000000..459cd7ea8b8 --- /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 00000000000..8cc8a5abbc7 --- /dev/null +++ b/drivers/gpu/drm/gma500/mdfld_intel_display.c @@ -0,0 +1,1035 @@ +/* + * 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 "gma_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) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + int count, temp; + + switch (pipe) { + case 0: + case 1: + case 2: + break; + default: + DRM_ERROR("Illegal Pipe Number.\n"); + return; + } + + /* FIXME JLIU7_PO */ + gma_wait_for_vblank(dev); + return; + + /* Wait for for the pipe disable to take effect. */ + for (count = 0; count < COUNT_MAX; count++) { + temp = REG_READ(map->conf); + if ((temp & PIPEACONF_PIPE_STATE) == 0) + break; + } +} + +void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + int count, temp; + + switch (pipe) { + case 0: + case 1: + case 2: + break; + default: + DRM_ERROR("Illegal Pipe Number.\n"); + return; + } + + /* FIXME JLIU7_PO */ + gma_wait_for_vblank(dev); + return; + + /* Wait for for the pipe enable to take effect. */ + for (count = 0; count < COUNT_MAX; count++) { + temp = REG_READ(map->conf); + if ((temp & PIPEACONF_PIPE_STATE) == 1) + break; + } +} + +/** + * 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_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb); + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + unsigned long start, offset; + 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->primary->fb) { + dev_dbg(dev->dev, "No FB bound\n"); + return 0; + } + + ret = check_fb(crtc->primary->fb); + if (ret) + return ret; + + if (pipe > 2) { + DRM_ERROR("Illegal Pipe Number.\n"); + return -EINVAL; + } + + if (!gma_power_begin(dev, true)) + return 0; + + start = psbfb->gtt->offset; + offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8); + + REG_WRITE(map->stride, crtc->primary->fb->pitches[0]); + dspcntr = REG_READ(map->cntr); + dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; + + switch (crtc->primary->fb->bits_per_pixel) { + case 8: + dspcntr |= DISPPLANE_8BPP; + break; + case 16: + if (crtc->primary->fb->depth == 15) + dspcntr |= DISPPLANE_15_16BPP; + else + dspcntr |= DISPPLANE_16BPP; + break; + case 24: + case 32: + dspcntr |= DISPPLANE_32BPP_NO_ALPHA; + break; + } + REG_WRITE(map->cntr, dspcntr); + + dev_dbg(dev->dev, "Writing base %08lX %08lX %d %d\n", + start, offset, x, y); + REG_WRITE(map->linoff, offset); + REG_READ(map->linoff); + REG_WRITE(map->surf, start); + REG_READ(map->surf); + + gma_power_end(dev); + + return 0; +} + +/* + * Disable the pipe, plane and pll. + * + */ +void mdfld_disable_crtc(struct drm_device *dev, int pipe) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + u32 temp; + + dev_dbg(dev->dev, "pipe = %d\n", pipe); + + + 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(map->cntr); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(map->cntr, + temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(map->base, REG_READ(map->base)); + REG_READ(map->base); + } + + /* FIXME_JLIU7 MDFLD_PO revisit */ + + /* Next, disable display pipes */ + temp = REG_READ(map->conf); + if ((temp & PIPEACONF_ENABLE) != 0) { + temp &= ~PIPEACONF_ENABLE; + temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF; + REG_WRITE(map->conf, temp); + REG_READ(map->conf); + + /* Wait for for the pipe disable to take effect. */ + mdfldWaitForPipeDisable(dev, pipe); + } + + temp = REG_READ(map->dpll); + if (temp & DPLL_VCO_ENABLE) { + if ((pipe != 1 && + !((REG_READ(PIPEACONF) | REG_READ(PIPECCONF)) + & PIPEACONF_ENABLE)) || pipe == 1) { + temp &= ~(DPLL_VCO_ENABLE); + REG_WRITE(map->dpll, temp); + REG_READ(map->dpll); + /* 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(map->dpll, 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 gma_crtc *gma_crtc = to_gma_crtc(crtc); + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; + u32 pipeconf = dev_priv->pipeconf[pipe]; + u32 temp; + int timeout = 0; + + dev_dbg(dev->dev, "mode = %d, pipe = %d\n", mode, pipe); + + /* Note: Old code uses pipe a stat for pipe b but that appears + to be a bug */ + + 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(map->dpll); + + 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(map->dpll, temp); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + } + + REG_WRITE(map->dpll, temp); + REG_READ(map->dpll); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + REG_WRITE(map->dpll, temp | DPLL_VCO_ENABLE); + REG_READ(map->dpll); + + /** + * 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(map->conf) & PIPECONF_DSIPLL_LOCK)) { + udelay(150); + timeout++; + } + } + + /* Enable the plane */ + temp = REG_READ(map->cntr); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + REG_WRITE(map->cntr, + temp | DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(map->base, REG_READ(map->base)); + } + + /* Enable the pipe */ + temp = REG_READ(map->conf); + if ((temp & PIPEACONF_ENABLE) == 0) { + REG_WRITE(map->conf, 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(map->status, REG_READ(map->status)); + msleep(100); + if (PIPE_VBLANK_STATUS & REG_READ(map->status)) + dev_dbg(dev->dev, "OK"); + else { + dev_dbg(dev->dev, "STUCK!!!!"); + /*shutdown controller*/ + temp = REG_READ(map->cntr); + REG_WRITE(map->cntr, + temp & ~DISPLAY_PLANE_ENABLE); + REG_WRITE(map->base, REG_READ(map->base)); + /*mdfld_dsi_dpi_shut_down(dev, pipe);*/ + REG_WRITE(0xb048, 1); + msleep(100); + temp = REG_READ(map->conf); + temp &= ~PIPEACONF_ENABLE; + REG_WRITE(map->conf, 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(map->cntr); + REG_WRITE(map->cntr, + temp | DISPLAY_PLANE_ENABLE); + REG_WRITE(map->base, REG_READ(map->base)); + /*mdfld_dsi_dpi_turn_on(dev, pipe);*/ + REG_WRITE(0xb048, 2); + msleep(100); + temp = REG_READ(map->conf); + temp |= PIPEACONF_ENABLE; + REG_WRITE(map->conf, temp); + } + } + + gma_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(map->cntr); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE(map->cntr, + temp & ~DISPLAY_PLANE_ENABLE); + /* Flush the plane changes */ + REG_WRITE(map->base, REG_READ(map->base)); + REG_READ(map->base); + } + + /* Next, disable display pipes */ + temp = REG_READ(map->conf); + if ((temp & PIPEACONF_ENABLE) != 0) { + temp &= ~PIPEACONF_ENABLE; + temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF; + REG_WRITE(map->conf, temp); + REG_READ(map->conf); + + /* Wait for for the pipe disable to take effect. */ + mdfldWaitForPipeDisable(dev, pipe); + } + + temp = REG_READ(map->dpll); + if (temp & DPLL_VCO_ENABLE) { + if ((pipe != 1 && !((REG_READ(PIPEACONF) + | REG_READ(PIPECCONF)) & PIPEACONF_ENABLE)) + || pipe == 1) { + temp &= ~(DPLL_VCO_ENABLE); + REG_WRITE(map->dpll, temp); + REG_READ(map->dpll); + /* 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 (gma_pipe_has_type(crtc, INTEL_OUTPUT_MIPI) + || gma_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 (gma_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 gma_crtc *gma_crtc = to_gma_crtc(crtc); + struct drm_psb_private *dev_priv = dev->dev_private; + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[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 gma_encoder *gma_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 + + ret = check_fb(crtc->primary->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(&gma_crtc->saved_mode, mode, + sizeof(struct drm_display_mode)); + memcpy(&gma_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; + + gma_encoder = gma_attached_encoder(connector); + + switch (gma_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(map->size, ((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(map->src, ((mode->crtc_hdisplay - 1) << 16) + | (mode->crtc_vdisplay - 1)); + } else { + REG_WRITE(map->size, + ((mode->crtc_vdisplay - 1) << 16) | + (mode->crtc_hdisplay - 1)); + REG_WRITE(map->src, + ((mode->crtc_hdisplay - 1) << 16) | + (mode->crtc_vdisplay - 1)); + } + + REG_WRITE(map->pos, 0); + + if (gma_encoder) + drm_object_property_get_value(&connector->base, + 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(map->htotal, (mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(map->vtotal, (mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - + offsetX - 1) | + ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16)); + REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - + offsetX - 1) | + ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16)); + REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - + offsetY - 1) | + ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16)); + REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - + offsetY - 1) | + ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16)); + } else { + REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16)); + REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16)); + REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16)); + REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16)); + REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16)); + REG_WRITE(map->vsync, (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 */ + dev_priv->pipeconf[pipe] = PIPEACONF_ENABLE; /* FIXME_JLIU7 REG_READ(pipeconf_reg); */ + + /* Set up the display plane register */ + dev_priv->dspcntr[pipe] = REG_READ(map->cntr); + dev_priv->dspcntr[pipe] |= pipe << DISPPLANE_SEL_PIPE_POS; + dev_priv->dspcntr[pipe] |= 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(map->dpll); + + if (dpll & DPLL_VCO_ENABLE) { + dpll &= ~DPLL_VCO_ENABLE; + REG_WRITE(map->dpll, dpll); + REG_READ(map->dpll); + + /* 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(map->fp0, 0); + dpll &= ~MDFLD_P1_MASK; + REG_WRITE(map->dpll, 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(map->dpll, 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(map->fp0, fp); + REG_WRITE(map->dpll, dpll); + /* FIXME_MDFLD PO - change 500 to 1 after PO */ + udelay(500); + + dpll |= DPLL_VCO_ENABLE; + REG_WRITE(map->dpll, dpll); + REG_READ(map->dpll); + + /* wait for DSI PLL to lock */ + while (timeout < 20000 && + !(REG_READ(map->conf) & 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(map->conf, dev_priv->pipeconf[pipe]); + REG_READ(map->conf); + + /* Wait for for the pipe enable to take effect. */ + REG_WRITE(map->cntr, dev_priv->dspcntr[pipe]); + gma_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 = gma_crtc_mode_fixup, + .mode_set = mdfld_crtc_mode_set, + .mode_set_base = mdfld__intel_pipe_set_base, + .prepare = gma_crtc_prepare, + .commit = gma_crtc_commit, +}; + diff --git a/drivers/gpu/drm/gma500/intel_opregion.c b/drivers/gpu/drm/gma500/mdfld_output.c index d946bc1b17b..c95966bb0c9 100644 --- a/drivers/gpu/drm/gma500/intel_opregion.c +++ b/drivers/gpu/drm/gma500/mdfld_output.c @@ -1,10 +1,10 @@ /* - * Copyright 2010 Intel Corporation + * 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, sublicense, + * 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: * @@ -20,62 +20,55 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * - * FIXME: resolve with the i915 version - */ + * Authors: + * Thomas Eaton <thomas.g.eaton@intel.com> + * Scott Rowe <scott.m.rowe@intel.com> +*/ -#include "psb_drv.h" +#include "mdfld_output.h" +#include "mdfld_dsi_dpi.h" +#include "mdfld_dsi_output.h" -struct opregion_header { - u8 signature[16]; - u32 size; - u32 opregion_ver; - u8 bios_ver[32]; - u8 vbios_ver[16]; - u8 driver_ver[16]; - u32 mboxes; - u8 reserved[164]; -} __packed; +#include "tc35876x-dsi-lvds.h" -struct opregion_apci { - /*FIXME: add it later*/ -} __packed; - -struct opregion_swsci { - /*FIXME: add it later*/ -} __packed; - -struct opregion_acpi { - /*FIXME: add it later*/ -} __packed; - -int gma_intel_opregion_init(struct drm_device *dev) +int mdfld_get_panel_type(struct drm_device *dev, int pipe) { struct drm_psb_private *dev_priv = dev->dev_private; - u32 opregion_phy; - void *base; - u32 *lid_state; - - dev_priv->lid_state = NULL; - - pci_read_config_dword(dev->pdev, 0xfc, &opregion_phy); - if (opregion_phy == 0) - return -ENOTSUPP; - - base = ioremap(opregion_phy, 8*1024); - if (!base) - return -ENOMEM; - - lid_state = base + 0x01ac; + return dev_priv->mdfld_panel_id; +} - dev_priv->lid_state = lid_state; - dev_priv->lid_last_state = readl(lid_state); - return 0; +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 gma_intel_opregion_exit(struct drm_device *dev) + +int mdfld_output_init(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; - if (dev_priv->lid_state) - iounmap(dev_priv->lid_state); + + /* 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 00000000000..ab2b27c0f03 --- /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 00000000000..dc0c6c3d3d2 --- /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 00000000000..d8d4170725b --- /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/mid_bios.c b/drivers/gpu/drm/gma500/mid_bios.c index 5eee9ad80da..a97e38e284f 100644 --- a/drivers/gpu/drm/gma500/mid_bios.c +++ b/drivers/gpu/drm/gma500/mid_bios.c @@ -25,7 +25,7 @@ #include <drm/drmP.h> #include <drm/drm.h> -#include "gma_drm.h" +#include <drm/gma_drm.h> #include "psb_drv.h" #include "mid_bios.h" @@ -118,139 +118,214 @@ static void mid_get_pci_revID(struct drm_psb_private *dev_priv) dev_priv->platform_rev_id); } +struct mid_vbt_header { + u32 signature; + u8 revision; +} __packed; + +/* The same for r0 and r1 */ +struct vbt_r0 { + struct mid_vbt_header vbt_header; + u8 size; + u8 checksum; +} __packed; + +struct vbt_r10 { + struct mid_vbt_header vbt_header; + u8 checksum; + u16 size; + u8 panel_count; + u8 primary_panel_idx; + u8 secondary_panel_idx; + u8 __reserved[5]; +} __packed; + +static int read_vbt_r0(u32 addr, struct vbt_r0 *vbt) +{ + void __iomem *vbt_virtual; + + vbt_virtual = ioremap(addr, sizeof(*vbt)); + if (vbt_virtual == NULL) + return -1; + + memcpy_fromio(vbt, vbt_virtual, sizeof(*vbt)); + iounmap(vbt_virtual); + + return 0; +} + +static int read_vbt_r10(u32 addr, struct vbt_r10 *vbt) +{ + void __iomem *vbt_virtual; + + vbt_virtual = ioremap(addr, sizeof(*vbt)); + if (!vbt_virtual) + return -1; + + memcpy_fromio(vbt, vbt_virtual, sizeof(*vbt)); + iounmap(vbt_virtual); + + return 0; +} + +static int mid_get_vbt_data_r0(struct drm_psb_private *dev_priv, u32 addr) +{ + struct vbt_r0 vbt; + void __iomem *gct_virtual; + struct gct_r0 gct; + u8 bpi; + + if (read_vbt_r0(addr, &vbt)) + return -1; + + gct_virtual = ioremap(addr + sizeof(vbt), vbt.size - sizeof(vbt)); + if (!gct_virtual) + return -1; + memcpy_fromio(&gct, gct_virtual, sizeof(gct)); + iounmap(gct_virtual); + + bpi = gct.PD.BootPanelIndex; + dev_priv->gct_data.bpi = bpi; + dev_priv->gct_data.pt = gct.PD.PanelType; + dev_priv->gct_data.DTD = gct.panel[bpi].DTD; + dev_priv->gct_data.Panel_Port_Control = + gct.panel[bpi].Panel_Port_Control; + dev_priv->gct_data.Panel_MIPI_Display_Descriptor = + gct.panel[bpi].Panel_MIPI_Display_Descriptor; + + return 0; +} + +static int mid_get_vbt_data_r1(struct drm_psb_private *dev_priv, u32 addr) +{ + struct vbt_r0 vbt; + void __iomem *gct_virtual; + struct gct_r1 gct; + u8 bpi; + + if (read_vbt_r0(addr, &vbt)) + return -1; + + gct_virtual = ioremap(addr + sizeof(vbt), vbt.size - sizeof(vbt)); + if (!gct_virtual) + return -1; + memcpy_fromio(&gct, gct_virtual, sizeof(gct)); + iounmap(gct_virtual); + + bpi = gct.PD.BootPanelIndex; + dev_priv->gct_data.bpi = bpi; + dev_priv->gct_data.pt = gct.PD.PanelType; + dev_priv->gct_data.DTD = gct.panel[bpi].DTD; + dev_priv->gct_data.Panel_Port_Control = + gct.panel[bpi].Panel_Port_Control; + dev_priv->gct_data.Panel_MIPI_Display_Descriptor = + gct.panel[bpi].Panel_MIPI_Display_Descriptor; + + return 0; +} + +static int mid_get_vbt_data_r10(struct drm_psb_private *dev_priv, u32 addr) +{ + struct vbt_r10 vbt; + void __iomem *gct_virtual; + struct gct_r10 *gct; + struct oaktrail_timing_info *dp_ti = &dev_priv->gct_data.DTD; + struct gct_r10_timing_info *ti; + int ret = -1; + + if (read_vbt_r10(addr, &vbt)) + return -1; + + gct = kmalloc(sizeof(*gct) * vbt.panel_count, GFP_KERNEL); + if (!gct) + return -1; + + gct_virtual = ioremap(addr + sizeof(vbt), + sizeof(*gct) * vbt.panel_count); + if (!gct_virtual) + goto out; + memcpy_fromio(gct, gct_virtual, sizeof(*gct)); + iounmap(gct_virtual); + + dev_priv->gct_data.bpi = vbt.primary_panel_idx; + dev_priv->gct_data.Panel_MIPI_Display_Descriptor = + gct[vbt.primary_panel_idx].Panel_MIPI_Display_Descriptor; + + ti = &gct[vbt.primary_panel_idx].DTD; + dp_ti->pixel_clock = ti->pixel_clock; + dp_ti->hactive_hi = ti->hactive_hi; + dp_ti->hactive_lo = ti->hactive_lo; + dp_ti->hblank_hi = ti->hblank_hi; + dp_ti->hblank_lo = ti->hblank_lo; + dp_ti->hsync_offset_hi = ti->hsync_offset_hi; + dp_ti->hsync_offset_lo = ti->hsync_offset_lo; + dp_ti->hsync_pulse_width_hi = ti->hsync_pulse_width_hi; + dp_ti->hsync_pulse_width_lo = ti->hsync_pulse_width_lo; + dp_ti->vactive_hi = ti->vactive_hi; + dp_ti->vactive_lo = ti->vactive_lo; + dp_ti->vblank_hi = ti->vblank_hi; + dp_ti->vblank_lo = ti->vblank_lo; + dp_ti->vsync_offset_hi = ti->vsync_offset_hi; + dp_ti->vsync_offset_lo = ti->vsync_offset_lo; + dp_ti->vsync_pulse_width_hi = ti->vsync_pulse_width_hi; + dp_ti->vsync_pulse_width_lo = ti->vsync_pulse_width_lo; + + ret = 0; +out: + kfree(gct); + return ret; +} + static void mid_get_vbt_data(struct drm_psb_private *dev_priv) { struct drm_device *dev = dev_priv->dev; - struct oaktrail_vbt *vbt = &dev_priv->vbt_data; u32 addr; - u16 new_size; - u8 *vbt_virtual; - u8 bpi; - u8 number_desc = 0; - struct oaktrail_timing_info *dp_ti = &dev_priv->gct_data.DTD; - struct gct_r10_timing_info ti; - void *pGCT; + u8 __iomem *vbt_virtual; + struct mid_vbt_header vbt_header; struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0)); + int ret = -1; - /* Get the address of the platform config vbt, B0:D2:F0;0xFC */ + /* Get the address of the platform config vbt */ pci_read_config_dword(pci_gfx_root, 0xFC, &addr); pci_dev_put(pci_gfx_root); dev_dbg(dev->dev, "drm platform config address is %x\n", addr); - /* check for platform config address == 0. */ - /* this means fw doesn't support vbt */ - - if (addr == 0) { - vbt->size = 0; - return; - } + if (!addr) + goto out; /* get the virtual address of the vbt */ - vbt_virtual = ioremap(addr, sizeof(*vbt)); - if (vbt_virtual == NULL) { - vbt->size = 0; - return; - } + vbt_virtual = ioremap(addr, sizeof(vbt_header)); + if (!vbt_virtual) + goto out; - memcpy(vbt, vbt_virtual, sizeof(*vbt)); - iounmap(vbt_virtual); /* Free virtual address space */ + memcpy_fromio(&vbt_header, vbt_virtual, sizeof(vbt_header)); + iounmap(vbt_virtual); - /* No matching signature don't process the data */ - if (memcmp(vbt->signature, "$GCT", 4)) { - vbt->size = 0; - return; - } + if (memcmp(&vbt_header.signature, "$GCT", 4)) + goto out; + + dev_dbg(dev->dev, "GCT revision is %02x\n", vbt_header.revision); - dev_dbg(dev->dev, "GCT revision is %x\n", vbt->revision); - - switch (vbt->revision) { - case 0: - vbt->oaktrail_gct = ioremap(addr + sizeof(*vbt) - 4, - vbt->size - sizeof(*vbt) + 4); - pGCT = vbt->oaktrail_gct; - bpi = ((struct oaktrail_gct_v1 *)pGCT)->PD.BootPanelIndex; - dev_priv->gct_data.bpi = bpi; - dev_priv->gct_data.pt = - ((struct oaktrail_gct_v1 *)pGCT)->PD.PanelType; - memcpy(&dev_priv->gct_data.DTD, - &((struct oaktrail_gct_v1 *)pGCT)->panel[bpi].DTD, - sizeof(struct oaktrail_timing_info)); - dev_priv->gct_data.Panel_Port_Control = - ((struct oaktrail_gct_v1 *)pGCT)->panel[bpi].Panel_Port_Control; - dev_priv->gct_data.Panel_MIPI_Display_Descriptor = - ((struct oaktrail_gct_v1 *)pGCT)->panel[bpi].Panel_MIPI_Display_Descriptor; + switch (vbt_header.revision) { + case 0x00: + ret = mid_get_vbt_data_r0(dev_priv, addr); break; - case 1: - vbt->oaktrail_gct = ioremap(addr + sizeof(*vbt) - 4, - vbt->size - sizeof(*vbt) + 4); - pGCT = vbt->oaktrail_gct; - bpi = ((struct oaktrail_gct_v2 *)pGCT)->PD.BootPanelIndex; - dev_priv->gct_data.bpi = bpi; - dev_priv->gct_data.pt = - ((struct oaktrail_gct_v2 *)pGCT)->PD.PanelType; - memcpy(&dev_priv->gct_data.DTD, - &((struct oaktrail_gct_v2 *)pGCT)->panel[bpi].DTD, - sizeof(struct oaktrail_timing_info)); - dev_priv->gct_data.Panel_Port_Control = - ((struct oaktrail_gct_v2 *)pGCT)->panel[bpi].Panel_Port_Control; - dev_priv->gct_data.Panel_MIPI_Display_Descriptor = - ((struct oaktrail_gct_v2 *)pGCT)->panel[bpi].Panel_MIPI_Display_Descriptor; + case 0x01: + ret = mid_get_vbt_data_r1(dev_priv, addr); break; case 0x10: - /*header definition changed from rev 01 (v2) to rev 10h. */ - /*so, some values have changed location*/ - new_size = vbt->checksum; /*checksum contains lo size byte*/ - /*LSB of oaktrail_gct contains hi size byte*/ - new_size |= ((0xff & (unsigned int)(long)vbt->oaktrail_gct)) << 8; - - vbt->checksum = vbt->size; /*size contains the checksum*/ - if (new_size > 0xff) - vbt->size = 0xff; /*restrict size to 255*/ - else - vbt->size = new_size; - - /* number of descriptors defined in the GCT */ - number_desc = ((0xff00 & (unsigned int)(long)vbt->oaktrail_gct)) >> 8; - bpi = ((0xff0000 & (unsigned int)(long)vbt->oaktrail_gct)) >> 16; - vbt->oaktrail_gct = ioremap(addr + GCT_R10_HEADER_SIZE, - GCT_R10_DISPLAY_DESC_SIZE * number_desc); - pGCT = vbt->oaktrail_gct; - pGCT = (u8 *)pGCT + (bpi*GCT_R10_DISPLAY_DESC_SIZE); - dev_priv->gct_data.bpi = bpi; /*save boot panel id*/ - - /*copy the GCT display timings into a temp structure*/ - memcpy(&ti, pGCT, sizeof(struct gct_r10_timing_info)); - - /*now copy the temp struct into the dev_priv->gct_data*/ - dp_ti->pixel_clock = ti.pixel_clock; - dp_ti->hactive_hi = ti.hactive_hi; - dp_ti->hactive_lo = ti.hactive_lo; - dp_ti->hblank_hi = ti.hblank_hi; - dp_ti->hblank_lo = ti.hblank_lo; - dp_ti->hsync_offset_hi = ti.hsync_offset_hi; - dp_ti->hsync_offset_lo = ti.hsync_offset_lo; - dp_ti->hsync_pulse_width_hi = ti.hsync_pulse_width_hi; - dp_ti->hsync_pulse_width_lo = ti.hsync_pulse_width_lo; - dp_ti->vactive_hi = ti.vactive_hi; - dp_ti->vactive_lo = ti.vactive_lo; - dp_ti->vblank_hi = ti.vblank_hi; - dp_ti->vblank_lo = ti.vblank_lo; - dp_ti->vsync_offset_hi = ti.vsync_offset_hi; - dp_ti->vsync_offset_lo = ti.vsync_offset_lo; - dp_ti->vsync_pulse_width_hi = ti.vsync_pulse_width_hi; - dp_ti->vsync_pulse_width_lo = ti.vsync_pulse_width_lo; - - /* Move the MIPI_Display_Descriptor data from GCT to dev priv */ - dev_priv->gct_data.Panel_MIPI_Display_Descriptor = - *((u8 *)pGCT + 0x0d); - dev_priv->gct_data.Panel_MIPI_Display_Descriptor |= - (*((u8 *)pGCT + 0x0e)) << 8; + ret = mid_get_vbt_data_r10(dev_priv, addr); break; default: dev_err(dev->dev, "Unknown revision of GCT!\n"); - vbt->size = 0; } + +out: + if (ret) + dev_err(dev->dev, "Unable to read GCT!"); + else + dev_priv->has_gct = true; } int mid_chip_setup(struct drm_device *dev) diff --git a/drivers/gpu/drm/gma500/mmu.c b/drivers/gpu/drm/gma500/mmu.c index c904d73b1de..0eaf11c1993 100644 --- a/drivers/gpu/drm/gma500/mmu.c +++ b/drivers/gpu/drm/gma500/mmu.c @@ -18,6 +18,7 @@ #include <drm/drmP.h> #include "psb_drv.h" #include "psb_reg.h" +#include "mmu.h" /* * Code for the SGX MMU: @@ -47,51 +48,6 @@ * but on average it should be fast. */ -struct psb_mmu_driver { - /* protects driver- and pd structures. Always take in read mode - * before taking the page table spinlock. - */ - struct rw_semaphore sem; - - /* protects page tables, directory tables and pt tables. - * and pt structures. - */ - spinlock_t lock; - - atomic_t needs_tlbflush; - - uint8_t __iomem *register_map; - struct psb_mmu_pd *default_pd; - /*uint32_t bif_ctrl;*/ - int has_clflush; - int clflush_add; - unsigned long clflush_mask; - - struct drm_psb_private *dev_priv; -}; - -struct psb_mmu_pd; - -struct psb_mmu_pt { - struct psb_mmu_pd *pd; - uint32_t index; - uint32_t count; - struct page *p; - uint32_t *v; -}; - -struct psb_mmu_pd { - struct psb_mmu_driver *driver; - int hw_context; - struct psb_mmu_pt **tables; - struct page *p; - struct page *dummy_pt; - struct page *dummy_page; - uint32_t pd_mask; - uint32_t invalid_pde; - uint32_t invalid_pte; -}; - static inline uint32_t psb_mmu_pt_index(uint32_t offset) { return (offset >> PSB_PTE_SHIFT) & 0x3FF; @@ -102,13 +58,13 @@ static inline uint32_t psb_mmu_pd_index(uint32_t offset) return offset >> PSB_PDE_SHIFT; } +#if defined(CONFIG_X86) static inline void psb_clflush(void *addr) { __asm__ __volatile__("clflush (%0)\n" : : "r"(addr) : "memory"); } -static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, - void *addr) +static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, void *addr) { if (!driver->has_clflush) return; @@ -117,62 +73,77 @@ static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, psb_clflush(addr); mb(); } +#else -static void psb_page_clflush(struct psb_mmu_driver *driver, struct page* page) -{ - uint32_t clflush_add = driver->clflush_add >> PAGE_SHIFT; - uint32_t clflush_count = PAGE_SIZE / clflush_add; - int i; - uint8_t *clf; - - clf = kmap_atomic(page, KM_USER0); - mb(); - for (i = 0; i < clflush_count; ++i) { - psb_clflush(clf); - clf += clflush_add; - } - mb(); - kunmap_atomic(clf, KM_USER0); +static inline void psb_mmu_clflush(struct psb_mmu_driver *driver, void *addr) +{; } -static void psb_pages_clflush(struct psb_mmu_driver *driver, - struct page *page[], unsigned long num_pages) -{ - int i; +#endif - if (!driver->has_clflush) - return ; +static void psb_mmu_flush_pd_locked(struct psb_mmu_driver *driver, int force) +{ + struct drm_device *dev = driver->dev; + struct drm_psb_private *dev_priv = dev->dev_private; - for (i = 0; i < num_pages; i++) - psb_page_clflush(driver, *page++); -} + if (atomic_read(&driver->needs_tlbflush) || force) { + uint32_t val = PSB_RSGX32(PSB_CR_BIF_CTRL); + PSB_WSGX32(val | _PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL); -static void psb_mmu_flush_pd_locked(struct psb_mmu_driver *driver, - int force) -{ + /* Make sure data cache is turned off before enabling it */ + wmb(); + PSB_WSGX32(val & ~_PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL); + (void)PSB_RSGX32(PSB_CR_BIF_CTRL); + if (driver->msvdx_mmu_invaldc) + atomic_set(driver->msvdx_mmu_invaldc, 1); + } atomic_set(&driver->needs_tlbflush, 0); } +#if 0 static void psb_mmu_flush_pd(struct psb_mmu_driver *driver, int force) { down_write(&driver->sem); psb_mmu_flush_pd_locked(driver, force); up_write(&driver->sem); } +#endif -void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot) +void psb_mmu_flush(struct psb_mmu_driver *driver) { - if (rc_prot) - down_write(&driver->sem); - if (rc_prot) - up_write(&driver->sem); + struct drm_device *dev = driver->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + uint32_t val; + + down_write(&driver->sem); + val = PSB_RSGX32(PSB_CR_BIF_CTRL); + if (atomic_read(&driver->needs_tlbflush)) + PSB_WSGX32(val | _PSB_CB_CTRL_INVALDC, PSB_CR_BIF_CTRL); + else + PSB_WSGX32(val | _PSB_CB_CTRL_FLUSH, PSB_CR_BIF_CTRL); + + /* Make sure data cache is turned off and MMU is flushed before + restoring bank interface control register */ + wmb(); + PSB_WSGX32(val & ~(_PSB_CB_CTRL_FLUSH | _PSB_CB_CTRL_INVALDC), + PSB_CR_BIF_CTRL); + (void)PSB_RSGX32(PSB_CR_BIF_CTRL); + + atomic_set(&driver->needs_tlbflush, 0); + if (driver->msvdx_mmu_invaldc) + atomic_set(driver->msvdx_mmu_invaldc, 1); + up_write(&driver->sem); } void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context) { - /*ttm_tt_cache_flush(&pd->p, 1);*/ - psb_pages_clflush(pd->driver, &pd->p, 1); + struct drm_device *dev = pd->driver->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + uint32_t offset = (hw_context == 0) ? PSB_CR_BIF_DIR_LIST_BASE0 : + PSB_CR_BIF_DIR_LIST_BASE1 + hw_context * 4; + down_write(&pd->driver->sem); + PSB_WSGX32(page_to_pfn(pd->p) << PAGE_SHIFT, offset); wmb(); psb_mmu_flush_pd_locked(pd->driver, 1); pd->hw_context = hw_context; @@ -183,7 +154,6 @@ void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context) static inline unsigned long psb_pd_addr_end(unsigned long addr, unsigned long end) { - addr = (addr + PSB_PDE_MASK + 1) & ~PSB_PDE_MASK; return (addr < end) ? addr : end; } @@ -223,12 +193,10 @@ struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver, goto out_err3; if (!trap_pagefaults) { - pd->invalid_pde = - psb_mmu_mask_pte(page_to_pfn(pd->dummy_pt), - invalid_type); - pd->invalid_pte = - psb_mmu_mask_pte(page_to_pfn(pd->dummy_page), - invalid_type); + pd->invalid_pde = psb_mmu_mask_pte(page_to_pfn(pd->dummy_pt), + invalid_type); + pd->invalid_pte = psb_mmu_mask_pte(page_to_pfn(pd->dummy_page), + invalid_type); } else { pd->invalid_pde = 0; pd->invalid_pte = 0; @@ -270,7 +238,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); @@ -279,12 +247,16 @@ void psb_mmu_free_pt(struct psb_mmu_pt *pt) void psb_mmu_free_pagedir(struct psb_mmu_pd *pd) { struct psb_mmu_driver *driver = pd->driver; + struct drm_device *dev = driver->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct psb_mmu_pt *pt; int i; down_write(&driver->sem); - if (pd->hw_context != -1) + if (pd->hw_context != -1) { + PSB_WSGX32(0, PSB_CR_BIF_DIR_LIST_BASE0 + pd->hw_context * 4); psb_mmu_flush_pd_locked(driver, 1); + } /* Should take the spinlock here, but we don't need to do that since we have the semaphore in write mode. */ @@ -325,13 +297,13 @@ 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) *ptes++ = pd->invalid_pte; - +#if defined(CONFIG_X86) if (pd->driver->has_clflush && pd->hw_context != -1) { mb(); for (i = 0; i < clflush_count; ++i) { @@ -340,8 +312,8 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd) } mb(); } - - kunmap_atomic(v, KM_USER0); +#endif + kunmap_atomic(v); spin_unlock(lock); pt->count = 0; @@ -376,18 +348,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]); + 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 +376,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,18 +385,17 @@ 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; if (pd->hw_context != -1) { - psb_mmu_clflush(pd->driver, - (void *) &v[pt->index]); + psb_mmu_clflush(pd->driver, (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; @@ -432,8 +403,8 @@ static void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt) spin_unlock(&pd->driver->lock); } -static inline void psb_mmu_set_pte(struct psb_mmu_pt *pt, - unsigned long addr, uint32_t pte) +static inline void psb_mmu_set_pte(struct psb_mmu_pt *pt, unsigned long addr, + uint32_t pte) { pt->v[psb_mmu_pt_index(addr)] = pte; } @@ -444,46 +415,13 @@ static inline void psb_mmu_invalidate_pte(struct psb_mmu_pt *pt, pt->v[psb_mmu_pt_index(addr)] = pt->pd->invalid_pte; } - -void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd, - uint32_t mmu_offset, uint32_t gtt_start, - uint32_t gtt_pages) -{ - uint32_t *v; - uint32_t start = psb_mmu_pd_index(mmu_offset); - struct psb_mmu_driver *driver = pd->driver; - int num_pages = gtt_pages; - - down_read(&driver->sem); - spin_lock(&driver->lock); - - v = kmap_atomic(pd->p, KM_USER0); - v += start; - - while (gtt_pages--) { - *v++ = gtt_start | pd->pd_mask; - gtt_start += PAGE_SIZE; - } - - /*ttm_tt_cache_flush(&pd->p, num_pages);*/ - psb_pages_clflush(pd->driver, &pd->p, num_pages); - kunmap_atomic(v, KM_USER0); - spin_unlock(&driver->lock); - - if (pd->hw_context != -1) - atomic_set(&pd->driver->needs_tlbflush, 1); - - up_read(&pd->driver->sem); - psb_mmu_flush_pd(pd->driver, 0); -} - struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver *driver) { struct psb_mmu_pd *pd; - /* down_read(&driver->sem); */ + down_read(&driver->sem); pd = driver->default_pd; - /* up_read(&driver->sem); */ + up_read(&driver->sem); return pd; } @@ -499,23 +437,28 @@ uint32_t psb_get_default_pd_addr(struct psb_mmu_driver *driver) void psb_mmu_driver_takedown(struct psb_mmu_driver *driver) { + struct drm_device *dev = driver->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + PSB_WSGX32(driver->bif_ctrl, PSB_CR_BIF_CTRL); psb_mmu_free_pagedir(driver->default_pd); kfree(driver); } -struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers, - int trap_pagefaults, - int invalid_type, - struct drm_psb_private *dev_priv) +struct psb_mmu_driver *psb_mmu_driver_init(struct drm_device *dev, + int trap_pagefaults, + int invalid_type, + atomic_t *msvdx_mmu_invaldc) { struct psb_mmu_driver *driver; + struct drm_psb_private *dev_priv = dev->dev_private; driver = kmalloc(sizeof(*driver), GFP_KERNEL); if (!driver) return NULL; - driver->dev_priv = dev_priv; + driver->dev = dev; driver->default_pd = psb_mmu_alloc_pd(driver, trap_pagefaults, invalid_type); if (!driver->default_pd) @@ -524,17 +467,24 @@ struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers, spin_lock_init(&driver->lock); init_rwsem(&driver->sem); down_write(&driver->sem); - driver->register_map = registers; atomic_set(&driver->needs_tlbflush, 1); + driver->msvdx_mmu_invaldc = msvdx_mmu_invaldc; + + driver->bif_ctrl = PSB_RSGX32(PSB_CR_BIF_CTRL); + PSB_WSGX32(driver->bif_ctrl | _PSB_CB_CTRL_CLEAR_FAULT, + PSB_CR_BIF_CTRL); + PSB_WSGX32(driver->bif_ctrl & ~_PSB_CB_CTRL_CLEAR_FAULT, + PSB_CR_BIF_CTRL); driver->has_clflush = 0; - if (boot_cpu_has(X86_FEATURE_CLFLSH)) { +#if defined(CONFIG_X86) + if (boot_cpu_has(X86_FEATURE_CLFLUSH)) { uint32_t tfms, misc, cap0, cap4, clflush_size; /* - * clflush size is determined at kernel setup for x86_64 - * but not for i386. We have to do it here. + * clflush size is determined at kernel setup for x86_64 but not + * for i386. We have to do it here. */ cpuid(0x00000001, &tfms, &misc, &cap0, &cap4); @@ -545,6 +495,7 @@ struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers, driver->clflush_mask = driver->clflush_add - 1; driver->clflush_mask = ~driver->clflush_mask; } +#endif up_write(&driver->sem); return driver; @@ -554,9 +505,9 @@ out_err1: return NULL; } -static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, - unsigned long address, uint32_t num_pages, - uint32_t desired_tile_stride, +#if defined(CONFIG_X86) +static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address, + uint32_t num_pages, uint32_t desired_tile_stride, uint32_t hw_tile_stride) { struct psb_mmu_pt *pt; @@ -570,11 +521,8 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long clflush_add = pd->driver->clflush_add; unsigned long clflush_mask = pd->driver->clflush_mask; - if (!pd->driver->has_clflush) { - /*ttm_tt_cache_flush(&pd->p, num_pages);*/ - psb_pages_clflush(pd->driver, &pd->p, num_pages); + if (!pd->driver->has_clflush) return; - } if (hw_tile_stride) rows = num_pages / desired_tile_stride; @@ -595,10 +543,8 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, if (!pt) continue; do { - psb_clflush(&pt->v - [psb_mmu_pt_index(addr)]); - } while (addr += - clflush_add, + psb_clflush(&pt->v[psb_mmu_pt_index(addr)]); + } while (addr += clflush_add, (addr & clflush_mask) < next); psb_mmu_pt_unmap_unlock(pt); @@ -607,6 +553,14 @@ static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, } mb(); } +#else +static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd, unsigned long address, + uint32_t num_pages, uint32_t desired_tile_stride, + uint32_t hw_tile_stride) +{ + drm_ttm_cache_flush(); +} +#endif void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd, unsigned long address, uint32_t num_pages) @@ -642,7 +596,7 @@ out: up_read(&pd->driver->sem); if (pd->hw_context != -1) - psb_mmu_flush(pd->driver, 0); + psb_mmu_flush(pd->driver); return; } @@ -669,7 +623,7 @@ void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address, add = desired_tile_stride << PAGE_SHIFT; row_add = hw_tile_stride << PAGE_SHIFT; - /* down_read(&pd->driver->sem); */ + down_read(&pd->driver->sem); /* Make sure we only need to flush this processor's cache */ @@ -697,10 +651,10 @@ void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address, psb_mmu_flush_ptes(pd, f_address, num_pages, desired_tile_stride, hw_tile_stride); - /* up_read(&pd->driver->sem); */ + up_read(&pd->driver->sem); if (pd->hw_context != -1) - psb_mmu_flush(pd->driver, 0); + psb_mmu_flush(pd->driver); } int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn, @@ -713,7 +667,7 @@ int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn, unsigned long end; unsigned long next; unsigned long f_address = address; - int ret = 0; + int ret = -ENOMEM; down_read(&pd->driver->sem); @@ -735,6 +689,7 @@ int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn, psb_mmu_pt_unmap_unlock(pt); } while (addr = next, next != end); + ret = 0; out: if (pd->hw_context != -1) @@ -743,15 +698,15 @@ out: up_read(&pd->driver->sem); if (pd->hw_context != -1) - psb_mmu_flush(pd->driver, 1); + psb_mmu_flush(pd->driver); - return ret; + return 0; } int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages, unsigned long address, uint32_t num_pages, - uint32_t desired_tile_stride, - uint32_t hw_tile_stride, int type) + uint32_t desired_tile_stride, uint32_t hw_tile_stride, + int type) { struct psb_mmu_pt *pt; uint32_t rows = 1; @@ -763,7 +718,7 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages, unsigned long add; unsigned long row_add; unsigned long f_address = address; - int ret = 0; + int ret = -ENOMEM; if (hw_tile_stride) { if (num_pages % desired_tile_stride != 0) @@ -786,14 +741,11 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages, do { next = psb_pd_addr_end(addr, end); pt = psb_mmu_pt_alloc_map_lock(pd, addr); - if (!pt) { - ret = -ENOMEM; + if (!pt) goto out; - } do { - pte = - psb_mmu_mask_pte(page_to_pfn(*pages++), - type); + pte = psb_mmu_mask_pte(page_to_pfn(*pages++), + type); psb_mmu_set_pte(pt, addr, pte); pt->count++; } while (addr += PAGE_SIZE, addr < next); @@ -803,6 +755,8 @@ int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages, address += row_add; } + + ret = 0; out: if (pd->hw_context != -1) psb_mmu_flush_ptes(pd, f_address, num_pages, @@ -811,7 +765,7 @@ out: up_read(&pd->driver->sem); if (pd->hw_context != -1) - psb_mmu_flush(pd->driver, 1); + psb_mmu_flush(pd->driver); return ret; } @@ -830,9 +784,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/mmu.h b/drivers/gpu/drm/gma500/mmu.h new file mode 100644 index 00000000000..e89abec6209 --- /dev/null +++ b/drivers/gpu/drm/gma500/mmu.h @@ -0,0 +1,93 @@ +/************************************************************************** + * Copyright (c) 2007-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. + **************************************************************************/ + +#ifndef __MMU_H +#define __MMU_H + +struct psb_mmu_driver { + /* protects driver- and pd structures. Always take in read mode + * before taking the page table spinlock. + */ + struct rw_semaphore sem; + + /* protects page tables, directory tables and pt tables. + * and pt structures. + */ + spinlock_t lock; + + atomic_t needs_tlbflush; + atomic_t *msvdx_mmu_invaldc; + struct psb_mmu_pd *default_pd; + uint32_t bif_ctrl; + int has_clflush; + int clflush_add; + unsigned long clflush_mask; + + struct drm_device *dev; +}; + +struct psb_mmu_pd; + +struct psb_mmu_pt { + struct psb_mmu_pd *pd; + uint32_t index; + uint32_t count; + struct page *p; + uint32_t *v; +}; + +struct psb_mmu_pd { + struct psb_mmu_driver *driver; + int hw_context; + struct psb_mmu_pt **tables; + struct page *p; + struct page *dummy_pt; + struct page *dummy_page; + uint32_t pd_mask; + uint32_t invalid_pde; + uint32_t invalid_pte; +}; + +extern struct psb_mmu_driver *psb_mmu_driver_init(struct drm_device *dev, + int trap_pagefaults, + int invalid_type, + atomic_t *msvdx_mmu_invaldc); +extern void psb_mmu_driver_takedown(struct psb_mmu_driver *driver); +extern struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver + *driver); +extern struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver, + int trap_pagefaults, + int invalid_type); +extern void psb_mmu_free_pagedir(struct psb_mmu_pd *pd); +extern void psb_mmu_flush(struct psb_mmu_driver *driver); +extern void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd, + unsigned long address, + uint32_t num_pages); +extern int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, + uint32_t start_pfn, + unsigned long address, + uint32_t num_pages, int type); +extern int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual, + unsigned long *pfn); +extern void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context); +extern int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages, + unsigned long address, uint32_t num_pages, + uint32_t desired_tile_stride, + uint32_t hw_tile_stride, int type); +extern void psb_mmu_remove_pages(struct psb_mmu_pd *pd, + unsigned long address, uint32_t num_pages, + uint32_t desired_tile_stride, + uint32_t hw_tile_stride); + +#endif diff --git a/drivers/gpu/drm/gma500/oaktrail.h b/drivers/gpu/drm/gma500/oaktrail.h index 2da1f368f14..30adbbe2302 100644 --- a/drivers/gpu/drm/gma500/oaktrail.h +++ b/drivers/gpu/drm/gma500/oaktrail.h @@ -19,14 +19,6 @@ /* MID device specific descriptors */ -struct oaktrail_vbt { - s8 signature[4]; /*4 bytes,"$GCT" */ - u8 revision; - u8 size; - u8 checksum; - void *oaktrail_gct; -} __packed; - struct oaktrail_timing_info { u16 pixel_clock; u8 hactive_lo; @@ -161,7 +153,7 @@ union oaktrail_panel_rx { u16 panel_receiver; } __packed; -struct oaktrail_gct_v1 { +struct gct_r0 { union { /*8 bits,Defined as follows: */ struct { u8 PanelType:4; /*4 bits, Bit field for panels*/ @@ -178,7 +170,7 @@ struct oaktrail_gct_v1 { union oaktrail_panel_rx panelrx[4]; /* panel receivers*/ } __packed; -struct oaktrail_gct_v2 { +struct gct_r1 { union { /*8 bits,Defined as follows: */ struct { u8 PanelType:4; /*4 bits, Bit field for panels*/ @@ -195,6 +187,16 @@ struct oaktrail_gct_v2 { union oaktrail_panel_rx panelrx[4]; /* panel receivers*/ } __packed; +struct gct_r10 { + struct gct_r10_timing_info DTD; + u16 Panel_MIPI_Display_Descriptor; + u16 Panel_MIPI_Receiver_Descriptor; + u16 Panel_Backlight_Inverter_Descriptor; + u8 Panel_Initial_Brightness; + u32 MIPI_Ctlr_Init_ptr; + u32 MIPI_Panel_Init_ptr; +} __packed; + struct oaktrail_gct_data { u8 bpi; /* boot panel index, number of panel used during boot */ u8 pt; /* panel type, 4 bit field, 0=lvds, 1=mipi */ @@ -213,9 +215,6 @@ struct oaktrail_gct_data { #define MODE_SETTING_IN_DSR 0x4 #define MODE_SETTING_ENCODER_DONE 0x8 -#define GCT_R10_HEADER_SIZE 16 -#define GCT_R10_DISPLAY_DESC_SIZE 28 - /* * Moorestown HDMI interfaces */ @@ -250,3 +249,9 @@ extern void oaktrail_hdmi_i2c_exit(struct pci_dev *dev); extern void oaktrail_hdmi_save(struct drm_device *dev); extern void oaktrail_hdmi_restore(struct drm_device *dev); extern void oaktrail_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev); +extern 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); +extern void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode); + + diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c index 9d12a3ee160..2de216c2374 100644 --- a/drivers/gpu/drm/gma500/oaktrail_crtc.c +++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c @@ -23,27 +23,13 @@ #include "psb_drv.h" #include "psb_intel_drv.h" #include "psb_intel_reg.h" -#include "psb_intel_display.h" +#include "gma_display.h" #include "power.h" -struct psb_intel_range_t { - int min, max; -}; - -struct oaktrail_limit_t { - struct psb_intel_range_t dot, m, p1; -}; - -struct oaktrail_clock_t { - /* derived values */ - int dot; - int m; - int p1; -}; - -#define MRST_LIMIT_LVDS_100L 0 -#define MRST_LIMIT_LVDS_83 1 -#define MRST_LIMIT_LVDS_100 2 +#define MRST_LIMIT_LVDS_100L 0 +#define MRST_LIMIT_LVDS_83 1 +#define MRST_LIMIT_LVDS_100 2 +#define MRST_LIMIT_SDVO 3 #define MRST_DOT_MIN 19750 #define MRST_DOT_MAX 120000 @@ -57,21 +43,40 @@ struct oaktrail_clock_t { #define MRST_P1_MAX_0 7 #define MRST_P1_MAX_1 8 -static const struct oaktrail_limit_t oaktrail_limits[] = { +static bool mrst_lvds_find_best_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, + int refclk, struct gma_clock_t *best_clock); + +static bool mrst_sdvo_find_best_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, + int refclk, struct gma_clock_t *best_clock); + +static const struct gma_limit_t mrst_limits[] = { { /* MRST_LIMIT_LVDS_100L */ .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, .m = {.min = MRST_M_MIN_100L, .max = MRST_M_MAX_100L}, .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, + .find_pll = mrst_lvds_find_best_pll, }, { /* MRST_LIMIT_LVDS_83L */ .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, .m = {.min = MRST_M_MIN_83, .max = MRST_M_MAX_83}, .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_0}, + .find_pll = mrst_lvds_find_best_pll, }, { /* MRST_LIMIT_LVDS_100 */ .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX}, .m = {.min = MRST_M_MIN_100, .max = MRST_M_MAX_100}, .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1}, + .find_pll = mrst_lvds_find_best_pll, + }, + { /* MRST_LIMIT_SDVO */ + .vco = {.min = 1400000, .max = 2800000}, + .n = {.min = 3, .max = 7}, + .m = {.min = 80, .max = 137}, + .p1 = {.min = 1, .max = 2}, + .p2 = {.dot_limit = 200000, .p2_slow = 10, .p2_fast = 10}, + .find_pll = mrst_sdvo_find_best_pll, }, }; @@ -82,55 +87,111 @@ static const u32 oaktrail_m_converts[] = { 0x12, 0x09, 0x24, 0x32, 0x39, 0x1c, }; -static const struct oaktrail_limit_t *oaktrail_limit(struct drm_crtc *crtc) +static const struct gma_limit_t *mrst_limit(struct drm_crtc *crtc, + int refclk) { - const struct oaktrail_limit_t *limit = NULL; + const struct gma_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_LVDS) - || psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) { + if (gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) + || gma_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) { switch (dev_priv->core_freq) { case 100: - limit = &oaktrail_limits[MRST_LIMIT_LVDS_100L]; + limit = &mrst_limits[MRST_LIMIT_LVDS_100L]; break; case 166: - limit = &oaktrail_limits[MRST_LIMIT_LVDS_83]; + limit = &mrst_limits[MRST_LIMIT_LVDS_83]; break; case 200: - limit = &oaktrail_limits[MRST_LIMIT_LVDS_100]; + limit = &mrst_limits[MRST_LIMIT_LVDS_100]; break; } + } else if (gma_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) { + limit = &mrst_limits[MRST_LIMIT_SDVO]; } else { limit = NULL; - dev_err(dev->dev, "oaktrail_limit Wrong display type.\n"); + dev_err(dev->dev, "mrst_limit Wrong display type.\n"); } return limit; } /** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ -static void oaktrail_clock(int refclk, struct oaktrail_clock_t *clock) +static void mrst_lvds_clock(int refclk, struct gma_clock_t *clock) { clock->dot = (refclk * clock->m) / (14 * clock->p1); } -void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock) +static void mrst_print_pll(struct gma_clock_t *clock) { - pr_debug("%s: dotclock = %d, m = %d, p1 = %d.\n", - prefix, clock->dot, clock->m, clock->p1); + DRM_DEBUG_DRIVER("dotclock=%d, m=%d, m1=%d, m2=%d, n=%d, p1=%d, p2=%d\n", + clock->dot, clock->m, clock->m1, clock->m2, clock->n, + clock->p1, clock->p2); +} + +static bool mrst_sdvo_find_best_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, + int refclk, struct gma_clock_t *best_clock) +{ + struct gma_clock_t clock; + u32 target_vco, actual_freq; + s32 freq_error, min_error = 100000; + + memset(best_clock, 0, sizeof(*best_clock)); + + for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) { + for (clock.n = limit->n.min; clock.n <= limit->n.max; + clock.n++) { + for (clock.p1 = limit->p1.min; + clock.p1 <= limit->p1.max; clock.p1++) { + /* p2 value always stored in p2_slow on SDVO */ + clock.p = clock.p1 * limit->p2.p2_slow; + target_vco = target * clock.p; + + /* VCO will increase at this point so break */ + if (target_vco > limit->vco.max) + break; + + if (target_vco < limit->vco.min) + continue; + + actual_freq = (refclk * clock.m) / + (clock.n * clock.p); + freq_error = 10000 - + ((target * 10000) / actual_freq); + + if (freq_error < -min_error) { + /* freq_error will start to decrease at + this point so break */ + break; + } + + if (freq_error < 0) + freq_error = -freq_error; + + if (freq_error < min_error) { + min_error = freq_error; + *best_clock = clock; + } + } + } + if (min_error == 0) + break; + } + + return min_error == 0; } /** * 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 -mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk, - struct oaktrail_clock_t *best_clock) +static bool mrst_lvds_find_best_pll(const struct gma_limit_t *limit, + struct drm_crtc *crtc, int target, + int refclk, struct gma_clock_t *best_clock) { - struct oaktrail_clock_t clock; - const struct oaktrail_limit_t *limit = oaktrail_limit(crtc); + struct gma_clock_t clock; int err = target; memset(best_clock, 0, sizeof(*best_clock)); @@ -140,7 +201,7 @@ mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk, clock.p1++) { int this_err; - oaktrail_clock(refclk, &clock); + mrst_lvds_clock(refclk, &clock); this_err = abs(clock.dot - target); if (this_err < err) { @@ -149,7 +210,6 @@ mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk, } } } - dev_dbg(crtc->dev->dev, "mrstFindBestPLL err = %d.\n", err); return err != target; } @@ -162,14 +222,18 @@ mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk, static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode) { struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int dspbase_reg = (pipe == 0) ? MRST_DSPABASE : DSPBBASE; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 temp; - bool enabled; + int i; + int need_aux = gma_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) ? 1 : 0; + + if (gma_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) { + oaktrail_crtc_hdmi_dpms(crtc, mode); + return; + } if (!gma_power_begin(dev, true)) return; @@ -181,36 +245,46 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int 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) { - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - } - /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) == 0) - REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); - /* 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)); - } + for (i = 0; i <= need_aux; i++) { + /* Enable the DPLL */ + temp = REG_READ_WITH_AUX(map->dpll, i); + if ((temp & DPLL_VCO_ENABLE) == 0) { + REG_WRITE_WITH_AUX(map->dpll, temp, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE_WITH_AUX(map->dpll, + temp | DPLL_VCO_ENABLE, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Wait for the clocks to stabilize. */ + udelay(150); + REG_WRITE_WITH_AUX(map->dpll, + temp | DPLL_VCO_ENABLE, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Wait for the clocks to stabilize. */ + udelay(150); + } - psb_intel_crtc_load_lut(crtc); + /* Enable the pipe */ + temp = REG_READ_WITH_AUX(map->conf, i); + if ((temp & PIPEACONF_ENABLE) == 0) { + REG_WRITE_WITH_AUX(map->conf, + temp | PIPEACONF_ENABLE, i); + } + + /* Enable the plane */ + temp = REG_READ_WITH_AUX(map->cntr, i); + if ((temp & DISPLAY_PLANE_ENABLE) == 0) { + REG_WRITE_WITH_AUX(map->cntr, + temp | DISPLAY_PLANE_ENABLE, + i); + /* Flush the plane changes */ + REG_WRITE_WITH_AUX(map->base, + REG_READ_WITH_AUX(map->base, i), i); + } + + } + gma_crtc_load_lut(crtc); /* Give the overlay scaler a chance to enable if it's on this pipe */ @@ -221,50 +295,52 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode) * if it's on this pipe */ /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */ - /* 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); - } + for (i = 0; i <= need_aux; i++) { + /* Disable the VGA plane that we never use */ + REG_WRITE_WITH_AUX(VGACNTRL, VGA_DISP_DISABLE, i); + /* Disable display plane */ + temp = REG_READ_WITH_AUX(map->cntr, i); + if ((temp & DISPLAY_PLANE_ENABLE) != 0) { + REG_WRITE_WITH_AUX(map->cntr, + temp & ~DISPLAY_PLANE_ENABLE, i); + /* Flush the plane changes */ + REG_WRITE_WITH_AUX(map->base, + REG_READ(map->base), i); + REG_READ_WITH_AUX(map->base, i); + } - /* Next, disable display pipes */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) != 0) { - REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - REG_READ(pipeconf_reg); - } - /* Wait for for the pipe disable to take effect. */ - psb_intel_wait_for_vblank(dev); + /* Next, disable display pipes */ + temp = REG_READ_WITH_AUX(map->conf, i); + if ((temp & PIPEACONF_ENABLE) != 0) { + REG_WRITE_WITH_AUX(map->conf, + temp & ~PIPEACONF_ENABLE, i); + REG_READ_WITH_AUX(map->conf, i); + } + /* Wait for for the pipe disable to take effect. */ + gma_wait_for_vblank(dev); + + temp = REG_READ_WITH_AUX(map->dpll, i); + if ((temp & DPLL_VCO_ENABLE) != 0) { + REG_WRITE_WITH_AUX(map->dpll, + temp & ~DPLL_VCO_ENABLE, i); + REG_READ_WITH_AUX(map->dpll, i); + } - temp = REG_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) != 0) { - REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + /* Wait for the clocks to turn off. */ + udelay(150); } - - /* Wait for the clocks to turn off. */ - udelay(150); break; } - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; - - /*Set FIFO Watermarks*/ - REG_WRITE(DSPARB, 0x3FFF); - REG_WRITE(DSPFW1, 0x3F88080A); - REG_WRITE(DSPFW2, 0x0b060808); + /* Set FIFO Watermarks (values taken from EMGD) */ + REG_WRITE(DSPARB, 0x3f80); + REG_WRITE(DSPFW1, 0x3f8f0404); + REG_WRITE(DSPFW2, 0x04040f04); REG_WRITE(DSPFW3, 0x0); - REG_WRITE(DSPFW4, 0x08030404); + REG_WRITE(DSPFW4, 0x04040404); REG_WRITE(DSPFW5, 0x04040404); REG_WRITE(DSPFW6, 0x78); - REG_WRITE(0x70400, REG_READ(0x70400) | 0x4000); - /* Must write Bit 14 of the Chicken Bit Register */ + REG_WRITE(DSPCHICKENBIT, REG_READ(DSPCHICKENBIT) | 0xc040); gma_power_end(dev); } @@ -292,38 +368,34 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); struct drm_psb_private *dev_priv = dev->dev_private; - int pipe = psb_intel_crtc->pipe; - int fp_reg = (pipe == 0) ? MRST_FPA0 : FPB0; - int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - 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 pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC; + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; int refclk = 0; - struct oaktrail_clock_t clock; + struct gma_clock_t clock; + const struct gma_limit_t *limit; 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; + struct gma_encoder *gma_encoder = NULL; uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN; struct drm_connector *connector; + int i; + int need_aux = gma_pipe_has_type(crtc, INTEL_OUTPUT_SDVO) ? 1 : 0; + + if (gma_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) + return oaktrail_crtc_hdmi_mode_set(crtc, mode, adjusted_mode, x, y, old_fb); if (!gma_power_begin(dev, true)) return 0; - memcpy(&psb_intel_crtc->saved_mode, + memcpy(&gma_crtc->saved_mode, mode, sizeof(struct drm_display_mode)); - memcpy(&psb_intel_crtc->saved_adjusted_mode, + memcpy(&gma_crtc->saved_adjusted_mode, adjusted_mode, sizeof(struct drm_display_mode)); @@ -331,21 +403,15 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, if (!connector->encoder || connector->encoder->crtc != crtc) continue; - psb_intel_encoder = psb_intel_attached_encoder(connector); + gma_encoder = gma_attached_encoder(connector); - switch (psb_intel_encoder->type) { + switch (gma_encoder->type) { case INTEL_OUTPUT_LVDS: is_lvds = true; break; 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; @@ -353,18 +419,20 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, } /* Disable the VGA plane that we never use */ - REG_WRITE(VGACNTRL, VGA_DISP_DISABLE); + for (i = 0; i <= need_aux; i++) + REG_WRITE_WITH_AUX(VGACNTRL, VGA_DISP_DISABLE, i); /* Disable the panel fitter if it was on our pipe */ if (oaktrail_panel_fitter_pipe(dev) == pipe) REG_WRITE(PFIT_CONTROL, 0); - REG_WRITE(pipesrc_reg, - ((mode->crtc_hdisplay - 1) << 16) | - (mode->crtc_vdisplay - 1)); + for (i = 0; i <= need_aux; i++) { + REG_WRITE_WITH_AUX(map->src, ((mode->crtc_hdisplay - 1) << 16) | + (mode->crtc_vdisplay - 1), i); + } - if (psb_intel_encoder) - drm_connector_property_get_value(connector, + if (gma_encoder) + drm_object_property_get_value(&connector->base, dev->mode_config.scaling_mode_property, &scalingType); if (scalingType == DRM_MODE_SCALE_NO_SCALE) { @@ -378,35 +446,39 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, 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)); + for (i = 0; i <= need_aux; i++) { + REG_WRITE_WITH_AUX(map->htotal, (mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vtotal, (mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16), i); + REG_WRITE_WITH_AUX(map->hblank, + (adjusted_mode->crtc_hblank_start - offsetX - 1) | + ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16), i); + REG_WRITE_WITH_AUX(map->hsync, + (adjusted_mode->crtc_hsync_start - offsetX - 1) | + ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vblank, + (adjusted_mode->crtc_vblank_start - offsetY - 1) | + ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vsync, + (adjusted_mode->crtc_vsync_start - offsetY - 1) | + ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16), i); + } } 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)); + for (i = 0; i <= need_aux; i++) { + REG_WRITE_WITH_AUX(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | + ((adjusted_mode->crtc_htotal - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | + ((adjusted_mode->crtc_vtotal - 1) << 16), i); + REG_WRITE_WITH_AUX(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | + ((adjusted_mode->crtc_hblank_end - 1) << 16), i); + REG_WRITE_WITH_AUX(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | + ((adjusted_mode->crtc_hsync_end - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | + ((adjusted_mode->crtc_vblank_end - 1) << 16), i); + REG_WRITE_WITH_AUX(map->vsync, (adjusted_mode->crtc_vsync_start - 1) | + ((adjusted_mode->crtc_vsync_end - 1) << 16), i); + } } /* Flush the plane changes */ @@ -417,10 +489,10 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, } /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); + pipeconf = REG_READ(map->conf); /* Set up the display plane register */ - dspcntr = REG_READ(dspcntr_reg); + dspcntr = REG_READ(map->cntr); dspcntr |= DISPPLANE_GAMMA_ENABLE; if (pipe == 0) @@ -428,27 +500,33 @@ 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; - refclk = dev_priv->core_freq * 1000; dpll = 0; /*BIT16 = 0 for 100MHz reference */ - ok = mrstFindBestPLL(crtc, adjusted_mode->clock, refclk, &clock); + refclk = is_sdvo ? 96000 : dev_priv->core_freq * 1000; + limit = mrst_limit(crtc, refclk); + ok = limit->find_pll(limit, crtc, adjusted_mode->clock, + refclk, &clock); - if (!ok) { - dev_dbg(dev->dev, "mrstFindBestPLL fail in oaktrail_crtc_mode_set.\n"); - } else { - dev_dbg(dev->dev, "oaktrail_crtc_mode_set pixel clock = %d," - "m = %x, p1 = %x.\n", clock.dot, clock.m, - clock.p1); + if (is_sdvo) { + /* Convert calculated values to register values */ + clock.p1 = (1L << (clock.p1 - 1)); + clock.m -= 2; + clock.n = (1L << (clock.n - 1)); } - fp = oaktrail_m_converts[(clock.m - MRST_M_MIN)] << 8; + if (!ok) + DRM_ERROR("Failed to find proper PLL settings"); + + mrst_print_pll(&clock); + + if (is_sdvo) + fp = clock.n << 16 | clock.m; + else + fp = oaktrail_m_converts[(clock.m - MRST_M_MIN)] << 8; dpll |= DPLL_VGA_MODE_DIS; @@ -472,69 +550,65 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc, /* compute bitmask from p1 value */ - dpll |= (1 << (clock.p1 - 2)) << 17; + if (is_sdvo) + dpll |= clock.p1 << 16; // dpll |= (1 << (clock.p1 - 1)) << 16; + else + dpll |= (1 << (clock.p1 - 2)) << 17; dpll |= DPLL_VCO_ENABLE; - mrstPrintPll("chosen", &clock); - if (dpll & DPLL_VCO_ENABLE) { - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Check the DPLLA lock bit PIPEACONF[29] */ - udelay(150); + for (i = 0; i <= need_aux; i++) { + REG_WRITE_WITH_AUX(map->fp0, fp, i); + REG_WRITE_WITH_AUX(map->dpll, dpll & ~DPLL_VCO_ENABLE, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Check the DPLLA lock bit PIPEACONF[29] */ + udelay(150); + } } - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); + for (i = 0; i <= need_aux; i++) { + REG_WRITE_WITH_AUX(map->fp0, fp, i); + REG_WRITE_WITH_AUX(map->dpll, dpll, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Wait for the clocks to stabilize. */ + udelay(150); - /* write it again -- the BIOS does, after all */ - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); + /* write it again -- the BIOS does, after all */ + REG_WRITE_WITH_AUX(map->dpll, dpll, i); + REG_READ_WITH_AUX(map->dpll, i); + /* Wait for the clocks to stabilize. */ + udelay(150); - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); - psb_intel_wait_for_vblank(dev); + REG_WRITE_WITH_AUX(map->conf, pipeconf, i); + REG_READ_WITH_AUX(map->conf, i); + gma_wait_for_vblank(dev); - REG_WRITE(dspcntr_reg, dspcntr); - psb_intel_wait_for_vblank(dev); + REG_WRITE_WITH_AUX(map->cntr, dspcntr, i); + gma_wait_for_vblank(dev); + } oaktrail_crtc_mode_set_exit: gma_power_end(dev); return 0; } -static bool oaktrail_crtc_mode_fixup(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - 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; - 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; + struct drm_psb_private *dev_priv = dev->dev_private; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + struct psb_framebuffer *psbfb = to_psb_fb(crtc->primary->fb); + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; unsigned long start, offset; - int dspbase = (pipe == 0 ? DSPALINOFF : DSPBBASE); - int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; u32 dspcntr; int ret = 0; /* no fb bound */ - if (!crtc->fb) { + if (!crtc->primary->fb) { dev_dbg(dev->dev, "No FB bound\n"); return 0; } @@ -543,19 +617,19 @@ int oaktrail_pipe_set_base(struct drm_crtc *crtc, return 0; start = psbfb->gtt->offset; - offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8); + offset = y * crtc->primary->fb->pitches[0] + x * (crtc->primary->fb->bits_per_pixel / 8); - REG_WRITE(dspstride, crtc->fb->pitches[0]); + REG_WRITE(map->stride, crtc->primary->fb->pitches[0]); - dspcntr = REG_READ(dspcntr_reg); + dspcntr = REG_READ(map->cntr); dspcntr &= ~DISPPLANE_PIXFORMAT_MASK; - switch (crtc->fb->bits_per_pixel) { + switch (crtc->primary->fb->bits_per_pixel) { case 8: dspcntr |= DISPPLANE_8BPP; break; case 16: - if (crtc->fb->depth == 15) + if (crtc->primary->fb->depth == 15) dspcntr |= DISPPLANE_15_16BPP; else dspcntr |= DISPPLANE_16BPP; @@ -569,36 +643,30 @@ int oaktrail_pipe_set_base(struct drm_crtc *crtc, ret = -EINVAL; goto pipe_set_base_exit; } - REG_WRITE(dspcntr_reg, dspcntr); + REG_WRITE(map->cntr, dspcntr); - REG_WRITE(dspbase, offset); - REG_READ(dspbase); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); + REG_WRITE(map->base, offset); + REG_READ(map->base); + REG_WRITE(map->surf, start); + REG_READ(map->surf); pipe_set_base_exit: gma_power_end(dev); return ret; } -static void oaktrail_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 oaktrail_crtc_commit(struct drm_crtc *crtc) -{ - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); -} - const struct drm_crtc_helper_funcs oaktrail_helper_funcs = { .dpms = oaktrail_crtc_dpms, - .mode_fixup = oaktrail_crtc_mode_fixup, + .mode_fixup = gma_crtc_mode_fixup, .mode_set = oaktrail_crtc_mode_set, .mode_set_base = oaktrail_pipe_set_base, - .prepare = oaktrail_crtc_prepare, - .commit = oaktrail_crtc_commit, + .prepare = gma_crtc_prepare, + .commit = gma_crtc_commit, }; +/* Not used yet */ +const struct gma_clock_funcs mrst_clock_funcs = { + .clock = mrst_lvds_clock, + .limit = mrst_limit, + .pll_is_valid = gma_pll_is_valid, +}; diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c index 63aea2f010d..368a03ae301 100644 --- a/drivers/gpu/drm/gma500/oaktrail_device.c +++ b/drivers/gpu/drm/gma500/oaktrail_device.c @@ -22,11 +22,11 @@ #include <linux/dmi.h> #include <drm/drmP.h> #include <drm/drm.h> -#include "gma_drm.h" +#include <drm/gma_drm.h> #include "psb_drv.h" #include "psb_reg.h" #include "psb_intel_reg.h" -#include <asm/mrst.h> +#include <asm/intel-mid.h> #include <asm/intel_scu_ipc.h> #include "mid_bios.h" #include "intel_bios.h" @@ -40,6 +40,9 @@ static int oaktrail_output_init(struct drm_device *dev) dev_err(dev->dev, "DSI is not supported\n"); if (dev_priv->hdmi_priv) oaktrail_hdmi_init(dev, &dev_priv->mode_dev); + + psb_intel_sdvo_init(dev, SDVOB); + return 0; } @@ -141,7 +144,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 +179,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 +189,83 @@ 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; + struct psb_pipe *p = ®s->pipe[0]; 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); + p->conf = PSB_RVDC32(PIPEACONF); + p->src = PSB_RVDC32(PIPEASRC); + p->fp0 = PSB_RVDC32(MRST_FPA0); + p->fp1 = PSB_RVDC32(MRST_FPA1); + p->dpll = PSB_RVDC32(MRST_DPLL_A); + p->htotal = PSB_RVDC32(HTOTAL_A); + p->hblank = PSB_RVDC32(HBLANK_A); + p->hsync = PSB_RVDC32(HSYNC_A); + p->vtotal = PSB_RVDC32(VTOTAL_A); + p->vblank = PSB_RVDC32(VBLANK_A); + p->vsync = PSB_RVDC32(VSYNC_A); + regs->psb.saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A); + p->cntr = PSB_RVDC32(DSPACNTR); + p->stride = PSB_RVDC32(DSPASTRIDE); + p->addr = PSB_RVDC32(DSPABASE); + p->surf = PSB_RVDC32(DSPASURF); + p->linoff = PSB_RVDC32(DSPALINOFF); + p->tileoff = 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)); + p->palette[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 +303,81 @@ 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; + struct psb_pipe *p = ®s->pipe[0]; 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(p->fp0, MRST_FPA0); + PSB_WVDC32(p->fp1, MRST_FPA1); /* Actually enable it */ - PSB_WVDC32(dev_priv->saveDPLL_A, MRST_DPLL_A); + PSB_WVDC32(p->dpll, 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(p->htotal, HTOTAL_A); + PSB_WVDC32(p->hblank, HBLANK_A); + PSB_WVDC32(p->hsync, HSYNC_A); + PSB_WVDC32(p->vtotal, VTOTAL_A); + PSB_WVDC32(p->vblank, VBLANK_A); + PSB_WVDC32(p->vsync, VSYNC_A); + PSB_WVDC32(p->src, 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(p->conf, 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(p->linoff, DSPALINOFF); + PSB_WVDC32(p->stride, DSPASTRIDE); + PSB_WVDC32(p->tileoff, DSPATILEOFF); /* Enable the plane */ - PSB_WVDC32(dev_priv->saveDSPACNTR, DSPACNTR); - PSB_WVDC32(dev_priv->saveDSPASURF, DSPASURF); + PSB_WVDC32(p->cntr, DSPACNTR); + PSB_WVDC32(p->surf, 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(p->palette[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 +391,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; } @@ -456,31 +459,88 @@ static int oaktrail_power_up(struct drm_device *dev) return 0; } +/* Oaktrail */ +static const struct psb_offset oaktrail_regmap[2] = { + { + .fp0 = MRST_FPA0, + .fp1 = MRST_FPA1, + .cntr = DSPACNTR, + .conf = PIPEACONF, + .src = PIPEASRC, + .dpll = MRST_DPLL_A, + .htotal = HTOTAL_A, + .hblank = HBLANK_A, + .hsync = HSYNC_A, + .vtotal = VTOTAL_A, + .vblank = VBLANK_A, + .vsync = VSYNC_A, + .stride = DSPASTRIDE, + .size = DSPASIZE, + .pos = DSPAPOS, + .surf = DSPASURF, + .addr = MRST_DSPABASE, + .base = MRST_DSPABASE, + .status = PIPEASTAT, + .linoff = DSPALINOFF, + .tileoff = DSPATILEOFF, + .palette = PALETTE_A, + }, + { + .fp0 = FPB0, + .fp1 = FPB1, + .cntr = DSPBCNTR, + .conf = PIPEBCONF, + .src = PIPEBSRC, + .dpll = DPLL_B, + .htotal = HTOTAL_B, + .hblank = HBLANK_B, + .hsync = HSYNC_B, + .vtotal = VTOTAL_B, + .vblank = VBLANK_B, + .vsync = VSYNC_B, + .stride = DSPBSTRIDE, + .size = DSPBSIZE, + .pos = DSPBPOS, + .surf = DSPBSURF, + .addr = DSPBBASE, + .base = DSPBBASE, + .status = PIPEBSTAT, + .linoff = DSPBLINOFF, + .tileoff = DSPBTILEOFF, + .palette = PALETTE_B, + }, +}; static int oaktrail_chip_setup(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; - struct oaktrail_vbt *vbt = &dev_priv->vbt_data; int ret; + if (pci_enable_msi(dev->pdev)) + dev_warn(dev->dev, "Enabling MSI failed!\n"); + + dev_priv->regmap = oaktrail_regmap; + ret = mid_chip_setup(dev); if (ret < 0) return ret; - if (vbt->size == 0) { + if (!dev_priv->has_gct) { /* Now pull the BIOS data */ - gma_intel_opregion_init(dev); + psb_intel_opregion_init(dev); psb_intel_init_bios(dev); } + gma_intel_setup_gmbus(dev); + oaktrail_hdmi_setup(dev); return 0; } static void oaktrail_teardown(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; - struct oaktrail_vbt *vbt = &dev_priv->vbt_data; + gma_intel_teardown_gmbus(dev); oaktrail_hdmi_teardown(dev); - if (vbt->size == 0) + if (!dev_priv->has_gct) psb_intel_destroy_bios(dev); } @@ -489,6 +549,10 @@ const struct psb_ops oaktrail_chip_ops = { .accel_2d = 1, .pipes = 2, .crtcs = 2, + .hdmi_mask = (1 << 1), + .lvds_mask = (1 << 0), + .sdvo_mask = (1 << 1), + .cursor_needs_phys = 0, .sgx_offset = MRST_SGX_OFFSET, .chip_setup = oaktrail_chip_setup, @@ -502,7 +566,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 025d30970cc..cf018ddcc5a 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,125 +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; - - struct drm_device *dev = encoder->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; - u32 temp; - - if (dpms_mode == mode) - return; - - if (mode != DRM_MODE_DPMS_ON) - temp = 0x0; - else - temp = 0x99; - - dpms_mode = mode; - HDMI_WRITE(HDMI_VIDEO_REG, temp); -} - static unsigned int htotal_calculate(struct drm_display_mode *mode) { u32 htotal, new_crtc_htotal; @@ -339,6 +167,7 @@ static unsigned int htotal_calculate(struct drm_display_mode *mode) */ new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock; + DRM_DEBUG_KMS("new crtc htotal 0x%4x\n", new_crtc_htotal); return (mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16); } @@ -376,6 +205,57 @@ static void oaktrail_hdmi_find_dpll(struct drm_crtc *crtc, int target, best_clock->nf = (nf << 14); } +static void scu_busy_loop(void __iomem *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; + } + } +} + +/* + * You don't want to know, you really really don't want to know.... + * + * This is magic. However it's safe magic because of the way the platform + * works and it is necessary magic. + */ +static void oaktrail_hdmi_reset(struct drm_device *dev) +{ + void __iomem *base; + unsigned long scu_ipc_mmio = 0xff11c000UL; + 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); +} + int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -401,11 +281,12 @@ int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, u32 dspcntr, pipeconf, dpll, temp; int dspcntr_reg = DSPBCNTR; + if (!gma_power_begin(dev, true)) + return 0; + /* 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) { @@ -415,15 +296,14 @@ int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, } udelay(150); - /* reset controller: FIXME - can we sort out the ioremap mess ? */ - iounmap(hdmi_dev->regs); + /* Reset controller */ oaktrail_hdmi_reset(dev); /* program and enable dpll */ refclk = 25000; oaktrail_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock); - /* Setting DPLL */ + /* Set the DPLL */ dpll = REG_READ(DPLL_CTRL); dpll &= ~DPLL_PDIV_MASK; dpll &= ~(DPLL_PWRDN | DPLL_RESET); @@ -435,12 +315,6 @@ int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, 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); @@ -455,8 +329,7 @@ int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, 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(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)); @@ -464,14 +337,12 @@ int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, 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)); + 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(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); REG_WRITE(dsppos_reg, 0); /* Flush the plane changes */ @@ -495,18 +366,152 @@ int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc, REG_WRITE(PCH_PIPEBCONF, pipeconf); REG_READ(PCH_PIPEBCONF); - wait_for_vblank(dev); + gma_wait_for_vblank(dev); REG_WRITE(dspcntr_reg, dspcntr); - wait_for_vblank(dev); + gma_wait_for_vblank(dev); + + gma_power_end(dev); return 0; } +void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode) +{ + struct drm_device *dev = crtc->dev; + u32 temp; + + DRM_DEBUG_KMS("%s %d\n", __func__, mode); + + switch (mode) { + case DRM_MODE_DPMS_OFF: + 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); + } + + gma_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); + } + + gma_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 - Squawk! */ + REG_WRITE(0x70400, 0x4000); + + return; +} + +static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode) +{ + static int dpms_mode = -1; + + struct drm_device *dev = encoder->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv; + u32 temp; + + if (dpms_mode == mode) + return; + + if (mode != DRM_MODE_DPMS_ON) + temp = 0x0; + else + temp = 0x99; + + dpms_mode = mode; + HDMI_WRITE(HDMI_VIDEO_REG, temp); +} + static int oaktrail_hdmi_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct drm_psb_private *dev_priv = connector->dev->dev_private; if (mode->clock > 165000) return MODE_CLOCK_HIGH; if (mode->clock < 20000) @@ -515,21 +520,9 @@ static int oaktrail_hdmi_mode_valid(struct drm_connector *connector, if (mode->flags & DRM_MODE_FLAG_DBLSCAN) return MODE_NO_DBLESCAN; - /* We assume worst case scenario of 32 bpp here, since we don't know */ - if ((ALIGN(mode->hdisplay * 4, 64) * mode->vdisplay) > - dev_priv->vram_stolen_size) - return MODE_MEM; - return MODE_OK; } -static bool oaktrail_hdmi_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - static enum drm_connector_status oaktrail_hdmi_detect(struct drm_connector *connector, bool force) { @@ -566,13 +559,15 @@ static const unsigned char raw_edid[] = { static int oaktrail_hdmi_get_modes(struct drm_connector *connector) { - struct drm_device *dev = connector->dev; - struct drm_psb_private *dev_priv = dev->dev_private; struct i2c_adapter *i2c_adap; struct edid *edid; - struct drm_display_mode *mode, *t; - int i = 0, ret = 0; + int ret = 0; + /* + * FIXME: We need to figure this lot out. In theory we can + * read the EDID somehow but I've yet to find working reference + * code. + */ i2c_adap = i2c_get_adapter(3); if (i2c_adap == NULL) { DRM_ERROR("No ddc adapter available!\n"); @@ -585,19 +580,8 @@ static int oaktrail_hdmi_get_modes(struct drm_connector *connector) if (edid) { drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); - connector->display_info.raw_edid = NULL; } - - /* - * prune modes that require frame buffer bigger than stolen mem - */ - list_for_each_entry_safe(mode, t, &connector->probed_modes, head) { - if ((mode->hdisplay * mode->vdisplay * 4) >= dev_priv->vram_stolen_size) { - i++; - drm_mode_remove(connector, mode); - } - } - return ret - i; + return ret; } static void oaktrail_hdmi_mode_set(struct drm_encoder *encoder, @@ -617,17 +601,17 @@ static void oaktrail_hdmi_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs oaktrail_hdmi_helper_funcs = { .dpms = oaktrail_hdmi_dpms, - .mode_fixup = oaktrail_hdmi_mode_fixup, - .prepare = psb_intel_encoder_prepare, + .mode_fixup = gma_encoder_mode_fixup, + .prepare = gma_encoder_prepare, .mode_set = oaktrail_hdmi_mode_set, - .commit = psb_intel_encoder_commit, + .commit = gma_encoder_commit, }; static const struct drm_connector_helper_funcs oaktrail_hdmi_connector_helper_funcs = { .get_modes = oaktrail_hdmi_get_modes, .mode_valid = oaktrail_hdmi_mode_valid, - .best_encoder = psb_intel_best_encoder, + .best_encoder = gma_best_encoder, }; static const struct drm_connector_funcs oaktrail_hdmi_connector_funcs = { @@ -649,21 +633,21 @@ static const struct drm_encoder_funcs oaktrail_hdmi_enc_funcs = { void oaktrail_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev) { - struct psb_intel_encoder *psb_intel_encoder; - struct psb_intel_connector *psb_intel_connector; + struct gma_encoder *gma_encoder; + struct gma_connector *gma_connector; struct drm_connector *connector; struct drm_encoder *encoder; - psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL); - if (!psb_intel_encoder) + gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); + if (!gma_encoder) return; - psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL); - if (!psb_intel_connector) + gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); + if (!gma_connector) goto failed_connector; - connector = &psb_intel_connector->base; - encoder = &psb_intel_encoder->base; + connector = &gma_connector->base; + encoder = &gma_encoder->base; drm_connector_init(dev, connector, &oaktrail_hdmi_connector_funcs, DRM_MODE_CONNECTOR_DVID); @@ -672,10 +656,9 @@ void oaktrail_hdmi_init(struct drm_device *dev, &oaktrail_hdmi_enc_funcs, DRM_MODE_ENCODER_TMDS); - psb_intel_connector_attach_encoder(psb_intel_connector, - psb_intel_encoder); + gma_connector_attach_encoder(gma_connector, gma_encoder); - psb_intel_encoder->type = INTEL_OUTPUT_HDMI; + gma_encoder->type = INTEL_OUTPUT_HDMI; drm_encoder_helper_add(encoder, &oaktrail_hdmi_helper_funcs); drm_connector_helper_add(connector, &oaktrail_hdmi_connector_helper_funcs); @@ -683,16 +666,17 @@ void oaktrail_hdmi_init(struct drm_device *dev, connector->interlace_allowed = false; connector->doublescan_allowed = false; drm_sysfs_connector_add(connector); + dev_info(dev->dev, "HDMI initialised.\n"); return; failed_connector: - kfree(psb_intel_encoder); + kfree(gma_encoder); } static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) }, - {} + { 0 } }; void oaktrail_hdmi_setup(struct drm_device *dev) @@ -737,6 +721,9 @@ void oaktrail_hdmi_setup(struct drm_device *dev) dev_priv->hdmi_priv = hdmi_dev; oaktrail_hdmi_audio_disable(dev); + + dev_info(dev->dev, "HDMI hardware present.\n"); + return; free: @@ -766,6 +753,8 @@ 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; + struct psb_pipe *pipeb = &dev_priv->regs.pipe[1]; int i; /* dpll */ @@ -776,14 +765,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); + pipeb->conf = PSB_RVDC32(PIPEBCONF); + pipeb->src = PSB_RVDC32(PIPEBSRC); + pipeb->htotal = PSB_RVDC32(HTOTAL_B); + pipeb->hblank = PSB_RVDC32(HBLANK_B); + pipeb->hsync = PSB_RVDC32(HSYNC_B); + pipeb->vtotal = PSB_RVDC32(VTOTAL_B); + pipeb->vblank = PSB_RVDC32(VBLANK_B); + pipeb->vsync = PSB_RVDC32(VSYNC_B); hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF); hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC); @@ -795,21 +784,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); + pipeb->cntr = PSB_RVDC32(DSPBCNTR); + pipeb->stride = PSB_RVDC32(DSPBSTRIDE); + pipeb->addr = PSB_RVDC32(DSPBBASE); + pipeb->surf = PSB_RVDC32(DSPBSURF); + pipeb->linoff = PSB_RVDC32(DSPBLINOFF); + pipeb->tileoff = 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)); + pipeb->palette[i] = PSB_RVDC32(PALETTE_B + (i << 2)); } /* restore HDMI register state */ @@ -817,6 +806,8 @@ 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; + struct psb_pipe *pipeb = &dev_priv->regs.pipe[1]; int i; /* dpll */ @@ -828,13 +819,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(pipeb->src, PIPEBSRC); + PSB_WVDC32(pipeb->htotal, HTOTAL_B); + PSB_WVDC32(pipeb->hblank, HBLANK_B); + PSB_WVDC32(pipeb->hsync, HSYNC_B); + PSB_WVDC32(pipeb->vtotal, VTOTAL_B); + PSB_WVDC32(pipeb->vblank, VBLANK_B); + PSB_WVDC32(pipeb->vsync, VSYNC_B); PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC); PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B); @@ -844,22 +835,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(pipeb->conf, 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(pipeb->linoff, DSPBLINOFF); + PSB_WVDC32(pipeb->stride, DSPBSTRIDE); + PSB_WVDC32(pipeb->tileoff, DSPBTILEOFF); + PSB_WVDC32(pipeb->cntr, DSPBCNTR); + PSB_WVDC32(pipeb->surf, 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(pipeb->palette[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 705440874ac..e2810706114 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c @@ -99,7 +99,7 @@ static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg) i2c_dev->status = I2C_STAT_INIT; i2c_dev->msg = pmsg; i2c_dev->buf_offset = 0; - INIT_COMPLETION(i2c_dev->complete); + reinit_completion(&i2c_dev->complete); /* Enable I2C transaction */ temp = ((pmsg->len) << 20) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION; @@ -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 */ } @@ -250,7 +250,7 @@ static irqreturn_t oaktrail_hdmi_i2c_handler(int this_irq, void *dev) */ static void oaktrail_hdmi_i2c_gpio_fix(void) { - void *base; + void __iomem *base; unsigned int gpio_base = 0xff12c000; int gpio_len = 0x1000; u32 temp; @@ -319,8 +319,7 @@ void oaktrail_hdmi_i2c_exit(struct pci_dev *dev) struct hdmi_i2c_dev *i2c_dev; hdmi_dev = pci_get_drvdata(dev); - if (i2c_del_adapter(&oaktrail_hdmi_i2c_adapter)) - DRM_DEBUG_DRIVER("Failed to delete hdmi-i2c adapter\n"); + i2c_del_adapter(&oaktrail_hdmi_i2c_adapter); i2c_dev = hdmi_dev->i2c_dev; kfree(i2c_dev); diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c index 238bbe10530..9b099468a5d 100644 --- a/drivers/gpu/drm/gma500/oaktrail_lvds.c +++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c @@ -22,7 +22,7 @@ #include <linux/i2c.h> #include <drm/drmP.h> -#include <asm/mrst.h> +#include <asm/intel-mid.h> #include "intel_bios.h" #include "psb_drv.h" @@ -43,7 +43,7 @@ * Sets the power state for the panel. */ static void oaktrail_lvds_set_power(struct drm_device *dev, - struct psb_intel_encoder *psb_intel_encoder, + struct gma_encoder *gma_encoder, bool on) { u32 pp_status; @@ -78,13 +78,12 @@ static void oaktrail_lvds_set_power(struct drm_device *dev, static void oaktrail_lvds_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; - struct psb_intel_encoder *psb_intel_encoder = - to_psb_intel_encoder(encoder); + struct gma_encoder *gma_encoder = to_gma_encoder(encoder); if (mode == DRM_MODE_DPMS_ON) - oaktrail_lvds_set_power(dev, psb_intel_encoder, true); + oaktrail_lvds_set_power(dev, gma_encoder, true); else - oaktrail_lvds_set_power(dev, psb_intel_encoder, false); + oaktrail_lvds_set_power(dev, gma_encoder, false); /* XXX: We never power down the LVDS pairs. */ } @@ -133,8 +132,8 @@ static void oaktrail_lvds_mode_set(struct drm_encoder *encoder, return; } - drm_connector_property_get_value( - connector, + drm_object_property_get_value( + &connector->base, dev->mode_config.scaling_mode_property, &v); @@ -166,8 +165,7 @@ static void oaktrail_lvds_prepare(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_intel_encoder *psb_intel_encoder = - to_psb_intel_encoder(encoder); + struct gma_encoder *gma_encoder = to_gma_encoder(encoder); struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; if (!gma_power_begin(dev, true)) @@ -176,7 +174,7 @@ static void oaktrail_lvds_prepare(struct drm_encoder *encoder) mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & BACKLIGHT_DUTY_CYCLE_MASK); - oaktrail_lvds_set_power(dev, psb_intel_encoder, false); + oaktrail_lvds_set_power(dev, gma_encoder, false); gma_power_end(dev); } @@ -192,7 +190,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; @@ -203,14 +201,13 @@ static void oaktrail_lvds_commit(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_intel_encoder *psb_intel_encoder = - to_psb_intel_encoder(encoder); + struct gma_encoder *gma_encoder = to_gma_encoder(encoder); struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; if (mode_dev->backlight_duty_cycle == 0) mode_dev->backlight_duty_cycle = oaktrail_lvds_get_max_backlight(dev); - oaktrail_lvds_set_power(dev, psb_intel_encoder, true); + oaktrail_lvds_set_power(dev, gma_encoder, true); } static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = { @@ -221,30 +218,6 @@ static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = { .commit = oaktrail_lvds_commit, }; -static struct drm_display_mode lvds_configuration_modes[] = { - /* hard coded fixed mode for TPO LTPS LPJ040K001A */ - { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 33264, 800, 836, - 846, 1056, 0, 480, 489, 491, 525, 0, 0) }, - /* hard coded fixed mode for LVDS 800x480 */ - { DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 30994, 800, 801, - 802, 1024, 0, 480, 481, 482, 525, 0, 0) }, - /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ - { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072, - 1104, 1184, 0, 600, 603, 604, 608, 0, 0) }, - /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */ - { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104, - 1136, 1184, 0, 600, 603, 604, 608, 0, 0) }, - /* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */ - { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124, - 1204, 1312, 0, 600, 607, 610, 621, 0, 0) }, - /* hard coded fixed mode for LVDS 1024x768 */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, - 1184, 1344, 0, 768, 771, 777, 806, 0, 0) }, - /* hard coded fixed mode for LVDS 1366x768 */ - { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430, - 1558, 1664, 0, 768, 769, 770, 776, 0, 0) }, -}; - /* Returns the panel fixed mode from configuration. */ static void oaktrail_lvds_get_configuration_mode(struct drm_device *dev, @@ -257,7 +230,7 @@ static void oaktrail_lvds_get_configuration_mode(struct drm_device *dev, mode_dev->panel_fixed_mode = NULL; /* Use the firmware provided data on Moorestown */ - if (dev_priv->vbt_data.size != 0x00) { /*if non-zero, then use vbt*/ + if (dev_priv->has_gct) { mode = kzalloc(sizeof(*mode), GFP_KERNEL); if (!mode) return; @@ -306,10 +279,10 @@ static void oaktrail_lvds_get_configuration_mode(struct drm_device *dev, mode_dev->panel_fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); - /* Then guess */ + + /* If we still got no mode then bail */ if (mode_dev->panel_fixed_mode == NULL) - mode_dev->panel_fixed_mode - = drm_mode_duplicate(dev, &lvds_configuration_modes[2]); + return; drm_mode_set_name(mode_dev->panel_fixed_mode); drm_mode_set_crtcinfo(mode_dev->panel_fixed_mode, 0); @@ -325,26 +298,25 @@ static void oaktrail_lvds_get_configuration_mode(struct drm_device *dev, void oaktrail_lvds_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev) { - struct psb_intel_encoder *psb_intel_encoder; - struct psb_intel_connector *psb_intel_connector; + struct gma_encoder *gma_encoder; + struct gma_connector *gma_connector; struct drm_connector *connector; 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; */ - psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL); - if (!psb_intel_encoder) + gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); + if (!gma_encoder) return; - psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL); - if (!psb_intel_connector) + gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); + if (!gma_connector) goto failed_connector; - connector = &psb_intel_connector->base; - encoder = &psb_intel_encoder->base; + connector = &gma_connector->base; + encoder = &gma_encoder->base; dev_priv->is_lvds_on = true; drm_connector_init(dev, connector, &psb_intel_lvds_connector_funcs, @@ -353,9 +325,8 @@ void oaktrail_lvds_init(struct drm_device *dev, drm_encoder_init(dev, encoder, &psb_intel_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS); - psb_intel_connector_attach_encoder(psb_intel_connector, - psb_intel_encoder); - psb_intel_encoder->type = INTEL_OUTPUT_LVDS; + gma_connector_attach_encoder(gma_connector, gma_encoder); + gma_encoder->type = INTEL_OUTPUT_LVDS; drm_encoder_helper_add(encoder, &oaktrail_lvds_helper_funcs); drm_connector_helper_add(connector, @@ -364,15 +335,15 @@ void oaktrail_lvds_init(struct drm_device *dev, connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_connector_attach_property(connector, + drm_object_attach_property(&connector->base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); - drm_connector_attach_property(connector, + drm_object_attach_property(&connector->base, dev_priv->backlight_property, BRIGHTNESS_MAX_LEVEL); mode_dev->panel_wants_dither = false; - if (dev_priv->vbt_data.size != 0x00) + if (dev_priv->has_gct) mode_dev->panel_wants_dither = (dev_priv->gct_data. Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE); if (dev_priv->lvds_dither) @@ -388,6 +359,7 @@ void oaktrail_lvds_init(struct drm_device *dev, * if closed, act like it's not there for now */ + mutex_lock(&dev->mode_config.mutex); i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus); if (i2c_adap == NULL) dev_err(dev->dev, "No ddc adapter available!\n"); @@ -400,7 +372,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); } @@ -430,20 +402,24 @@ void oaktrail_lvds_init(struct drm_device *dev, } out: + mutex_unlock(&dev->mode_config.mutex); + drm_sysfs_connector_add(connector); return; failed_find: + mutex_unlock(&dev->mode_config.mutex); + dev_dbg(dev->dev, "No LVDS modes found, disabling.\n"); - if (psb_intel_encoder->ddc_bus) - psb_intel_i2c_destroy(psb_intel_encoder->ddc_bus); + if (gma_encoder->ddc_bus) + psb_intel_i2c_destroy(gma_encoder->ddc_bus); /* failed_ddc: */ drm_encoder_cleanup(encoder); drm_connector_cleanup(connector); - kfree(psb_intel_connector); + kfree(gma_connector); failed_connector: - kfree(psb_intel_encoder); + kfree(gma_encoder); } diff --git a/drivers/gpu/drm/gma500/opregion.c b/drivers/gpu/drm/gma500/opregion.c new file mode 100644 index 00000000000..ab696ca7eee --- /dev/null +++ b/drivers/gpu/drm/gma500/opregion.c @@ -0,0 +1,357 @@ +/* + * 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 <linux/acpi.h> +#include "psb_drv.h" +#include "psb_intel_reg.h" + +#define PCI_ASLE 0xe4 +#define PCI_ASLS 0xfc + +#define OPREGION_HEADER_OFFSET 0 +#define OPREGION_ACPI_OFFSET 0x100 +#define ACPI_CLID 0x01ac /* current lid state indicator */ +#define ACPI_CDCK 0x01b0 /* current docking state indicator */ +#define OPREGION_SWSCI_OFFSET 0x200 +#define OPREGION_ASLE_OFFSET 0x300 +#define OPREGION_VBT_OFFSET 0x400 + +#define OPREGION_SIGNATURE "IntelGraphicsMem" +#define MBOX_ACPI (1<<0) +#define MBOX_SWSCI (1<<1) +#define MBOX_ASLE (1<<2) + +struct opregion_header { + u8 signature[16]; + u32 size; + u32 opregion_ver; + u8 bios_ver[32]; + u8 vbios_ver[16]; + u8 driver_ver[16]; + u32 mboxes; + u8 reserved[164]; +} __packed; + +/* OpRegion mailbox #1: public ACPI methods */ +struct opregion_acpi { + u32 drdy; /* driver readiness */ + u32 csts; /* notification status */ + u32 cevt; /* current event */ + u8 rsvd1[20]; + u32 didl[8]; /* supported display devices ID list */ + u32 cpdl[8]; /* currently presented display list */ + u32 cadl[8]; /* currently active display list */ + u32 nadl[8]; /* next active devices list */ + u32 aslp; /* ASL sleep time-out */ + u32 tidx; /* toggle table index */ + u32 chpd; /* current hotplug enable indicator */ + u32 clid; /* current lid state*/ + u32 cdck; /* current docking state */ + u32 sxsw; /* Sx state resume */ + u32 evts; /* ASL supported events */ + u32 cnot; /* current OS notification */ + u32 nrdy; /* driver status */ + u8 rsvd2[60]; +} __packed; + +/* OpRegion mailbox #2: SWSCI */ +struct opregion_swsci { + /*FIXME: add it later*/ +} __packed; + +/* OpRegion mailbox #3: ASLE */ +struct opregion_asle { + u32 ardy; /* driver readiness */ + u32 aslc; /* ASLE interrupt command */ + u32 tche; /* technology enabled indicator */ + u32 alsi; /* current ALS illuminance reading */ + u32 bclp; /* backlight brightness to set */ + u32 pfit; /* panel fitting state */ + u32 cblv; /* current brightness level */ + u16 bclm[20]; /* backlight level duty cycle mapping table */ + u32 cpfm; /* current panel fitting mode */ + u32 epfm; /* enabled panel fitting modes */ + u8 plut[74]; /* panel LUT and identifier */ + u32 pfmb; /* PWM freq and min brightness */ + u8 rsvd[102]; +} __packed; + +/* ASLE irq request bits */ +#define ASLE_SET_ALS_ILLUM (1 << 0) +#define ASLE_SET_BACKLIGHT (1 << 1) +#define ASLE_SET_PFIT (1 << 2) +#define ASLE_SET_PWM_FREQ (1 << 3) +#define ASLE_REQ_MSK 0xf + +/* response bits of ASLE irq request */ +#define ASLE_ALS_ILLUM_FAILED (1<<10) +#define ASLE_BACKLIGHT_FAILED (1<<12) +#define ASLE_PFIT_FAILED (1<<14) +#define ASLE_PWM_FREQ_FAILED (1<<16) + +/* ASLE backlight brightness to set */ +#define ASLE_BCLP_VALID (1<<31) +#define ASLE_BCLP_MSK (~(1<<31)) + +/* ASLE panel fitting request */ +#define ASLE_PFIT_VALID (1<<31) +#define ASLE_PFIT_CENTER (1<<0) +#define ASLE_PFIT_STRETCH_TEXT (1<<1) +#define ASLE_PFIT_STRETCH_GFX (1<<2) + +/* response bits of ASLE irq request */ +#define ASLE_ALS_ILLUM_FAILED (1<<10) +#define ASLE_BACKLIGHT_FAILED (1<<12) +#define ASLE_PFIT_FAILED (1<<14) +#define ASLE_PWM_FREQ_FAILED (1<<16) + +/* ASLE backlight brightness to set */ +#define ASLE_BCLP_VALID (1<<31) +#define ASLE_BCLP_MSK (~(1<<31)) + +/* ASLE panel fitting request */ +#define ASLE_PFIT_VALID (1<<31) +#define ASLE_PFIT_CENTER (1<<0) +#define ASLE_PFIT_STRETCH_TEXT (1<<1) +#define ASLE_PFIT_STRETCH_GFX (1<<2) + +/* PWM frequency and minimum brightness */ +#define ASLE_PFMB_BRIGHTNESS_MASK (0xff) +#define ASLE_PFMB_BRIGHTNESS_VALID (1<<8) +#define ASLE_PFMB_PWM_MASK (0x7ffffe00) +#define ASLE_PFMB_PWM_VALID (1<<31) + +#define ASLE_CBLV_VALID (1<<31) + +static struct psb_intel_opregion *system_opregion; + +static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct opregion_asle *asle = dev_priv->opregion.asle; + struct backlight_device *bd = dev_priv->backlight_device; + + DRM_DEBUG_DRIVER("asle set backlight %x\n", bclp); + + if (!(bclp & ASLE_BCLP_VALID)) + return ASLE_BACKLIGHT_FAILED; + + if (bd == NULL) + return ASLE_BACKLIGHT_FAILED; + + bclp &= ASLE_BCLP_MSK; + if (bclp > 255) + return ASLE_BACKLIGHT_FAILED; + + if (config_enabled(CONFIG_BACKLIGHT_CLASS_DEVICE)) { + int max = bd->props.max_brightness; + gma_backlight_set(dev, bclp * max / 255); + } + + asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID; + + return 0; +} + +static void psb_intel_opregion_asle_work(struct work_struct *work) +{ + struct psb_intel_opregion *opregion = + container_of(work, struct psb_intel_opregion, asle_work); + struct drm_psb_private *dev_priv = + container_of(opregion, struct drm_psb_private, opregion); + struct opregion_asle *asle = opregion->asle; + u32 asle_stat = 0; + u32 asle_req; + + if (!asle) + return; + + asle_req = asle->aslc & ASLE_REQ_MSK; + if (!asle_req) { + DRM_DEBUG_DRIVER("non asle set request??\n"); + return; + } + + if (asle_req & ASLE_SET_BACKLIGHT) + asle_stat |= asle_set_backlight(dev_priv->dev, asle->bclp); + + asle->aslc = asle_stat; + +} + +void psb_intel_opregion_asle_intr(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + + if (dev_priv->opregion.asle) + schedule_work(&dev_priv->opregion.asle_work); +} + +#define ASLE_ALS_EN (1<<0) +#define ASLE_BLC_EN (1<<1) +#define ASLE_PFIT_EN (1<<2) +#define ASLE_PFMB_EN (1<<3) + +void psb_intel_opregion_enable_asle(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct opregion_asle *asle = dev_priv->opregion.asle; + + if (asle && system_opregion ) { + /* Don't do this on Medfield or other non PC like devices, they + use the bit for something different altogether */ + psb_enable_pipestat(dev_priv, 0, PIPE_LEGACY_BLC_EVENT_ENABLE); + psb_enable_pipestat(dev_priv, 1, PIPE_LEGACY_BLC_EVENT_ENABLE); + + asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN + | ASLE_PFMB_EN; + asle->ardy = 1; + } +} + +#define ACPI_EV_DISPLAY_SWITCH (1<<0) +#define ACPI_EV_LID (1<<1) +#define ACPI_EV_DOCK (1<<2) + + +static int psb_intel_opregion_video_event(struct notifier_block *nb, + unsigned long val, void *data) +{ + /* The only video events relevant to opregion are 0x80. These indicate + either a docking event, lid switch or display switch request. In + Linux, these are handled by the dock, button and video drivers. + We might want to fix the video driver to be opregion-aware in + future, but right now we just indicate to the firmware that the + request has been handled */ + + struct opregion_acpi *acpi; + + if (!system_opregion) + return NOTIFY_DONE; + + acpi = system_opregion->acpi; + acpi->csts = 0; + + return NOTIFY_OK; +} + +static struct notifier_block psb_intel_opregion_notifier = { + .notifier_call = psb_intel_opregion_video_event, +}; + +void psb_intel_opregion_init(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_intel_opregion *opregion = &dev_priv->opregion; + + if (!opregion->header) + return; + + if (opregion->acpi) { + /* Notify BIOS we are ready to handle ACPI video ext notifs. + * Right now, all the events are handled by the ACPI video + * module. We don't actually need to do anything with them. */ + opregion->acpi->csts = 0; + opregion->acpi->drdy = 1; + + system_opregion = opregion; + register_acpi_notifier(&psb_intel_opregion_notifier); + } +} + +void psb_intel_opregion_fini(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_intel_opregion *opregion = &dev_priv->opregion; + + if (!opregion->header) + return; + + if (opregion->acpi) { + opregion->acpi->drdy = 0; + + system_opregion = NULL; + unregister_acpi_notifier(&psb_intel_opregion_notifier); + } + + cancel_work_sync(&opregion->asle_work); + + /* just clear all opregion memory pointers now */ + iounmap(opregion->header); + opregion->header = NULL; + opregion->acpi = NULL; + opregion->swsci = NULL; + opregion->asle = NULL; + opregion->vbt = NULL; +} + +int psb_intel_opregion_setup(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_intel_opregion *opregion = &dev_priv->opregion; + u32 opregion_phy, mboxes; + void __iomem *base; + int err = 0; + + pci_read_config_dword(dev->pdev, PCI_ASLS, &opregion_phy); + if (opregion_phy == 0) { + DRM_DEBUG_DRIVER("ACPI Opregion not supported\n"); + return -ENOTSUPP; + } + + INIT_WORK(&opregion->asle_work, psb_intel_opregion_asle_work); + + DRM_DEBUG("OpRegion detected at 0x%8x\n", opregion_phy); + base = acpi_os_ioremap(opregion_phy, 8*1024); + if (!base) + return -ENOMEM; + + if (memcmp(base, OPREGION_SIGNATURE, 16)) { + DRM_DEBUG_DRIVER("opregion signature mismatch\n"); + err = -EINVAL; + goto err_out; + } + + opregion->header = base; + opregion->vbt = base + OPREGION_VBT_OFFSET; + + opregion->lid_state = base + ACPI_CLID; + + mboxes = opregion->header->mboxes; + if (mboxes & MBOX_ACPI) { + DRM_DEBUG_DRIVER("Public ACPI methods supported\n"); + opregion->acpi = base + OPREGION_ACPI_OFFSET; + } + + if (mboxes & MBOX_ASLE) { + DRM_DEBUG_DRIVER("ASLE supported\n"); + opregion->asle = base + OPREGION_ASLE_OFFSET; + } + + return 0; + +err_out: + iounmap(base); + return err; +} + diff --git a/drivers/gpu/drm/gma500/opregion.h b/drivers/gpu/drm/gma500/opregion.h new file mode 100644 index 00000000000..4a90f8b0e16 --- /dev/null +++ b/drivers/gpu/drm/gma500/opregion.h @@ -0,0 +1,54 @@ +/* + * Copyright 2012 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. + * + */ + +#if defined(CONFIG_ACPI) +extern void psb_intel_opregion_asle_intr(struct drm_device *dev); +extern void psb_intel_opregion_init(struct drm_device *dev); +extern void psb_intel_opregion_fini(struct drm_device *dev); +extern int psb_intel_opregion_setup(struct drm_device *dev); +extern void psb_intel_opregion_enable_asle(struct drm_device *dev); + +#else + +extern inline void psb_intel_opregion_asle_intr(struct drm_device *dev) +{ +} + +extern inline void psb_intel_opregion_init(struct drm_device *dev) +{ +} + +extern inline void psb_intel_opregion_fini(struct drm_device *dev) +{ +} + +extern inline int psb_intel_opregion_setup(struct drm_device *dev) +{ + return 0; +} + +extern inline void psb_intel_opregion_enable_asle(struct drm_device *dev) +{ +} +#endif diff --git a/drivers/gpu/drm/gma500/power.c b/drivers/gpu/drm/gma500/power.c index 94025693bae..b6b135fcd59 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; @@ -112,6 +110,8 @@ static void gma_resume_display(struct pci_dev *pdev) PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); pci_write_config_word(pdev, PSB_GMCH_CTRL, dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED); + + psb_gtt_restore(dev); /* Rebuild our GTT mappings */ dev_priv->ops->restore_regs(dev); } @@ -132,9 +132,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 +162,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 +195,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 +303,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) @@ -314,3 +315,18 @@ int psb_runtime_idle(struct device *dev) else return 1; } + +int gma_power_thaw(struct device *_dev) +{ + return gma_power_resume(_dev); +} + +int gma_power_freeze(struct device *_dev) +{ + return gma_power_suspend(_dev); +} + +int gma_power_restore(struct device *_dev) +{ + return gma_power_resume(_dev); +} diff --git a/drivers/gpu/drm/gma500/power.h b/drivers/gpu/drm/gma500/power.h index 1969d2ecb32..56d8708bd41 100644 --- a/drivers/gpu/drm/gma500/power.h +++ b/drivers/gpu/drm/gma500/power.h @@ -41,6 +41,9 @@ void gma_power_uninit(struct drm_device *dev); */ int gma_power_suspend(struct device *dev); int gma_power_resume(struct device *dev); +int gma_power_thaw(struct device *dev); +int gma_power_freeze(struct device *dev); +int gma_power_restore(struct device *_dev); /* * These are the functions the driver should use to wrap all hw access diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index e5f5906172b..07df7d4eea7 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -20,12 +20,13 @@ #include <linux/backlight.h> #include <drm/drmP.h> #include <drm/drm.h> -#include "gma_drm.h" +#include <drm/gma_drm.h> #include "psb_drv.h" #include "psb_reg.h" #include "psb_intel_reg.h" #include "intel_bios.h" - +#include "psb_device.h" +#include "gma_device.h" static int psb_output_init(struct drm_device *dev) { @@ -144,6 +145,10 @@ static int psb_backlight_init(struct drm_device *dev) psb_backlight_device->props.max_brightness = 100; backlight_update_status(psb_backlight_device); dev_priv->backlight_device = psb_backlight_device; + + /* This must occur after the backlight is properly initialised */ + psb_lid_timer_init(dev_priv); + return 0; } @@ -177,28 +182,30 @@ 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); + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (drm_helper_crtc_in_use(crtc)) crtc->funcs->save(crtc); } list_for_each_entry(connector, &dev->mode_config.connector_list, head) - connector->funcs->save(connector); + if (connector->funcs->save) + connector->funcs->save(connector); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } @@ -213,29 +220,31 @@ 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); - mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) if (drm_helper_crtc_in_use(crtc)) crtc->funcs->restore(crtc); list_for_each_entry(connector, &dev->mode_config.connector_list, head) - connector->funcs->restore(connector); + if (connector->funcs->restore) + connector->funcs->restore(connector); - mutex_unlock(&dev->mode_config.mutex); + drm_modeset_unlock_all(dev); return 0; } @@ -249,55 +258,73 @@ static int psb_power_up(struct drm_device *dev) return 0; } -static void psb_get_core_freq(struct drm_device *dev) -{ - uint32_t clock; - struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0); - struct drm_psb_private *dev_priv = dev->dev_private; - - /*pci_write_config_dword(pci_root, 0xD4, 0x00C32004);*/ - /*pci_write_config_dword(pci_root, 0xD0, 0xE0033000);*/ - - pci_write_config_dword(pci_root, 0xD0, 0xD0050300); - pci_read_config_dword(pci_root, 0xD4, &clock); - pci_dev_put(pci_root); - - switch (clock & 0x07) { - case 0: - dev_priv->core_freq = 100; - break; - case 1: - dev_priv->core_freq = 133; - break; - case 2: - dev_priv->core_freq = 150; - break; - case 3: - dev_priv->core_freq = 178; - break; - case 4: - dev_priv->core_freq = 200; - break; - case 5: - case 6: - case 7: - dev_priv->core_freq = 266; - default: - dev_priv->core_freq = 0; +/* Poulsbo */ +static const struct psb_offset psb_regmap[2] = { + { + .fp0 = FPA0, + .fp1 = FPA1, + .cntr = DSPACNTR, + .conf = PIPEACONF, + .src = PIPEASRC, + .dpll = DPLL_A, + .htotal = HTOTAL_A, + .hblank = HBLANK_A, + .hsync = HSYNC_A, + .vtotal = VTOTAL_A, + .vblank = VBLANK_A, + .vsync = VSYNC_A, + .stride = DSPASTRIDE, + .size = DSPASIZE, + .pos = DSPAPOS, + .base = DSPABASE, + .surf = DSPASURF, + .addr = DSPABASE, + .status = PIPEASTAT, + .linoff = DSPALINOFF, + .tileoff = DSPATILEOFF, + .palette = PALETTE_A, + }, + { + .fp0 = FPB0, + .fp1 = FPB1, + .cntr = DSPBCNTR, + .conf = PIPEBCONF, + .src = PIPEBSRC, + .dpll = DPLL_B, + .htotal = HTOTAL_B, + .hblank = HBLANK_B, + .hsync = HSYNC_B, + .vtotal = VTOTAL_B, + .vblank = VBLANK_B, + .vsync = VSYNC_B, + .stride = DSPBSTRIDE, + .size = DSPBSIZE, + .pos = DSPBPOS, + .base = DSPBBASE, + .surf = DSPBSURF, + .addr = DSPBBASE, + .status = PIPEBSTAT, + .linoff = DSPBLINOFF, + .tileoff = DSPBTILEOFF, + .palette = PALETTE_B, } -} +}; static int psb_chip_setup(struct drm_device *dev) { - psb_get_core_freq(dev); + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->regmap = psb_regmap; + gma_get_core_freq(dev); gma_intel_setup_gmbus(dev); - gma_intel_opregion_init(dev); + psb_intel_opregion_init(dev); psb_intel_init_bios(dev); return 0; } static void psb_chip_teardown(struct drm_device *dev) { + struct drm_psb_private *dev_priv = dev->dev_private; + psb_lid_timer_takedown(dev_priv); gma_intel_teardown_gmbus(dev); } @@ -306,12 +333,17 @@ const struct psb_ops psb_chip_ops = { .accel_2d = 1, .pipes = 2, .crtcs = 2, + .hdmi_mask = (1 << 0), + .lvds_mask = (1 << 1), + .sdvo_mask = (1 << 0), + .cursor_needs_phys = 1, .sgx_offset = PSB_SGX_OFFSET, .chip_setup = psb_chip_setup, .chip_teardown = psb_chip_teardown, .crtc_helper = &psb_intel_helper_funcs, .crtc_funcs = &psb_intel_crtc_funcs, + .clock_funcs = &psb_clock_funcs, .output_init = psb_output_init, diff --git a/drivers/gpu/drm/gma500/psb_intel_display.h b/drivers/gpu/drm/gma500/psb_device.h index 535b49a5e40..35e304c7f85 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.h +++ b/drivers/gpu/drm/gma500/psb_device.h @@ -1,4 +1,6 @@ -/* copyright (c) 2008, Intel Corporation +/* + * Copyright © 2013 Patrik Jakobsson + * Copyright © 2011 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, @@ -12,17 +14,11 @@ * 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> */ -#ifndef _INTEL_DISPLAY_H_ -#define _INTEL_DISPLAY_H_ +#ifndef _PSB_DEVICE_H_ +#define _PSB_DEVICE_H_ -bool psb_intel_pipe_has_type(struct drm_crtc *crtc, int type); -void psb_intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, uint32_t type, uint32_t size); -void psb_intel_crtc_destroy(struct drm_crtc *crtc); +extern const struct gma_clock_funcs psb_clock_funcs; #endif diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index f14768f2b36..6e8fe9ec02b 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -21,7 +21,6 @@ #include <drm/drmP.h> #include <drm/drm.h> -#include "gma_drm.h" #include "psb_drv.h" #include "framebuffer.h" #include "psb_reg.h" @@ -37,108 +36,87 @@ #include <acpi/video.h> #include <linux/module.h> -static int drm_psb_trap_pagefaults; - -static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent); - -MODULE_PARM_DESC(trap_pagefaults, "Error and reset on MMU pagefaults"); -module_param_named(trap_pagefaults, drm_psb_trap_pagefaults, int, 0600); - +static struct drm_driver driver; +static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +/* + * The table below contains a mapping of the PCI vendor ID and the PCI Device ID + * to the different groups of PowerVR 5-series chip designs + * + * 0x8086 = Intel Corporation + * + * PowerVR SGX535 - Poulsbo - Intel GMA 500, Intel Atom Z5xx + * PowerVR SGX535 - Moorestown - Intel GMA 600 + * PowerVR SGX535 - Oaktrail - Intel GMA 600, Intel Atom Z6xx, E6xx + * PowerVR SGX540 - Medfield - Intel Atom Z2460 + * PowerVR SGX544MP2 - Medfield - + * PowerVR SGX545 - Cedartrail - Intel GMA 3600, Intel Atom D2500, N2600 + * PowerVR SGX545 - Cedartrail - Intel GMA 3650, Intel Atom D2550, D2700, + * N2800 + */ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = { { 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, { 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, #if defined(CONFIG_DRM_GMA600) - { 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops}, - { 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops}, - { 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops}, - { 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops}, - { 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops}, - { 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops}, - { 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops}, - { 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops}, - /* Atom E620 */ - { 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops}, + { 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, + { 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, + { 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, + { 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, + { 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, + { 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, + { 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, + { 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, + { 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}, - { 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops}, - { 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}, + { 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 }, + { 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 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 }, + { 0x8086, 0x0be8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 0x8086, 0x0be9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 0x8086, 0x0bea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 0x8086, 0x0beb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 0x8086, 0x0bec, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, + { 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, #endif - { 0, 0, 0} + { 0, } }; MODULE_DEVICE_TABLE(pci, pciidlist); /* * Standard IOCTLs. */ - -#define DRM_IOCTL_PSB_ADB \ - DRM_IOWR(DRM_GMA_ADB + DRM_COMMAND_BASE, uint32_t) -#define DRM_IOCTL_PSB_MODE_OPERATION \ - DRM_IOWR(DRM_GMA_MODE_OPERATION + DRM_COMMAND_BASE, \ - struct drm_psb_mode_operation_arg) -#define DRM_IOCTL_PSB_STOLEN_MEMORY \ - DRM_IOWR(DRM_GMA_STOLEN_MEMORY + DRM_COMMAND_BASE, \ - struct drm_psb_stolen_memory_arg) -#define DRM_IOCTL_PSB_GAMMA \ - DRM_IOWR(DRM_GMA_GAMMA + DRM_COMMAND_BASE, \ - struct drm_psb_dpst_lut_arg) -#define DRM_IOCTL_PSB_DPST_BL \ - DRM_IOWR(DRM_GMA_DPST_BL + DRM_COMMAND_BASE, \ - uint32_t) -#define DRM_IOCTL_PSB_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 \ - DRM_IOWR(DRM_GMA_GEM_CREATE + DRM_COMMAND_BASE, \ - struct drm_psb_gem_create) -#define DRM_IOCTL_PSB_GEM_MMAP \ - DRM_IOWR(DRM_GMA_GEM_MMAP + DRM_COMMAND_BASE, \ - struct drm_psb_gem_mmap) - -static int psb_adb_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -static int psb_gamma_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv); -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_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_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, - psb_intel_get_pipe_from_crtc_id, 0), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_CREATE, psb_gem_create_ioctl, - DRM_UNLOCKED | DRM_AUTH), - PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_MMAP, psb_gem_mmap_ioctl, - DRM_UNLOCKED | DRM_AUTH), +static const struct drm_ioctl_desc psb_ioctls[] = { }; -static void psb_lastclose(struct drm_device *dev) +static void psb_driver_lastclose(struct drm_device *dev) { - return; -} + int ret; + struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_fbdev *fbdev = dev_priv->fbdev; -static void psb_do_takedown(struct drm_device *dev) -{ + ret = drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->psb_fb_helper); + if (ret) + DRM_DEBUG("failed to restore crtc mode\n"); + + return; } static int psb_do_init(struct drm_device *dev) @@ -148,77 +126,54 @@ static int psb_do_init(struct drm_device *dev) uint32_t stolen_gtt; - int ret = -ENOMEM; - if (pg->mmu_gatt_start & 0x0FFFFFFF) { dev_err(dev->dev, "Gatt must be 256M aligned. This is a bug.\n"); - ret = -EINVAL; - goto out_err; + return -EINVAL; } - stolen_gtt = (pg->stolen_size >> PAGE_SHIFT) * 4; stolen_gtt = (stolen_gtt + PAGE_SIZE - 1) >> PAGE_SHIFT; - stolen_gtt = - (stolen_gtt < pg->gtt_pages) ? stolen_gtt : pg->gtt_pages; + stolen_gtt = (stolen_gtt < pg->gtt_pages) ? stolen_gtt : pg->gtt_pages; dev_priv->gatt_free_offset = pg->mmu_gatt_start + (stolen_gtt << PAGE_SHIFT) * 1024; - if (1 || drm_debug) { - uint32_t core_id = PSB_RSGX32(PSB_CR_CORE_ID); - uint32_t core_rev = PSB_RSGX32(PSB_CR_CORE_REVISION); - DRM_INFO("SGX core id = 0x%08x\n", core_id); - DRM_INFO("SGX core rev major = 0x%02x, minor = 0x%02x\n", - (core_rev & _PSB_CC_REVISION_MAJOR_MASK) >> - _PSB_CC_REVISION_MAJOR_SHIFT, - (core_rev & _PSB_CC_REVISION_MINOR_MASK) >> - _PSB_CC_REVISION_MINOR_SHIFT); - DRM_INFO - ("SGX core rev maintenance = 0x%02x, designer = 0x%02x\n", - (core_rev & _PSB_CC_REVISION_MAINTENANCE_MASK) >> - _PSB_CC_REVISION_MAINTENANCE_SHIFT, - (core_rev & _PSB_CC_REVISION_DESIGNER_MASK) >> - _PSB_CC_REVISION_DESIGNER_SHIFT); - } - - spin_lock_init(&dev_priv->irqmask_lock); spin_lock_init(&dev_priv->lock_2d); PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK0); PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK1); PSB_RSGX32(PSB_CR_BIF_BANK1); - PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_MMU_ER_MASK, - PSB_CR_BIF_CTRL); + + /* Do not bypass any MMU access, let them pagefault instead */ + PSB_WSGX32((PSB_RSGX32(PSB_CR_BIF_CTRL) & ~_PSB_MMU_ER_MASK), + PSB_CR_BIF_CTRL); + PSB_RSGX32(PSB_CR_BIF_CTRL); + psb_spank(dev_priv); /* mmu_gatt ?? */ PSB_WSGX32(pg->gatt_start, PSB_CR_BIF_TWOD_REQ_BASE); + PSB_RSGX32(PSB_CR_BIF_TWOD_REQ_BASE); /* Post */ + return 0; -out_err: - psb_do_takedown(dev); - return ret; } static int psb_driver_unload(struct drm_device *dev) { struct drm_psb_private *dev_priv = dev->dev_private; - /* Kill vblank etc here */ - - gma_backlight_exit(dev); - - psb_modeset_cleanup(dev); + /* TODO: Kill vblank etc here */ if (dev_priv) { - psb_lid_timer_takedown(dev_priv); - gma_intel_opregion_exit(dev); + if (dev_priv->backlight_device) + gma_backlight_exit(dev); + psb_modeset_cleanup(dev); if (dev_priv->ops->chip_teardown) dev_priv->ops->chip_teardown(dev); - psb_do_takedown(dev); + psb_intel_opregion_fini(dev); if (dev_priv->pf_pd) { psb_mmu_free_pagedir(dev_priv->pf_pd); @@ -239,6 +194,7 @@ static int psb_driver_unload(struct drm_device *dev) } psb_gtt_takedown(dev); if (dev_priv->scratch_page) { + set_pages_wb(dev_priv->scratch_page, 1); __free_page(dev_priv->scratch_page); dev_priv->scratch_page = NULL; } @@ -250,43 +206,45 @@ static int psb_driver_unload(struct drm_device *dev) iounmap(dev_priv->sgx_reg); dev_priv->sgx_reg = NULL; } + if (dev_priv->aux_reg) { + iounmap(dev_priv->aux_reg); + dev_priv->aux_reg = NULL; + } + if (dev_priv->aux_pdev) + pci_dev_put(dev_priv->aux_pdev); + + /* Destroy VBT data */ + psb_intel_destroy_bios(dev); kfree(dev_priv); dev->dev_private = NULL; - - /*destroy VBT data*/ - psb_intel_destroy_bios(dev); } - gma_power_uninit(dev); - return 0; } - -static int psb_driver_load(struct drm_device *dev, unsigned long chipset) +static int psb_driver_load(struct drm_device *dev, unsigned long flags) { struct drm_psb_private *dev_priv; - unsigned long resource_start; - struct psb_gtt *pg; + unsigned long resource_start, resource_len; unsigned long irqflags; int ret = -ENOMEM; - uint32_t tt_pages; struct drm_connector *connector; - struct psb_intel_encoder *psb_intel_encoder; + struct gma_encoder *gma_encoder; + struct psb_gtt *pg; + /* allocating and initializing driver private data */ dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); if (dev_priv == NULL) return -ENOMEM; - dev_priv->ops = (struct psb_ops *)chipset; + dev_priv->ops = (struct psb_ops *)flags; dev_priv->dev = dev; dev->dev_private = (void *) dev_priv; - if (!IS_PSB(dev)) { - if (pci_enable_msi(dev->pdev)) - dev_warn(dev->dev, "Enabling MSI failed!\n"); - } + pg = &dev_priv->gtt; + + pci_set_master(dev->pdev); dev_priv->num_pipe = dev_priv->ops->pipes; @@ -302,6 +260,32 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) if (!dev_priv->sgx_reg) goto out_err; + if (IS_MRST(dev)) { + dev_priv->aux_pdev = pci_get_bus_and_slot(0, PCI_DEVFN(3, 0)); + + if (dev_priv->aux_pdev) { + resource_start = pci_resource_start(dev_priv->aux_pdev, + PSB_AUX_RESOURCE); + resource_len = pci_resource_len(dev_priv->aux_pdev, + PSB_AUX_RESOURCE); + dev_priv->aux_reg = ioremap_nocache(resource_start, + resource_len); + if (!dev_priv->aux_reg) + goto out_err; + + DRM_DEBUG_KMS("Found aux vdc"); + } else { + /* Couldn't find the aux vdc so map to primary vdc */ + dev_priv->aux_reg = dev_priv->vdc_reg; + DRM_DEBUG_KMS("Couldn't find aux pci device"); + } + dev_priv->gmbus_reg = dev_priv->aux_reg; + } else { + dev_priv->gmbus_reg = dev_priv->vdc_reg; + } + + psb_intel_opregion_setup(dev); + ret = dev_priv->ops->chip_setup(dev); if (ret) goto out_err; @@ -321,37 +305,35 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) if (ret) goto out_err; - dev_priv->mmu = psb_mmu_driver_init((void *)0, - drm_psb_trap_pagefaults, 0, - dev_priv); + dev_priv->mmu = psb_mmu_driver_init(dev, 1, 0, 0); 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; - psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0); - psb_mmu_set_pd_context(dev_priv->pf_pd, 1); - ret = psb_do_init(dev); if (ret) return ret; + /* Add stolen memory to SGX MMU */ + down_read(&pg->sem); + ret = psb_mmu_insert_pfn_sequence(psb_mmu_get_default_pd(dev_priv->mmu), + dev_priv->stolen_base >> PAGE_SHIFT, + pg->gatt_start, + pg->stolen_size >> PAGE_SHIFT, 0); + up_read(&pg->sem); + + psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0); + psb_mmu_set_pd_context(dev_priv->pf_pd, 1); + PSB_WSGX32(0x20000000, PSB_CR_PDS_EXEC_BASE); PSB_WSGX32(0x30000000, PSB_CR_BIF_3D_REQ_BASE); -/* igd_opregion_init(&dev_priv->opregion_dev); */ acpi_video_register(); - if (dev_priv->lid_state) - psb_lid_timer_init(dev_priv); + /* Setup vertical blanking handling */ ret = drm_vblank_init(dev, dev_priv->num_pipe); if (ret) goto out_err; @@ -369,13 +351,11 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R); PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R); spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); - if (IS_PSB(dev) && drm_core_check_feature(dev, DRIVER_MODESET)) - drm_irq_install(dev); - dev->vblank_disable_allowed = 1; + drm_irq_install(dev, dev->pdev->irq); + dev->vblank_disable_allowed = true; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ - dev->driver->get_vblank_counter = psb_get_vblank_counter; psb_modeset_init(dev); @@ -385,9 +365,9 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) /* Only add backlight support if we have LVDS output */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - psb_intel_encoder = psb_intel_attached_encoder(connector); + gma_encoder = gma_attached_encoder(connector); - switch (psb_intel_encoder->type) { + switch (gma_encoder->type) { case INTEL_OUTPUT_LVDS: case INTEL_OUTPUT_MIPI: ret = gma_backlight_init(dev); @@ -397,19 +377,20 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset) if (ret) return ret; + psb_intel_opregion_enable_asle(dev); #if 0 - /*enable runtime pm at last*/ + /* Enable runtime pm at last */ pm_runtime_enable(&dev->pdev->dev); pm_runtime_set_active(&dev->pdev->dev); #endif - /*Intel drm driver load is done, continue doing pvr load*/ + /* Intel drm driver load is done, continue doing pvr load */ return 0; out_err: psb_driver_unload(dev); 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; } @@ -424,161 +405,6 @@ static inline void get_brightness(struct backlight_device *bd) #endif } -static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = psb_priv(dev); - uint32_t *arg = data; - - dev_priv->blc_adj2 = *arg; - get_brightness(dev_priv->backlight_device); - return 0; -} - -static int psb_adb_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = psb_priv(dev); - uint32_t *arg = data; - - dev_priv->blc_adj1 = *arg; - get_brightness(dev_priv->backlight_device); - return 0; -} - -static int psb_gamma_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_dpst_lut_arg *lut_arg = data; - struct drm_mode_object *obj; - struct drm_crtc *crtc; - struct drm_connector *connector; - struct psb_intel_crtc *psb_intel_crtc; - int i = 0; - int32_t obj_id; - - obj_id = lut_arg->output_id; - obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR); - if (!obj) { - dev_dbg(dev->dev, "Invalid Connector object.\n"); - return -EINVAL; - } - - connector = obj_to_connector(obj); - crtc = connector->encoder->crtc; - psb_intel_crtc = to_psb_intel_crtc(crtc); - - for (i = 0; i < 256; i++) - psb_intel_crtc->lut_adj[i] = lut_arg->lut[i]; - - psb_intel_crtc_load_lut(crtc); - - return 0; -} - -static int psb_mode_operation_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - uint32_t obj_id; - uint16_t op; - struct drm_mode_modeinfo *umode; - struct drm_display_mode *mode = NULL; - struct drm_psb_mode_operation_arg *arg; - struct drm_mode_object *obj; - struct drm_connector *connector; - struct drm_connector_helper_funcs *connector_funcs; - int ret = 0; - int resp = MODE_OK; - - arg = (struct drm_psb_mode_operation_arg *)data; - obj_id = arg->obj_id; - op = arg->operation; - - switch (op) { - case PSB_MODE_OPERATION_MODE_VALID: - umode = &arg->mode; - - mutex_lock(&dev->mode_config.mutex); - - obj = drm_mode_object_find(dev, obj_id, - DRM_MODE_OBJECT_CONNECTOR); - if (!obj) { - ret = -EINVAL; - goto mode_op_out; - } - - connector = obj_to_connector(obj); - - mode = drm_mode_create(dev); - if (!mode) { - ret = -ENOMEM; - goto mode_op_out; - } - - /* drm_crtc_convert_umode(mode, umode); */ - { - mode->clock = umode->clock; - mode->hdisplay = umode->hdisplay; - mode->hsync_start = umode->hsync_start; - mode->hsync_end = umode->hsync_end; - mode->htotal = umode->htotal; - mode->hskew = umode->hskew; - mode->vdisplay = umode->vdisplay; - mode->vsync_start = umode->vsync_start; - mode->vsync_end = umode->vsync_end; - mode->vtotal = umode->vtotal; - mode->vscan = umode->vscan; - mode->vrefresh = umode->vrefresh; - mode->flags = umode->flags; - mode->type = umode->type; - strncpy(mode->name, umode->name, DRM_DISPLAY_MODE_LEN); - mode->name[DRM_DISPLAY_MODE_LEN-1] = 0; - } - - connector_funcs = (struct drm_connector_helper_funcs *) - connector->helper_private; - - if (connector_funcs->mode_valid) { - resp = connector_funcs->mode_valid(connector, mode); - arg->data = resp; - } - - /*do some clean up work*/ - if (mode) - drm_mode_destroy(dev, mode); -mode_op_out: - mutex_unlock(&dev->mode_config.mutex); - return ret; - - default: - dev_dbg(dev->dev, "Unsupported psb mode operation\n"); - return -EOPNOTSUPP; - } - - return 0; -} - -static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = psb_priv(dev); - struct drm_psb_stolen_memory_arg *arg = data; - - arg->base = dev_priv->stolen_base; - arg->size = dev_priv->vram_stolen_size; - - return 0; -} - -static int psb_driver_open(struct drm_device *dev, struct drm_file *priv) -{ - return 0; -} - -static void psb_driver_close(struct drm_device *dev, struct drm_file *priv) -{ -} - static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -596,15 +422,21 @@ static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd, /* FIXME: do we need to wrap the other side of this */ } - -/* When a client dies: +/* + * 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) +{ +} + +static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + return drm_get_pci_dev(pdev, ent, &driver); } -static void psb_remove(struct pci_dev *pdev) + +static void psb_pci_remove(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); drm_put_dev(dev); @@ -613,12 +445,15 @@ static void psb_remove(struct pci_dev *pdev) static const struct dev_pm_ops psb_pm_ops = { .resume = gma_power_resume, .suspend = gma_power_suspend, + .thaw = gma_power_thaw, + .freeze = gma_power_freeze, + .restore = gma_power_restore, .runtime_suspend = psb_runtime_suspend, .runtime_resume = psb_runtime_resume, .runtime_idle = psb_runtime_idle, }; -static struct vm_operations_struct psb_gem_vm_ops = { +static const struct vm_operations_struct psb_gem_vm_ops = { .fault = psb_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, @@ -631,18 +466,18 @@ static const struct file_operations psb_gem_fops = { .unlocked_ioctl = psb_unlocked_ioctl, .mmap = drm_gem_mmap, .poll = drm_poll, - .fasync = drm_fasync, .read = drm_read, }; static struct drm_driver driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | \ - DRIVER_IRQ_VBL | DRIVER_MODESET | DRIVER_GEM , + DRIVER_MODESET | DRIVER_GEM, .load = psb_driver_load, .unload = psb_driver_unload, + .lastclose = psb_driver_lastclose, + .preclose = psb_driver_preclose, - .ioctls = psb_ioctls, - .num_ioctls = DRM_ARRAY_SIZE(psb_ioctls), + .num_ioctls = ARRAY_SIZE(psb_ioctls), .device_is_agp = psb_driver_device_is_agp, .irq_preinstall = psb_irq_preinstall, .irq_postinstall = psb_irq_postinstall, @@ -651,40 +486,31 @@ static struct drm_driver driver = { .enable_vblank = psb_enable_vblank, .disable_vblank = psb_disable_vblank, .get_vblank_counter = psb_get_vblank_counter, - .lastclose = psb_lastclose, - .open = psb_driver_open, - .preclose = psb_driver_preclose, - .postclose = psb_driver_close, - .reclaim_buffers = drm_core_reclaim_buffers, - .gem_init_object = psb_gem_init_object, .gem_free_object = psb_gem_free_object, .gem_vm_ops = &psb_gem_vm_ops, + .dumb_create = psb_gem_dumb_create, .dumb_map_offset = psb_gem_dumb_map_gtt, - .dumb_destroy = psb_gem_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, + .ioctls = psb_ioctls, .fops = &psb_gem_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, - .date = PSB_DRM_DRIVER_DATE, - .major = PSB_DRM_DRIVER_MAJOR, - .minor = PSB_DRM_DRIVER_MINOR, - .patchlevel = PSB_DRM_DRIVER_PATCHLEVEL + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL }; static struct pci_driver psb_pci_driver = { .name = DRIVER_NAME, .id_table = pciidlist, - .probe = psb_probe, - .remove = psb_remove, + .probe = psb_pci_probe, + .remove = psb_pci_remove, .driver.pm = &psb_pm_ops, }; -static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) -{ - return drm_get_pci_dev(pdev, ent, &driver); -} - static int __init psb_init(void) { return drm_pci_init(&driver, &psb_pci_driver); @@ -698,6 +524,6 @@ static void __exit psb_exit(void) late_initcall(psb_init); module_exit(psb_exit); -MODULE_AUTHOR("Alan Cox <alan@linux.intel.com> and others"); +MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); +MODULE_LICENSE(DRIVER_LICENSE); diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index eb1568a0da9..55ebe2bd88d 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -23,14 +23,28 @@ #include <linux/kref.h> #include <drm/drmP.h> -#include "drm_global.h" -#include "gem_glue.h" -#include "gma_drm.h" +#include <drm/drm_global.h> +#include <drm/gma_drm.h> #include "psb_reg.h" #include "psb_intel_drv.h" +#include "gma_display.h" +#include "intel_bios.h" #include "gtt.h" #include "power.h" +#include "opregion.h" #include "oaktrail.h" +#include "mmu.h" + +#define DRIVER_AUTHOR "Alan Cox <alan@linux.intel.com> and others" +#define DRIVER_LICENSE "GPL" + +#define DRIVER_NAME "gma500" +#define DRIVER_DESC "DRM driver for the Intel GMA500, GMA600, GMA3600, GMA3650" +#define DRIVER_DATE "20140314" + +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 /* Append new drm mode definition here, align with libdrm definition */ #define DRM_MODE_SCALE_NO_SCALE 2 @@ -42,25 +56,12 @@ enum { CHIP_MFLD_0130 = 3, /* Medfield */ }; -#define IS_PSB(dev) (((dev)->pci_device & 0xfffe) == 0x8108) -#define IS_MRST(dev) (((dev)->pci_device & 0xfffc) == 0x4100) -#define IS_MFLD(dev) (((dev)->pci_device & 0xfff8) == 0x0130) - -/* - * Driver definitions - */ - -#define DRIVER_NAME "gma500" -#define DRIVER_DESC "DRM driver for the Intel GMA500" - -#define PSB_DRM_DRIVER_DATE "2011-06-06" -#define PSB_DRM_DRIVER_MAJOR 1 -#define PSB_DRM_DRIVER_MINOR 0 -#define PSB_DRM_DRIVER_PATCHLEVEL 0 +#define IS_PSB(dev) (((dev)->pdev->device & 0xfffe) == 0x8108) +#define IS_MRST(dev) (((dev)->pdev->device & 0xfff0) == 0x4100) +#define IS_MFLD(dev) (((dev)->pdev->device & 0xfff8) == 0x0130) +#define IS_CDV(dev) (((dev)->pdev->device & 0xfff0) == 0x0be0) -/* - * Hardware offsets - */ +/* Hardware offsets */ #define PSB_VDC_OFFSET 0x00000000 #define PSB_VDC_SIZE 0x000080000 #define MRST_MMIO_SIZE 0x0000C0000 @@ -68,15 +69,14 @@ enum { #define PSB_SGX_SIZE 0x8000 #define PSB_SGX_OFFSET 0x00040000 #define MRST_SGX_OFFSET 0x00080000 -/* - * PCI resource identifiers - */ + +/* PCI resource identifiers */ #define PSB_MMIO_RESOURCE 0 +#define PSB_AUX_RESOURCE 0 #define PSB_GATT_RESOURCE 2 #define PSB_GTT_RESOURCE 3 -/* - * PCI configuration - */ + +/* PCI configuration */ #define PSB_GMCH_CTRL 0x52 #define PSB_BSM 0x5C #define _PSB_GMCH_ENABLED 0x4 @@ -84,42 +84,35 @@ enum { #define _PSB_PGETBL_ENABLED 0x00000001 #define PSB_SGX_2D_SLAVE_PORT 0x4000 -/* To get rid of */ +/* TODO: To get rid of */ #define PSB_TT_PRIV0_LIMIT (256*1024*1024) #define PSB_TT_PRIV0_PLIMIT (PSB_TT_PRIV0_LIMIT >> PAGE_SHIFT) -/* - * SGX side MMU definitions (these can probably go) - */ +/* SGX side MMU definitions (these can probably go) */ -/* - * Flags for external memory type field. - */ +/* Flags for external memory type field */ #define PSB_MMU_CACHED_MEMORY 0x0001 /* Bind to MMU only */ #define PSB_MMU_RO_MEMORY 0x0002 /* MMU RO memory */ #define PSB_MMU_WO_MEMORY 0x0004 /* MMU WO memory */ -/* - * PTE's and PDE's - */ + +/* PTE's and PDE's */ #define PSB_PDE_MASK 0x003FFFFF #define PSB_PDE_SHIFT 22 #define PSB_PTE_SHIFT 12 -/* - * Cache control - */ + +/* Cache control */ #define PSB_PTE_VALID 0x0001 /* PTE / PDE valid */ #define PSB_PTE_WO 0x0002 /* Write only */ #define PSB_PTE_RO 0x0004 /* Read only */ #define PSB_PTE_CACHED 0x0008 /* CPU cache coherent */ -/* - * VDC registers and bits - */ +/* VDC registers and bits */ #define PSB_MSVDX_CLOCKGATING 0x2064 #define PSB_TOPAZ_CLOCKGATING 0x2068 #define PSB_HWSTAM 0x2098 #define PSB_INSTPM 0x20C0 #define PSB_INT_IDENTITY_R 0x20A4 +#define _PSB_IRQ_ASLE (1<<0) #define _MDFLD_PIPEC_EVENT_FLAG (1<<2) #define _MDFLD_PIPEC_VBLANK_FLAG (1<<3) #define _PSB_DPST_PIPEB_FLAG (1<<4) @@ -130,6 +123,7 @@ enum { #define _PSB_VSYNC_PIPEA_FLAG (1<<7) #define _MDFLD_MIPIA_FLAG (1<<16) #define _MDFLD_MIPIC_FLAG (1<<17) +#define _PSB_IRQ_DISP_HOTSYNC (1<<17) #define _PSB_IRQ_SGX_FLAG (1<<18) #define _PSB_IRQ_MSVDX_FLAG (1<<19) #define _LNC_IRQ_TOPAZ_FLAG (1<<20) @@ -206,8 +200,8 @@ enum { #define PSB_HIGH_REG_OFFS 0x0600 #define PSB_NUM_VBLANKS 2 -#define PSB_WATCHDOG_DELAY (DRM_HZ * 2) -#define PSB_LID_DELAY (DRM_HZ / 10) +#define PSB_WATCHDOG_DELAY (HZ * 2) +#define PSB_LID_DELAY (HZ / 10) #define MDFLD_PNW_B0 0x04 #define MDFLD_PNW_C0 0x08 @@ -226,7 +220,7 @@ enum { #define MDFLD_DSR_RR 45 #define MDFLD_DPU_ENABLE (1 << 31) #define MDFLD_DSR_FULLSCREEN (1 << 30) -#define MDFLD_DSR_DELAY (DRM_HZ / MDFLD_DSR_RR) +#define MDFLD_DSR_DELAY (HZ / MDFLD_DSR_RR) #define PSB_PWR_STATE_ON 1 #define PSB_PWR_STATE_OFF 2 @@ -257,7 +251,9 @@ struct psb_intel_opregion { struct opregion_acpi *acpi; struct opregion_swsci *swsci; struct opregion_asle *asle; - int enabled; + void *vbt; + u32 __iomem *lid_state; + struct work_struct asle_work; }; struct sdvo_device_mapping { @@ -276,22 +272,189 @@ struct intel_gmbus { u32 reg0; }; +/* Register offset maps */ +struct psb_offset { + u32 fp0; + u32 fp1; + u32 cntr; + u32 conf; + u32 src; + u32 dpll; + u32 dpll_md; + u32 htotal; + u32 hblank; + u32 hsync; + u32 vtotal; + u32 vblank; + u32 vsync; + u32 stride; + u32 size; + u32 pos; + u32 surf; + u32 addr; + u32 base; + u32 status; + u32 linoff; + u32 tileoff; + u32 palette; +}; + +/* + * 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. + */ + +/* Common status for pipes */ +struct psb_pipe { + u32 fp0; + u32 fp1; + u32 cntr; + u32 conf; + u32 src; + u32 dpll; + u32 dpll_md; + u32 htotal; + u32 hblank; + u32 hsync; + u32 vtotal; + u32 vblank; + u32 vsync; + u32 stride; + u32 size; + u32 pos; + u32 base; + u32 surf; + u32 addr; + u32 status; + u32 linoff; + u32 tileoff; + u32 palette[256]; +}; + +struct psb_state { + 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 saveCLOCKGATING; + uint32_t saveDSPARB; + 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 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 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 saveMIPI; + 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 { + struct psb_pipe pipe[3]; + 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 struct drm_psb_private { struct drm_device *dev; + struct pci_dev *aux_pdev; /* Currently only used by mrst */ const struct psb_ops *ops; + const struct psb_offset *regmap; + + struct child_device_config *child_dev; + int child_dev_num; struct psb_gtt gtt; /* GTT Memory manager */ struct psb_gtt_mm *gtt_mm; struct page *scratch_page; - u32 *gtt_map; + u32 __iomem *gtt_map; uint32_t stolen_base; - void *vram_addr; + u8 __iomem *vram_addr; unsigned long vram_stolen_size; int gtt_initialized; u16 gmch_ctrl; /* Saved GTT setup */ @@ -303,49 +466,35 @@ struct drm_psb_private { struct psb_mmu_driver *mmu; struct psb_mmu_pd *pf_pd; - /* - * Register base - */ - - uint8_t *sgx_reg; - uint8_t *vdc_reg; + /* Register base */ + uint8_t __iomem *sgx_reg; + uint8_t __iomem *vdc_reg; + uint8_t __iomem *aux_reg; /* Auxillary vdc pipe regs */ uint32_t gatt_free_offset; - /* - * Fencing / irq. - */ - + /* Fencing / irq */ uint32_t vdc_irq_mask; uint32_t pipestat[PSB_NUM_PIPE]; spinlock_t irqmask_lock; - /* - * Power - */ - + /* Power */ bool suspended; bool display_power; int display_count; - /* - * Modesetting - */ + /* Modesetting */ struct psb_intel_mode_device mode_dev; + bool modeset; /* true if we have done the mode_device setup */ struct drm_crtc *plane_to_crtc_mapping[PSB_NUM_PIPE]; struct drm_crtc *pipe_to_crtc_mapping[PSB_NUM_PIPE]; uint32_t num_pipe; - /* - * OSPM info (Power management base) (can go ?) - */ + /* OSPM info (Power management base) (TODO: can go ?) */ uint32_t ospm_base; - /* - * Sizes info - */ - + /* Sizes info */ u32 fuse_reg_value; u32 video_device_fuse; @@ -354,6 +503,7 @@ struct drm_psb_private { /* gmbus */ struct intel_gmbus *gmbus; + uint8_t __iomem *gmbus_reg; /* Used by SDVO */ int crt_ddc_pin; @@ -364,9 +514,7 @@ struct drm_psb_private { struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_property; - /* - * LVDS info - */ + /* LVDS info */ int backlight_duty_cycle; /* restore backlight to this value */ bool panel_wants_dither; struct drm_display_mode *panel_fixed_mode; @@ -394,231 +542,29 @@ struct drm_psb_private { int rpm_enabled; /* MID specific */ - struct oaktrail_vbt vbt_data; + bool has_gct; 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; + + /* Register state */ + 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; + /* Hotplug handling */ + struct work_struct hotplug_work; - /* 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 - */ + /* LID-Switch */ spinlock_t lid_lock; struct timer_list lid_timer; struct psb_intel_opregion opregion; - u32 *lid_state; u32 lid_last_state; - /* - * Watchdog - */ - + /* Watchdog */ uint32_t apm_reg; uint16_t apm_base; @@ -628,6 +574,8 @@ struct drm_psb_private { */ struct backlight_device *backlight_device; struct drm_property *backlight_property; + bool backlight_enabled; + int backlight_level; uint32_t blc_adj1; uint32_t blc_adj2; @@ -635,36 +583,75 @@ 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; + + bool dplla_96mhz; /* DPLL data from the VBT */ + + struct { + int rate; + int lanes; + int preemphasis; + int vswing; + + bool initialized; + bool support; + int bpp; + struct edp_power_seq pps; + } edp; + uint8_t panel_type; }; -/* - * Operations for each board type - */ - +/* Operations for each board type */ struct psb_ops { const char *name; unsigned int accel_2d:1; int pipes; /* Number of output pipes */ int crtcs; /* Number of CRTCs */ int sgx_offset; /* Base offset of SGX device */ + int hdmi_mask; /* Mask of HDMI CRTCs */ + int lvds_mask; /* Mask of LVDS CRTCs */ + int sdvo_mask; /* Mask of SDVO CRTCs */ + int cursor_needs_phys; /* If cursor base reg need physical address */ /* Sub functions */ struct drm_crtc_helper_funcs const *crtc_helper; struct drm_crtc_funcs const *crtc_funcs; + const struct gma_clock_funcs *clock_funcs; /* Setup hooks */ int (*chip_setup)(struct drm_device *dev); void (*chip_teardown)(struct drm_device *dev); + /* Optional helper caller after modeset */ + void (*errata)(struct drm_device *dev); /* Display management hooks */ int (*output_init)(struct drm_device *dev); + int (*hotplug)(struct drm_device *dev); + void (*hotplug_enable)(struct drm_device *dev, bool on); /* Power management hooks */ void (*init_pm)(struct drm_device *dev); int (*save_regs)(struct drm_device *dev); int (*restore_regs)(struct drm_device *dev); int (*power_up)(struct drm_device *dev); int (*power_down)(struct drm_device *dev); + void (*update_wm)(struct drm_device *dev, struct drm_crtc *crtc); + void (*disable_sr)(struct drm_device *dev); void (*lvds_bl_power)(struct drm_device *dev, bool on); #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE @@ -676,8 +663,6 @@ struct psb_ops { -struct psb_mmu_driver; - extern int drm_crtc_probe_output_modes(struct drm_device *dev, int, int); extern int drm_pick_crtcs(struct drm_device *dev); @@ -686,53 +671,8 @@ static inline struct drm_psb_private *psb_priv(struct drm_device *dev) return (struct drm_psb_private *) dev->dev_private; } -/* - * MMU stuff. - */ - -extern struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers, - int trap_pagefaults, - int invalid_type, - struct drm_psb_private *dev_priv); -extern void psb_mmu_driver_takedown(struct psb_mmu_driver *driver); -extern struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver - *driver); -extern void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd, uint32_t mmu_offset, - uint32_t gtt_start, uint32_t gtt_pages); -extern struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver, - int trap_pagefaults, - int invalid_type); -extern void psb_mmu_free_pagedir(struct psb_mmu_pd *pd); -extern void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot); -extern void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd, - unsigned long address, - uint32_t num_pages); -extern int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, - uint32_t start_pfn, - unsigned long address, - uint32_t num_pages, int type); -extern int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual, - unsigned long *pfn); - -/* - * Enable / disable MMU for different requestors. - */ - - -extern void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context); -extern int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages, - unsigned long address, uint32_t num_pages, - uint32_t desired_tile_stride, - uint32_t hw_tile_stride, int type); -extern void psb_mmu_remove_pages(struct psb_mmu_pd *pd, - unsigned long address, uint32_t num_pages, - uint32_t desired_tile_stride, - uint32_t hw_tile_stride); -/* - *psb_irq.c - */ - -extern irqreturn_t psb_irq_handler(DRM_IRQ_ARGS); +/* psb_irq.c */ +extern irqreturn_t psb_irq_handler(int irq, void *arg); extern int psb_irq_enable_dpst(struct drm_device *dev); extern int psb_irq_disable_dpst(struct drm_device *dev); extern void psb_irq_preinstall(struct drm_device *dev); @@ -754,30 +694,17 @@ psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask); extern u32 psb_get_vblank_counter(struct drm_device *dev, int crtc); -/* - * intel_opregion.c - */ -extern int gma_intel_opregion_init(struct drm_device *dev); -extern int gma_intel_opregion_exit(struct drm_device *dev); - -/* - * framebuffer.c - */ +/* framebuffer.c */ extern int psbfb_probed(struct drm_device *dev); extern int psbfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); -/* - * accel_2d.c - */ +/* accel_2d.c */ extern void psbfb_copyarea(struct fb_info *info, const struct fb_copyarea *region); extern int psbfb_sync(struct fb_info *info); extern void psb_spank(struct drm_psb_private *dev_priv); -/* - * psb_reset.c - */ - +/* psb_reset.c */ extern void psb_lid_timer_init(struct drm_psb_private *dev_priv); extern void psb_lid_timer_takedown(struct drm_psb_private *dev_priv); extern void psb_print_pagefault(struct drm_psb_private *dev_priv); @@ -790,6 +717,9 @@ extern int psb_fbdev_init(struct drm_device *dev); /* backlight.c */ int gma_backlight_init(struct drm_device *dev); void gma_backlight_exit(struct drm_device *dev); +void gma_backlight_disable(struct drm_device *dev); +void gma_backlight_enable(struct drm_device *dev); +void gma_backlight_set(struct drm_device *dev, int v); /* oaktrail_crtc.c */ extern const struct drm_crtc_helper_funcs oaktrail_helper_funcs; @@ -808,14 +738,11 @@ extern const struct drm_connector_helper_funcs extern const struct drm_connector_funcs psb_intel_lvds_connector_funcs; /* gem.c */ -extern int psb_gem_init_object(struct drm_gem_object *obj); extern void psb_gem_free_object(struct drm_gem_object *obj); extern int psb_gem_get_aperture(struct drm_device *dev, void *data, struct drm_file *file); extern int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args); -extern int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, - uint32_t handle); extern int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, uint32_t handle, uint64_t *offset); extern int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); @@ -830,12 +757,13 @@ 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; -/* - * Debug print bits setting - */ +/* Debug print bits setting */ #define PSB_D_GENERAL (1 << 0) #define PSB_D_INIT (1 << 1) #define PSB_D_IRQ (1 << 2) @@ -849,13 +777,9 @@ extern const struct psb_ops cdv_chip_ops; #define PSB_D_MSVDX (1 << 9) #define PSB_D_TOPAZ (1 << 10) -extern int drm_psb_no_fb; extern int drm_idle_check_interval; -/* - * Utilities - */ - +/* Utilities */ static inline u32 MRST_MSG_READ32(uint port, uint offset) { int mcr = (0xD0<<24) | (port << 16) | (offset << 8); @@ -899,16 +823,58 @@ static inline uint32_t REGISTER_READ(struct drm_device *dev, uint32_t reg) return ioread32(dev_priv->vdc_reg + reg); } +static inline uint32_t REGISTER_READ_AUX(struct drm_device *dev, uint32_t reg) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + return ioread32(dev_priv->aux_reg + reg); +} + #define REG_READ(reg) REGISTER_READ(dev, (reg)) +#define REG_READ_AUX(reg) REGISTER_READ_AUX(dev, (reg)) + +/* Useful for post reads */ +static inline uint32_t REGISTER_READ_WITH_AUX(struct drm_device *dev, + uint32_t reg, int aux) +{ + uint32_t val; + + if (aux) + val = REG_READ_AUX(reg); + else + val = REG_READ(reg); + + return val; +} + +#define REG_READ_WITH_AUX(reg, aux) REGISTER_READ_WITH_AUX(dev, (reg), (aux)) static inline void REGISTER_WRITE(struct drm_device *dev, uint32_t reg, - uint32_t val) + uint32_t val) { struct drm_psb_private *dev_priv = dev->dev_private; iowrite32((val), dev_priv->vdc_reg + (reg)); } +static inline void REGISTER_WRITE_AUX(struct drm_device *dev, uint32_t reg, + uint32_t val) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + iowrite32((val), dev_priv->aux_reg + (reg)); +} + #define REG_WRITE(reg, val) REGISTER_WRITE(dev, (reg), (val)) +#define REG_WRITE_AUX(reg, val) REGISTER_WRITE_AUX(dev, (reg), (val)) + +static inline void REGISTER_WRITE_WITH_AUX(struct drm_device *dev, uint32_t reg, + uint32_t val, int aux) +{ + if (aux) + REG_WRITE_AUX(reg, val); + else + REG_WRITE(reg, val); +} + +#define REG_WRITE_WITH_AUX(reg, val, aux) REGISTER_WRITE_WITH_AUX(dev, (reg), (val), (aux)) static inline void REGISTER_WRITE16(struct drm_device *dev, uint32_t reg, uint32_t val) diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index 49e983508d5..87b50ba64ed 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c @@ -19,177 +19,61 @@ */ #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <drm/drmP.h> #include "framebuffer.h" #include "psb_drv.h" #include "psb_intel_drv.h" #include "psb_intel_reg.h" -#include "psb_intel_display.h" +#include "gma_display.h" #include "power.h" -struct psb_intel_clock_t { - /* given values */ - int n; - int m1, m2; - int p1, p2; - /* derived values */ - int dot; - int vco; - int m; - int p; -}; - -struct psb_intel_range_t { - int min, max; -}; - -struct psb_intel_p2_t { - int dot_limit; - int p2_slow, p2_fast; -}; +#define INTEL_LIMIT_I9XX_SDVO_DAC 0 +#define INTEL_LIMIT_I9XX_LVDS 1 -#define INTEL_P2_NUM 2 - -struct psb_intel_limit_t { - struct psb_intel_range_t dot, vco, n, m, m1, m2, p, p1; - struct psb_intel_p2_t p2; -}; - -#define I8XX_DOT_MIN 25000 -#define I8XX_DOT_MAX 350000 -#define I8XX_VCO_MIN 930000 -#define I8XX_VCO_MAX 1400000 -#define I8XX_N_MIN 3 -#define I8XX_N_MAX 16 -#define I8XX_M_MIN 96 -#define I8XX_M_MAX 140 -#define I8XX_M1_MIN 18 -#define I8XX_M1_MAX 26 -#define I8XX_M2_MIN 6 -#define I8XX_M2_MAX 16 -#define I8XX_P_MIN 4 -#define I8XX_P_MAX 128 -#define I8XX_P1_MIN 2 -#define I8XX_P1_MAX 33 -#define I8XX_P1_LVDS_MIN 1 -#define I8XX_P1_LVDS_MAX 6 -#define I8XX_P2_SLOW 4 -#define I8XX_P2_FAST 2 -#define I8XX_P2_LVDS_SLOW 14 -#define I8XX_P2_LVDS_FAST 14 /* No fast option */ -#define I8XX_P2_SLOW_LIMIT 165000 - -#define I9XX_DOT_MIN 20000 -#define I9XX_DOT_MAX 400000 -#define I9XX_VCO_MIN 1400000 -#define I9XX_VCO_MAX 2800000 -#define I9XX_N_MIN 3 -#define I9XX_N_MAX 8 -#define I9XX_M_MIN 70 -#define I9XX_M_MAX 120 -#define I9XX_M1_MIN 10 -#define I9XX_M1_MAX 20 -#define I9XX_M2_MIN 5 -#define I9XX_M2_MAX 9 -#define I9XX_P_SDVO_DAC_MIN 5 -#define I9XX_P_SDVO_DAC_MAX 80 -#define I9XX_P_LVDS_MIN 7 -#define I9XX_P_LVDS_MAX 98 -#define I9XX_P1_MIN 1 -#define I9XX_P1_MAX 8 -#define I9XX_P2_SDVO_DAC_SLOW 10 -#define I9XX_P2_SDVO_DAC_FAST 5 -#define I9XX_P2_SDVO_DAC_SLOW_LIMIT 200000 -#define I9XX_P2_LVDS_SLOW 14 -#define I9XX_P2_LVDS_FAST 7 -#define I9XX_P2_LVDS_SLOW_LIMIT 112000 - -#define INTEL_LIMIT_I8XX_DVO_DAC 0 -#define INTEL_LIMIT_I8XX_LVDS 1 -#define INTEL_LIMIT_I9XX_SDVO_DAC 2 -#define INTEL_LIMIT_I9XX_LVDS 3 - -static const struct psb_intel_limit_t psb_intel_limits[] = { - { /* INTEL_LIMIT_I8XX_DVO_DAC */ - .dot = {.min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX}, - .vco = {.min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX}, - .n = {.min = I8XX_N_MIN, .max = I8XX_N_MAX}, - .m = {.min = I8XX_M_MIN, .max = I8XX_M_MAX}, - .m1 = {.min = I8XX_M1_MIN, .max = I8XX_M1_MAX}, - .m2 = {.min = I8XX_M2_MIN, .max = I8XX_M2_MAX}, - .p = {.min = I8XX_P_MIN, .max = I8XX_P_MAX}, - .p1 = {.min = I8XX_P1_MIN, .max = I8XX_P1_MAX}, - .p2 = {.dot_limit = I8XX_P2_SLOW_LIMIT, - .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST}, - }, - { /* INTEL_LIMIT_I8XX_LVDS */ - .dot = {.min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX}, - .vco = {.min = I8XX_VCO_MIN, .max = I8XX_VCO_MAX}, - .n = {.min = I8XX_N_MIN, .max = I8XX_N_MAX}, - .m = {.min = I8XX_M_MIN, .max = I8XX_M_MAX}, - .m1 = {.min = I8XX_M1_MIN, .max = I8XX_M1_MAX}, - .m2 = {.min = I8XX_M2_MIN, .max = I8XX_M2_MAX}, - .p = {.min = I8XX_P_MIN, .max = I8XX_P_MAX}, - .p1 = {.min = I8XX_P1_LVDS_MIN, .max = I8XX_P1_LVDS_MAX}, - .p2 = {.dot_limit = I8XX_P2_SLOW_LIMIT, - .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST}, - }, +static const struct gma_limit_t psb_intel_limits[] = { { /* INTEL_LIMIT_I9XX_SDVO_DAC */ - .dot = {.min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX}, - .vco = {.min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX}, - .n = {.min = I9XX_N_MIN, .max = I9XX_N_MAX}, - .m = {.min = I9XX_M_MIN, .max = I9XX_M_MAX}, - .m1 = {.min = I9XX_M1_MIN, .max = I9XX_M1_MAX}, - .m2 = {.min = I9XX_M2_MIN, .max = I9XX_M2_MAX}, - .p = {.min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX}, - .p1 = {.min = I9XX_P1_MIN, .max = I9XX_P1_MAX}, - .p2 = {.dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT, - .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = - I9XX_P2_SDVO_DAC_FAST}, + .dot = {.min = 20000, .max = 400000}, + .vco = {.min = 1400000, .max = 2800000}, + .n = {.min = 1, .max = 6}, + .m = {.min = 70, .max = 120}, + .m1 = {.min = 8, .max = 18}, + .m2 = {.min = 3, .max = 7}, + .p = {.min = 5, .max = 80}, + .p1 = {.min = 1, .max = 8}, + .p2 = {.dot_limit = 200000, .p2_slow = 10, .p2_fast = 5}, + .find_pll = gma_find_best_pll, }, { /* INTEL_LIMIT_I9XX_LVDS */ - .dot = {.min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX}, - .vco = {.min = I9XX_VCO_MIN, .max = I9XX_VCO_MAX}, - .n = {.min = I9XX_N_MIN, .max = I9XX_N_MAX}, - .m = {.min = I9XX_M_MIN, .max = I9XX_M_MAX}, - .m1 = {.min = I9XX_M1_MIN, .max = I9XX_M1_MAX}, - .m2 = {.min = I9XX_M2_MIN, .max = I9XX_M2_MAX}, - .p = {.min = I9XX_P_LVDS_MIN, .max = I9XX_P_LVDS_MAX}, - .p1 = {.min = I9XX_P1_MIN, .max = I9XX_P1_MAX}, + .dot = {.min = 20000, .max = 400000}, + .vco = {.min = 1400000, .max = 2800000}, + .n = {.min = 1, .max = 6}, + .m = {.min = 70, .max = 120}, + .m1 = {.min = 8, .max = 18}, + .m2 = {.min = 3, .max = 7}, + .p = {.min = 7, .max = 98}, + .p1 = {.min = 1, .max = 8}, /* The single-channel range is 25-112Mhz, and dual-channel * is 80-224Mhz. Prefer single channel as much as possible. */ - .p2 = {.dot_limit = I9XX_P2_LVDS_SLOW_LIMIT, - .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST}, + .p2 = {.dot_limit = 112000, .p2_slow = 14, .p2_fast = 7}, + .find_pll = gma_find_best_pll, }, }; -static const struct psb_intel_limit_t *psb_intel_limit(struct drm_crtc *crtc) +static const struct gma_limit_t *psb_intel_limit(struct drm_crtc *crtc, + int refclk) { - const struct psb_intel_limit_t *limit; + const struct gma_limit_t *limit; - if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) + if (gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) limit = &psb_intel_limits[INTEL_LIMIT_I9XX_LVDS]; else limit = &psb_intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; return limit; } -/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */ - -static void i8xx_clock(int refclk, struct psb_intel_clock_t *clock) -{ - clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); - clock->p = clock->p1 * clock->p2; - clock->vco = refclk * clock->m / (clock->n + 2); - clock->dot = clock->vco / clock->p; -} - -/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */ - -static void i9xx_clock(int refclk, struct psb_intel_clock_t *clock) +static void psb_intel_clock(int refclk, struct gma_clock_t *clock) { clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); clock->p = clock->p1 * clock->p2; @@ -197,377 +81,6 @@ static void i9xx_clock(int refclk, struct psb_intel_clock_t *clock) clock->dot = clock->vco / clock->p; } -static void psb_intel_clock(struct drm_device *dev, int refclk, - struct psb_intel_clock_t *clock) -{ - return i9xx_clock(refclk, clock); -} - -/** - * Returns whether any output on the specified pipe is of the specified type - */ -bool psb_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; - struct drm_connector *l_entry; - - list_for_each_entry(l_entry, &mode_config->connector_list, head) { - if (l_entry->encoder && l_entry->encoder->crtc == crtc) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(l_entry); - if (psb_intel_encoder->type == type) - return true; - } - } - return false; -} - -#define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } -/** - * Returns whether the given set of divisors are valid for a given refclk with - * the given connectors. - */ - -static bool psb_intel_PLL_is_valid(struct drm_crtc *crtc, - struct psb_intel_clock_t *clock) -{ - const struct psb_intel_limit_t *limit = psb_intel_limit(crtc); - - if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) - INTELPllInvalid("p1 out of range\n"); - if (clock->p < limit->p.min || limit->p.max < clock->p) - INTELPllInvalid("p out of range\n"); - if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) - INTELPllInvalid("m2 out of range\n"); - if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) - INTELPllInvalid("m1 out of range\n"); - if (clock->m1 <= clock->m2) - INTELPllInvalid("m1 <= m2\n"); - if (clock->m < limit->m.min || limit->m.max < clock->m) - INTELPllInvalid("m out of range\n"); - if (clock->n < limit->n.min || limit->n.max < clock->n) - INTELPllInvalid("n out of range\n"); - if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) - INTELPllInvalid("vco out of range\n"); - /* XXX: We may need to be checking "Dot clock" - * depending on the multiplier, connector, etc., - * rather than just a single range. - */ - if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) - INTELPllInvalid("dot out of range\n"); - - return true; -} - -/** - * Returns a set of divisors for the desired target clock with the given - * refclk, or FALSE. The returned values represent the clock equation: - * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. - */ -static bool psb_intel_find_best_PLL(struct drm_crtc *crtc, int target, - int refclk, - struct psb_intel_clock_t *best_clock) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_clock_t clock; - const struct psb_intel_limit_t *limit = psb_intel_limit(crtc); - int err = target; - - if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && - (REG_READ(LVDS) & LVDS_PORT_EN) != 0) { - /* - * For LVDS, if the panel is on, just rely on its current - * settings for dual-channel. We haven't figured out how to - * reliably set up different single/dual channel state, if we - * even can. - */ - if ((REG_READ(LVDS) & LVDS_CLKB_POWER_MASK) == - LVDS_CLKB_POWER_UP) - clock.p2 = limit->p2.p2_fast; - else - clock.p2 = limit->p2.p2_slow; - } else { - if (target < limit->p2.dot_limit) - clock.p2 = limit->p2.p2_slow; - else - clock.p2 = limit->p2.p2_fast; - } - - memset(best_clock, 0, sizeof(*best_clock)); - - for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; - clock.m1++) { - for (clock.m2 = limit->m2.min; - clock.m2 < clock.m1 && clock.m2 <= limit->m2.max; - clock.m2++) { - for (clock.n = limit->n.min; - clock.n <= limit->n.max; clock.n++) { - for (clock.p1 = limit->p1.min; - clock.p1 <= limit->p1.max; - clock.p1++) { - int this_err; - - psb_intel_clock(dev, refclk, &clock); - - if (!psb_intel_PLL_is_valid - (crtc, &clock)) - continue; - - this_err = abs(clock.dot - target); - if (this_err < err) { - *best_clock = clock; - err = this_err; - } - } - } - } - } - - return err != target; -} - -void psb_intel_wait_for_vblank(struct drm_device *dev) -{ - /* Wait for 20ms, i.e. one cycle at 50hz. */ - mdelay(20); -} - -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; - /* 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 dspbase = (pipe == 0 ? DSPABASE : DSPBBASE); - int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF); - int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - u32 dspcntr; - int ret = 0; - - if (!gma_power_begin(dev, true)) - return 0; - - /* no fb bound */ - if (!crtc->fb) { - dev_dbg(dev->dev, "No FB bound\n"); - goto psb_intel_pipe_cleaner; - } - - /* We are displaying this buffer, make sure it is actually loaded - into the GTT */ - ret = psb_gtt_pin(psbfb->gtt); - if (ret < 0) - goto psb_intel_pipe_set_base_exit; - 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; - default: - dev_err(dev->dev, "Unknown color depth\n"); - ret = -EINVAL; - psb_gtt_unpin(psbfb->gtt); - goto psb_intel_pipe_set_base_exit; - } - REG_WRITE(dspcntr_reg, dspcntr); - - - if (0 /* FIXMEAC - check what PSB needs */) { - REG_WRITE(dspbase, offset); - REG_READ(dspbase); - REG_WRITE(dspsurf, start); - REG_READ(dspsurf); - } else { - REG_WRITE(dspbase, start + offset); - REG_READ(dspbase); - } - -psb_intel_pipe_cleaner: - /* If there was a previous display we can now unpin it */ - if (old_fb) - psb_gtt_unpin(to_psb_fb(old_fb)->gtt); - -psb_intel_pipe_set_base_exit: - gma_power_end(dev); - return ret; -} - -/** - * 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 psb_intel_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_i915_master_private *master_priv; */ - /* struct drm_i915_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 = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - 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. - */ - 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) { - REG_WRITE(dpll_reg, temp); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - /* Wait for the clocks to stabilize. */ - udelay(150); - } - - /* Enable the pipe */ - temp = REG_READ(pipeconf_reg); - if ((temp & PIPEACONF_ENABLE) == 0) - REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE); - - /* 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)); - } - - 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 */ - - /* 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) { - REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE); - REG_READ(pipeconf_reg); - } - - /* Wait for vblank for the disable to take effect. */ - psb_intel_wait_for_vblank(dev); - - temp = REG_READ(dpll_reg); - if ((temp & DPLL_VCO_ENABLE) != 0) { - REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); - } - - /* Wait for the clocks to turn off. */ - udelay(150); - break; - } - - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; - - /*Set FIFO Watermarks*/ - REG_WRITE(DSPARB, 0x3F3E); -} - -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); -} - -void psb_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 psb_intel_lvds_prepare */ - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); -} - -void psb_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 psb_intel_lvds_commit */ - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); -} - -void psb_intel_encoder_destroy(struct drm_encoder *encoder) -{ - struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); - - drm_encoder_cleanup(encoder); - kfree(intel_encoder); -} - -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 @@ -592,69 +105,55 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, 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; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - int pipe = psb_intel_crtc->pipe; - int fp_reg = (pipe == 0) ? FPA0 : FPB0; - int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B; - int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR; - int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; - 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 pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; int refclk; - struct psb_intel_clock_t clock; + struct gma_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; + const struct gma_limit_t *limit; /* No scan out no play */ - if (crtc->fb == NULL) { + if (crtc->primary->fb == NULL) { crtc_funcs->mode_set_base(crtc, x, y, old_fb); return 0; } list_for_each_entry(connector, &mode_config->connector_list, head) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); if (!connector->encoder || connector->encoder->crtc != crtc) continue; - switch (psb_intel_encoder->type) { + switch (gma_encoder->type) { 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; - case INTEL_OUTPUT_ANALOG: - is_crt = true; - break; } } refclk = 96000; - ok = psb_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, + limit = gma_crtc->clock_funcs->limit(crtc, refclk); + + ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock); if (!ok) { - dev_err(dev->dev, "Couldn't find PLL settings for mode!\n"); + DRM_ERROR("Couldn't find PLL settings for mode! target: %d, actual: %d", + adjusted_mode->clock, clock.dot); return 0; } @@ -699,7 +198,7 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, dpll |= PLL_REF_INPUT_DREFCLK; /* setup pipeconf */ - pipeconf = REG_READ(pipeconf_reg); + pipeconf = REG_READ(map->conf); /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; @@ -721,9 +220,9 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, drm_mode_debug_printmodeline(mode); if (dpll & DPLL_VCO_ENABLE) { - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE); - REG_READ(dpll_reg); + REG_WRITE(map->fp0, fp); + REG_WRITE(map->dpll, dpll & ~DPLL_VCO_ENABLE); + REG_READ(map->dpll); udelay(150); } @@ -756,403 +255,86 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, REG_READ(LVDS); } - REG_WRITE(fp_reg, fp); - REG_WRITE(dpll_reg, dpll); - REG_READ(dpll_reg); + REG_WRITE(map->fp0, fp); + REG_WRITE(map->dpll, dpll); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); /* write it again -- the BIOS does, after all */ - REG_WRITE(dpll_reg, dpll); + REG_WRITE(map->dpll, dpll); - REG_READ(dpll_reg); + REG_READ(map->dpll); /* Wait for the clocks to stabilize. */ udelay(150); - REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) | + REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16)); - REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | + REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16)); - REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | + REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16)); - REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | + REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16)); - REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | + REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16)); - REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | + REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16)); /* pipesrc and dspsize control the size that is scaled from, * which should always be the user's requested size. */ - REG_WRITE(dspsize_reg, + REG_WRITE(map->size, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); - REG_WRITE(dsppos_reg, 0); - REG_WRITE(pipesrc_reg, + REG_WRITE(map->pos, 0); + REG_WRITE(map->src, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); - REG_WRITE(pipeconf_reg, pipeconf); - REG_READ(pipeconf_reg); + REG_WRITE(map->conf, pipeconf); + REG_READ(map->conf); - psb_intel_wait_for_vblank(dev); + gma_wait_for_vblank(dev); - REG_WRITE(dspcntr_reg, dspcntr); + REG_WRITE(map->cntr, dspcntr); /* Flush the plane changes */ crtc_funcs->mode_set_base(crtc, x, y, old_fb); - psb_intel_wait_for_vblank(dev); - - return 0; -} - -/** Loads the palette/gamma unit for the CRTC with the prepared values */ -void psb_intel_crtc_load_lut(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int palreg = PALETTE_A; - int i; - - /* The clocks have to be on to load the palette. */ - if (!crtc->enabled) - return; - - switch (psb_intel_crtc->pipe) { - case 0: - break; - case 1: - palreg = PALETTE_B; - break; - case 2: - palreg = PALETTE_C; - break; - default: - dev_err(dev->dev, "Illegal Pipe Number.\n"); - return; - } - - if (gma_power_begin(dev, false)) { - for (i = 0; i < 256; i++) { - REG_WRITE(palreg + 4 * i, - ((psb_intel_crtc->lut_r[i] + - psb_intel_crtc->lut_adj[i]) << 16) | - ((psb_intel_crtc->lut_g[i] + - psb_intel_crtc->lut_adj[i]) << 8) | - (psb_intel_crtc->lut_b[i] + - psb_intel_crtc->lut_adj[i])); - } - gma_power_end(dev); - } else { - for (i = 0; i < 256; i++) { - dev_priv->save_palette_a[i] = - ((psb_intel_crtc->lut_r[i] + - psb_intel_crtc->lut_adj[i]) << 16) | - ((psb_intel_crtc->lut_g[i] + - psb_intel_crtc->lut_adj[i]) << 8) | - (psb_intel_crtc->lut_b[i] + - psb_intel_crtc->lut_adj[i]); - } - - } -} - -/** - * Save HW states of giving crtc - */ -static void psb_intel_crtc_save(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_psb_private *dev_priv = - (struct drm_psb_private *)dev->dev_private; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - int pipeA = (psb_intel_crtc->pipe == 0); - uint32_t paletteReg; - int i; - - if (!crtc_state) { - dev_err(dev->dev, "No CRTC state found\n"); - return; - } - - crtc_state->saveDSPCNTR = REG_READ(pipeA ? DSPACNTR : DSPBCNTR); - crtc_state->savePIPECONF = REG_READ(pipeA ? PIPEACONF : PIPEBCONF); - crtc_state->savePIPESRC = REG_READ(pipeA ? PIPEASRC : PIPEBSRC); - crtc_state->saveFP0 = REG_READ(pipeA ? FPA0 : FPB0); - crtc_state->saveFP1 = REG_READ(pipeA ? FPA1 : FPB1); - crtc_state->saveDPLL = REG_READ(pipeA ? DPLL_A : DPLL_B); - crtc_state->saveHTOTAL = REG_READ(pipeA ? HTOTAL_A : HTOTAL_B); - crtc_state->saveHBLANK = REG_READ(pipeA ? HBLANK_A : HBLANK_B); - crtc_state->saveHSYNC = REG_READ(pipeA ? HSYNC_A : HSYNC_B); - crtc_state->saveVTOTAL = REG_READ(pipeA ? VTOTAL_A : VTOTAL_B); - crtc_state->saveVBLANK = REG_READ(pipeA ? VBLANK_A : VBLANK_B); - crtc_state->saveVSYNC = REG_READ(pipeA ? VSYNC_A : VSYNC_B); - crtc_state->saveDSPSTRIDE = REG_READ(pipeA ? DSPASTRIDE : DSPBSTRIDE); - - /*NOTE: DSPSIZE DSPPOS only for psb*/ - crtc_state->saveDSPSIZE = REG_READ(pipeA ? DSPASIZE : DSPBSIZE); - crtc_state->saveDSPPOS = REG_READ(pipeA ? DSPAPOS : DSPBPOS); - - crtc_state->saveDSPBASE = REG_READ(pipeA ? DSPABASE : DSPBBASE); - - paletteReg = pipeA ? PALETTE_A : PALETTE_B; - for (i = 0; i < 256; ++i) - crtc_state->savePalette[i] = REG_READ(paletteReg + (i << 2)); -} - -/** - * Restore HW states of giving crtc - */ -static void psb_intel_crtc_restore(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - /* struct drm_psb_private * dev_priv = - (struct drm_psb_private *)dev->dev_private; */ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_crtc_state *crtc_state = psb_intel_crtc->crtc_state; - /* struct drm_crtc_helper_funcs * crtc_funcs = crtc->helper_private; */ - int pipeA = (psb_intel_crtc->pipe == 0); - uint32_t paletteReg; - int i; - - if (!crtc_state) { - dev_err(dev->dev, "No crtc state\n"); - return; - } - - if (crtc_state->saveDPLL & DPLL_VCO_ENABLE) { - REG_WRITE(pipeA ? DPLL_A : DPLL_B, - crtc_state->saveDPLL & ~DPLL_VCO_ENABLE); - REG_READ(pipeA ? DPLL_A : DPLL_B); - udelay(150); - } - - REG_WRITE(pipeA ? FPA0 : FPB0, crtc_state->saveFP0); - REG_READ(pipeA ? FPA0 : FPB0); - - REG_WRITE(pipeA ? FPA1 : FPB1, crtc_state->saveFP1); - REG_READ(pipeA ? FPA1 : FPB1); - - REG_WRITE(pipeA ? DPLL_A : DPLL_B, crtc_state->saveDPLL); - REG_READ(pipeA ? DPLL_A : DPLL_B); - udelay(150); - - REG_WRITE(pipeA ? HTOTAL_A : HTOTAL_B, crtc_state->saveHTOTAL); - REG_WRITE(pipeA ? HBLANK_A : HBLANK_B, crtc_state->saveHBLANK); - REG_WRITE(pipeA ? HSYNC_A : HSYNC_B, crtc_state->saveHSYNC); - REG_WRITE(pipeA ? VTOTAL_A : VTOTAL_B, crtc_state->saveVTOTAL); - REG_WRITE(pipeA ? VBLANK_A : VBLANK_B, crtc_state->saveVBLANK); - REG_WRITE(pipeA ? VSYNC_A : VSYNC_B, crtc_state->saveVSYNC); - REG_WRITE(pipeA ? DSPASTRIDE : DSPBSTRIDE, crtc_state->saveDSPSTRIDE); - - REG_WRITE(pipeA ? DSPASIZE : DSPBSIZE, crtc_state->saveDSPSIZE); - REG_WRITE(pipeA ? DSPAPOS : DSPBPOS, crtc_state->saveDSPPOS); - - REG_WRITE(pipeA ? PIPEASRC : PIPEBSRC, crtc_state->savePIPESRC); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); - REG_WRITE(pipeA ? PIPEACONF : PIPEBCONF, crtc_state->savePIPECONF); - - psb_intel_wait_for_vblank(dev); - - REG_WRITE(pipeA ? DSPACNTR : DSPBCNTR, crtc_state->saveDSPCNTR); - REG_WRITE(pipeA ? DSPABASE : DSPBBASE, crtc_state->saveDSPBASE); - - psb_intel_wait_for_vblank(dev); - - paletteReg = pipeA ? PALETTE_A : PALETTE_B; - for (i = 0; i < 256; ++i) - REG_WRITE(paletteReg + (i << 2), crtc_state->savePalette[i]); -} - -static int psb_intel_crtc_cursor_set(struct drm_crtc *crtc, - struct drm_file *file_priv, - uint32_t handle, - uint32_t width, uint32_t height) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR; - uint32_t base = (pipe == 0) ? CURABASE : CURBBASE; - uint32_t temp; - size_t addr = 0; - struct gtt_range *gt; - struct drm_gem_object *obj; - int ret; - - /* if we want to turn of the cursor ignore width and height */ - if (!handle) { - /* turn off the cursor */ - temp = CURSOR_MODE_DISABLE; - - if (gma_power_begin(dev, false)) { - REG_WRITE(control, temp); - REG_WRITE(base, 0); - gma_power_end(dev); - } - - /* Unpin the old GEM object */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = NULL; - } - - return 0; - } - - /* Currently we only support 64x64 cursors */ - if (width != 64 || height != 64) { - dev_dbg(dev->dev, "we currently only support 64x64 cursors\n"); - return -EINVAL; - } - - obj = drm_gem_object_lookup(dev, file_priv, handle); - if (!obj) - return -ENOENT; - - if (obj->size < width * height * 4) { - dev_dbg(dev->dev, "buffer is to small\n"); - return -ENOMEM; - } - - gt = container_of(obj, struct gtt_range, gem); + gma_wait_for_vblank(dev); - /* Pin the memory into the GTT */ - ret = psb_gtt_pin(gt); - if (ret) { - dev_err(dev->dev, "Can not pin down handle 0x%x\n", handle); - return ret; - } - - - addr = gt->offset; /* Or resource.start ??? */ - - psb_intel_crtc->cursor_addr = addr; - - temp = 0; - /* set the pipe for the cursor */ - temp |= (pipe << 28); - temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; - - if (gma_power_begin(dev, false)) { - REG_WRITE(control, temp); - REG_WRITE(base, addr); - gma_power_end(dev); - } - - /* unpin the old bo */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = obj; - } - return 0; -} - -static int psb_intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct drm_device *dev = crtc->dev; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; - uint32_t temp = 0; - uint32_t addr; - - - if (x < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT); - x = -x; - } - if (y < 0) { - temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT); - y = -y; - } - - temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT); - temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT); - - addr = psb_intel_crtc->cursor_addr; - - if (gma_power_begin(dev, false)) { - REG_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp); - REG_WRITE((pipe == 0) ? CURABASE : CURBBASE, addr); - gma_power_end(dev); - } return 0; } -void psb_intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, uint32_t type, uint32_t size) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int i; - - if (size != 256) - return; - - for (i = 0; i < 256; i++) { - psb_intel_crtc->lut_r[i] = red[i] >> 8; - psb_intel_crtc->lut_g[i] = green[i] >> 8; - psb_intel_crtc->lut_b[i] = blue[i] >> 8; - } - - psb_intel_crtc_load_lut(crtc); -} - -static int psb_crtc_set_config(struct drm_mode_set *set) -{ - int ret; - struct drm_device *dev = set->crtc->dev; - struct drm_psb_private *dev_priv = dev->dev_private; - - if (!dev_priv->rpm_enabled) - return drm_crtc_helper_set_config(set); - - pm_runtime_forbid(&dev->pdev->dev); - ret = drm_crtc_helper_set_config(set); - pm_runtime_allow(&dev->pdev->dev); - return ret; -} - /* Returns the clock of the currently programmed mode of the given pipe. */ static int psb_intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc) { - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + struct drm_psb_private *dev_priv = dev->dev_private; + int pipe = gma_crtc->pipe; + const struct psb_offset *map = &dev_priv->regmap[pipe]; u32 dpll; u32 fp; - struct psb_intel_clock_t clock; + struct gma_clock_t clock; bool is_lvds; - struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; if (gma_power_begin(dev, false)) { - dpll = REG_READ((pipe == 0) ? DPLL_A : DPLL_B); + dpll = REG_READ(map->dpll); if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = REG_READ((pipe == 0) ? FPA0 : FPB0); + fp = REG_READ(map->fp0); else - fp = REG_READ((pipe == 0) ? FPA1 : FPB1); + fp = REG_READ(map->fp1); is_lvds = (pipe == 1) && (REG_READ(LVDS) & LVDS_PORT_EN); gma_power_end(dev); } else { - dpll = (pipe == 0) ? - dev_priv->saveDPLL_A : dev_priv->saveDPLL_B; + dpll = p->dpll; if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) - fp = (pipe == 0) ? - dev_priv->saveFPA0 : - dev_priv->saveFPB0; + fp = p->fp0; else - fp = (pipe == 0) ? - dev_priv->saveFPA1 : - dev_priv->saveFPB1; + fp = p->fp1; - 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; @@ -1169,9 +351,9 @@ static int psb_intel_crtc_clock_get(struct drm_device *dev, if ((dpll & PLL_REF_INPUT_MASK) == PLLB_REF_INPUT_SPREADSPECTRUMIN) { /* XXX: might not be 66MHz */ - i8xx_clock(66000, &clock); + psb_intel_clock(66000, &clock); } else - i8xx_clock(48000, &clock); + psb_intel_clock(48000, &clock); } else { if (dpll & PLL_P1_DIVIDE_BY_TWO) clock.p1 = 2; @@ -1186,7 +368,7 @@ static int psb_intel_crtc_clock_get(struct drm_device *dev, else clock.p2 = 2; - i8xx_clock(48000, &clock); + psb_intel_clock(48000, &clock); } /* XXX: It would be nice to validate the clocks, but we can't reuse @@ -1201,30 +383,28 @@ static int psb_intel_crtc_clock_get(struct drm_device *dev, struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, struct drm_crtc *crtc) { - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - int pipe = psb_intel_crtc->pipe; + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + int pipe = gma_crtc->pipe; struct drm_display_mode *mode; int htot; int hsync; int vtot; int vsync; struct drm_psb_private *dev_priv = dev->dev_private; + struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; + const struct psb_offset *map = &dev_priv->regmap[pipe]; if (gma_power_begin(dev, false)) { - htot = REG_READ((pipe == 0) ? HTOTAL_A : HTOTAL_B); - hsync = REG_READ((pipe == 0) ? HSYNC_A : HSYNC_B); - vtot = REG_READ((pipe == 0) ? VTOTAL_A : VTOTAL_B); - vsync = REG_READ((pipe == 0) ? VSYNC_A : VSYNC_B); + htot = REG_READ(map->htotal); + hsync = REG_READ(map->hsync); + vtot = REG_READ(map->vtotal); + vsync = REG_READ(map->vsync); gma_power_end(dev); } else { - htot = (pipe == 0) ? - dev_priv->saveHTOTAL_A : dev_priv->saveHTOTAL_B; - hsync = (pipe == 0) ? - dev_priv->saveHSYNC_A : dev_priv->saveHSYNC_B; - vtot = (pipe == 0) ? - dev_priv->saveVTOTAL_A : dev_priv->saveVTOTAL_B; - vsync = (pipe == 0) ? - dev_priv->saveVSYNC_A : dev_priv->saveVSYNC_B; + htot = p->htotal; + hsync = p->hsync; + vtot = p->vtotal; + vsync = p->vsync; } mode = kzalloc(sizeof(*mode), GFP_KERNEL); @@ -1247,147 +427,132 @@ struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, return mode; } -void psb_intel_crtc_destroy(struct drm_crtc *crtc) -{ - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - struct gtt_range *gt; - - /* Unpin the old GEM object */ - if (psb_intel_crtc->cursor_obj) { - gt = container_of(psb_intel_crtc->cursor_obj, - struct gtt_range, gem); - psb_gtt_unpin(gt); - drm_gem_object_unreference(psb_intel_crtc->cursor_obj); - psb_intel_crtc->cursor_obj = NULL; - } - kfree(psb_intel_crtc->crtc_state); - drm_crtc_cleanup(crtc); - kfree(psb_intel_crtc); -} - const struct drm_crtc_helper_funcs psb_intel_helper_funcs = { - .dpms = psb_intel_crtc_dpms, - .mode_fixup = psb_intel_crtc_mode_fixup, + .dpms = gma_crtc_dpms, + .mode_fixup = gma_crtc_mode_fixup, .mode_set = psb_intel_crtc_mode_set, - .mode_set_base = psb_intel_pipe_set_base, - .prepare = psb_intel_crtc_prepare, - .commit = psb_intel_crtc_commit, + .mode_set_base = gma_pipe_set_base, + .prepare = gma_crtc_prepare, + .commit = gma_crtc_commit, + .disable = gma_crtc_disable, }; const struct drm_crtc_funcs psb_intel_crtc_funcs = { - .save = psb_intel_crtc_save, - .restore = psb_intel_crtc_restore, - .cursor_set = psb_intel_crtc_cursor_set, - .cursor_move = psb_intel_crtc_cursor_move, - .gamma_set = psb_intel_crtc_gamma_set, - .set_config = psb_crtc_set_config, - .destroy = psb_intel_crtc_destroy, + .save = gma_crtc_save, + .restore = gma_crtc_restore, + .cursor_set = gma_crtc_cursor_set, + .cursor_move = gma_crtc_cursor_move, + .gamma_set = gma_crtc_gamma_set, + .set_config = gma_crtc_set_config, + .destroy = gma_crtc_destroy, +}; + +const struct gma_clock_funcs psb_clock_funcs = { + .clock = psb_intel_clock, + .limit = psb_intel_limit, + .pll_is_valid = gma_pll_is_valid, }; /* * Set the default value of cursor control and base register * to zero. This is a workaround for h/w defect on Oaktrail */ -static void psb_intel_cursor_init(struct drm_device *dev, int pipe) +static void psb_intel_cursor_init(struct drm_device *dev, + struct gma_crtc *gma_crtc) { + struct drm_psb_private *dev_priv = dev->dev_private; u32 control[3] = { CURACNTR, CURBCNTR, CURCCNTR }; u32 base[3] = { CURABASE, CURBBASE, CURCBASE }; + struct gtt_range *cursor_gt; + + if (dev_priv->ops->cursor_needs_phys) { + /* Allocate 4 pages of stolen mem for a hardware cursor. That + * is enough for the 64 x 64 ARGB cursors we support. + */ + cursor_gt = psb_gtt_alloc_range(dev, 4 * PAGE_SIZE, "cursor", 1, + PAGE_SIZE); + if (!cursor_gt) { + gma_crtc->cursor_gt = NULL; + goto out; + } + gma_crtc->cursor_gt = cursor_gt; + gma_crtc->cursor_addr = dev_priv->stolen_base + + cursor_gt->offset; + } else { + gma_crtc->cursor_gt = NULL; + } - REG_WRITE(control[pipe], 0); - REG_WRITE(base[pipe], 0); +out: + REG_WRITE(control[gma_crtc->pipe], 0); + REG_WRITE(base[gma_crtc->pipe], 0); } void psb_intel_crtc_init(struct drm_device *dev, int pipe, struct psb_intel_mode_device *mode_dev) { struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_intel_crtc *psb_intel_crtc; + struct gma_crtc *gma_crtc; int i; uint16_t *r_base, *g_base, *b_base; /* We allocate a extra array of drm_connector pointers * for fbdev after the crtc */ - psb_intel_crtc = - kzalloc(sizeof(struct psb_intel_crtc) + - (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), - GFP_KERNEL); - if (psb_intel_crtc == NULL) + gma_crtc = kzalloc(sizeof(struct gma_crtc) + + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), + GFP_KERNEL); + if (gma_crtc == NULL) return; - psb_intel_crtc->crtc_state = + gma_crtc->crtc_state = kzalloc(sizeof(struct psb_intel_crtc_state), GFP_KERNEL); - if (!psb_intel_crtc->crtc_state) { + if (!gma_crtc->crtc_state) { dev_err(dev->dev, "Crtc state error: No memory\n"); - kfree(psb_intel_crtc); + kfree(gma_crtc); return; } /* Set the CRTC operations from the chip specific data */ - drm_crtc_init(dev, &psb_intel_crtc->base, dev_priv->ops->crtc_funcs); + drm_crtc_init(dev, &gma_crtc->base, dev_priv->ops->crtc_funcs); + + /* Set the CRTC clock functions from chip specific data */ + gma_crtc->clock_funcs = dev_priv->ops->clock_funcs; - drm_mode_crtc_set_gamma_size(&psb_intel_crtc->base, 256); - psb_intel_crtc->pipe = pipe; - psb_intel_crtc->plane = pipe; + drm_mode_crtc_set_gamma_size(&gma_crtc->base, 256); + gma_crtc->pipe = pipe; + gma_crtc->plane = pipe; - r_base = psb_intel_crtc->base.gamma_store; + r_base = gma_crtc->base.gamma_store; g_base = r_base + 256; b_base = g_base + 256; for (i = 0; i < 256; i++) { - psb_intel_crtc->lut_r[i] = i; - psb_intel_crtc->lut_g[i] = i; - psb_intel_crtc->lut_b[i] = i; + gma_crtc->lut_r[i] = i; + gma_crtc->lut_g[i] = i; + gma_crtc->lut_b[i] = i; r_base[i] = i << 8; g_base[i] = i << 8; b_base[i] = i << 8; - psb_intel_crtc->lut_adj[i] = 0; + gma_crtc->lut_adj[i] = 0; } - psb_intel_crtc->mode_dev = mode_dev; - psb_intel_crtc->cursor_addr = 0; + gma_crtc->mode_dev = mode_dev; + gma_crtc->cursor_addr = 0; - drm_crtc_helper_add(&psb_intel_crtc->base, + drm_crtc_helper_add(&gma_crtc->base, dev_priv->ops->crtc_helper); /* Setup the array of drm_connector pointer array */ - psb_intel_crtc->mode_set.crtc = &psb_intel_crtc->base; + gma_crtc->mode_set.crtc = &gma_crtc->base; BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || - dev_priv->plane_to_crtc_mapping[psb_intel_crtc->plane] != NULL); - dev_priv->plane_to_crtc_mapping[psb_intel_crtc->plane] = - &psb_intel_crtc->base; - dev_priv->pipe_to_crtc_mapping[psb_intel_crtc->pipe] = - &psb_intel_crtc->base; - psb_intel_crtc->mode_set.connectors = - (struct drm_connector **) (psb_intel_crtc + 1); - psb_intel_crtc->mode_set.num_connectors = 0; - psb_intel_cursor_init(dev, pipe); -} - -int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_psb_private *dev_priv = dev->dev_private; - struct drm_psb_get_pipe_from_crtc_id_arg *pipe_from_crtc_id = data; - struct drm_mode_object *drmmode_obj; - struct psb_intel_crtc *crtc; - - if (!dev_priv) { - dev_err(dev->dev, "called with no initialization\n"); - return -EINVAL; - } + dev_priv->plane_to_crtc_mapping[gma_crtc->plane] != NULL); + dev_priv->plane_to_crtc_mapping[gma_crtc->plane] = &gma_crtc->base; + dev_priv->pipe_to_crtc_mapping[gma_crtc->pipe] = &gma_crtc->base; + gma_crtc->mode_set.connectors = (struct drm_connector **)(gma_crtc + 1); + gma_crtc->mode_set.num_connectors = 0; + psb_intel_cursor_init(dev, gma_crtc); - drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id, - DRM_MODE_OBJECT_CRTC); - - if (!drmmode_obj) { - dev_err(dev->dev, "no such CRTC id\n"); - return -EINVAL; - } - - crtc = to_psb_intel_crtc(obj_to_crtc(drmmode_obj)); - pipe_from_crtc_id->pipe = crtc->pipe; - - return 0; + /* Set to true so that the pipe is forced off on initial config. */ + gma_crtc->active = true; } struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, int pipe) @@ -1395,14 +560,14 @@ struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, int pipe) struct drm_crtc *crtc = NULL; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); - if (psb_intel_crtc->pipe == pipe) + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); + if (gma_crtc->pipe == pipe) break; } return crtc; } -int psb_intel_connector_clones(struct drm_device *dev, int type_mask) +int gma_connector_clones(struct drm_device *dev, int type_mask) { int index_mask = 0; struct drm_connector *connector; @@ -1410,37 +575,10 @@ int psb_intel_connector_clones(struct drm_device *dev, int type_mask) list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); - if (type_mask & (1 << psb_intel_encoder->type)) + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + if (type_mask & (1 << gma_encoder->type)) index_mask |= (1 << entry); entry++; } 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 -*/ -struct drm_encoder *psb_intel_best_encoder(struct drm_connector *connector) -{ - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); - - return &psb_intel_encoder->base; -} - -void psb_intel_connector_attach_encoder(struct psb_intel_connector *connector, - struct psb_intel_encoder *encoder) -{ - connector->encoder = encoder; - drm_mode_connector_attach_encoder(&connector->base, - &encoder->base); -} diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index f40535e5668..336bd3aa1a0 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -24,21 +24,15 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <linux/gpio.h> +#include "gma_display.h" /* * Display related stuff */ -/* store information about an Ixxx DVO */ -/* The i830->i865 use multiple DVOs with multiple i2cs */ -/* the i915, i945 have a single sDVO i2c bus - which is different */ -#define MAX_OUTPUTS 6 /* maximum connectors per crtcs in the mode set */ #define INTELFB_CONN_LIMIT 4 -#define INTEL_I2C_BUS_DVO 1 -#define INTEL_I2C_BUS_SDVO 2 - /* Intel Pipe Clone Bit */ #define INTEL_HDMIB_CLONE_BIT 1 #define INTEL_HDMIC_CLONE_BIT 2 @@ -69,11 +63,8 @@ #define INTEL_OUTPUT_HDMI 6 #define INTEL_OUTPUT_MIPI 7 #define INTEL_OUTPUT_MIPI2 8 - -#define INTEL_DVO_CHIP_NONE 0 -#define INTEL_DVO_CHIP_LVDS 1 -#define INTEL_DVO_CHIP_TMDS 2 -#define INTEL_DVO_CHIP_TVOUT 4 +#define INTEL_OUTPUT_DISPLAYPORT 9 +#define INTEL_OUTPUT_EDP 10 #define INTEL_MODE_PIXEL_MULTIPLIER_SHIFT (0x0) #define INTEL_MODE_PIXEL_MULTIPLIER_MASK (0xf << INTEL_MODE_PIXEL_MULTIPLIER_SHIFT) @@ -106,11 +97,6 @@ struct psb_intel_mode_device { size_t(*bo_offset) (struct drm_device *dev, void *bo); /* - * Cursor (Can go ?) - */ - int cursor_needs_physical; - - /* * LVDS info */ int backlight_duty_cycle; /* restore backlight to this value */ @@ -131,13 +117,18 @@ struct psb_intel_i2c_chan { u8 slave_addr; }; -struct psb_intel_encoder { +struct gma_encoder { struct drm_encoder base; int type; bool needs_tv_clock; - void (*hot_plug)(struct psb_intel_encoder *); + void (*hot_plug)(struct gma_encoder *); int crtc_mask; int clone_mask; + u32 ddi_select; /* Channel info */ +#define DDI0_SELECT 0x01 +#define DDI1_SELECT 0x02 +#define DP_MASK 0x8000 +#define DDI_MASK 0x03 void *dev_priv; /* For sdvo_priv, lvds_priv, etc... */ /* FIXME: Either make SDVO and LVDS store it's i2c here or give CDV it's @@ -146,9 +137,9 @@ struct psb_intel_encoder { struct psb_intel_i2c_chan *ddc_bus; }; -struct psb_intel_connector { +struct gma_connector { struct drm_connector base; - struct psb_intel_encoder *encoder; + struct gma_encoder *encoder; }; struct psb_intel_crtc_state { @@ -171,11 +162,12 @@ struct psb_intel_crtc_state { uint32_t savePalette[256]; }; -struct psb_intel_crtc { +struct gma_crtc { struct drm_crtc base; int pipe; int plane; uint32_t cursor_addr; + struct gtt_range *cursor_gt; u8 lut_r[256], lut_g[256], lut_b[256]; u8 lut_adj[256]; struct psb_intel_framebuffer *fbdev_fb; @@ -193,16 +185,20 @@ struct psb_intel_crtc { /*crtc mode setting flags*/ u32 mode_flags; + bool active; + /* Saved Crtc HW states */ struct psb_intel_crtc_state *crtc_state; + + const struct gma_clock_funcs *clock_funcs; }; -#define to_psb_intel_crtc(x) \ - container_of(x, struct psb_intel_crtc, base) -#define to_psb_intel_connector(x) \ - container_of(x, struct psb_intel_connector, base) -#define to_psb_intel_encoder(x) \ - container_of(x, struct psb_intel_encoder, base) +#define to_gma_crtc(x) \ + container_of(x, struct gma_crtc, base) +#define to_gma_connector(x) \ + container_of(x, struct gma_connector, base) +#define to_gma_encoder(x) \ + container_of(x, struct gma_encoder, base) #define to_psb_intel_framebuffer(x) \ container_of(x, struct psb_intel_framebuffer, base) @@ -230,29 +226,18 @@ extern void oaktrail_dsi_init(struct drm_device *dev, extern void mid_dsi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int dsi_num); -extern void psb_intel_crtc_load_lut(struct drm_crtc *crtc); -extern void psb_intel_encoder_prepare(struct drm_encoder *encoder); -extern void psb_intel_encoder_commit(struct drm_encoder *encoder); -extern void psb_intel_encoder_destroy(struct drm_encoder *encoder); +extern struct drm_encoder *gma_best_encoder(struct drm_connector *connector); +extern void gma_connector_attach_encoder(struct gma_connector *connector, + struct gma_encoder *encoder); -static inline struct psb_intel_encoder *psb_intel_attached_encoder( +static inline struct gma_encoder *gma_attached_encoder( struct drm_connector *connector) { - return to_psb_intel_connector(connector)->encoder; + return to_gma_connector(connector)->encoder; } -extern void psb_intel_connector_attach_encoder( - struct psb_intel_connector *connector, - struct psb_intel_encoder *encoder); - -extern struct drm_encoder *psb_intel_best_encoder(struct drm_connector - *connector); - extern struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, struct drm_crtc *crtc); -extern void psb_intel_wait_for_vblank(struct drm_device *dev); -extern int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, - struct drm_file *file_priv); extern struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, int pipe); extern struct drm_connector *psb_intel_sdvo_find(struct drm_device *dev, @@ -263,13 +248,8 @@ extern void psb_intel_sdvo_set_hotplug(struct drm_connector *connector, extern int intelfb_probe(struct drm_device *dev); extern int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); -extern struct drm_framebuffer *psb_intel_framebuffer_create(struct drm_device - *dev, struct - drm_mode_fb_cmd - *mode_cmd, - void *mm_private); extern bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); extern int psb_intel_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode); @@ -286,4 +266,20 @@ extern void gma_intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed); extern void gma_intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit); extern void gma_intel_teardown_gmbus(struct drm_device *dev); +/* DP support */ +extern void cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg); +extern void cdv_intel_dp_set_m_n(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + +extern void psb_intel_attach_force_audio_property(struct drm_connector *connector); +extern void psb_intel_attach_broadcast_rgb_property(struct drm_connector *connector); + +extern int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val); +extern int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val); +extern void cdv_sb_reset(struct drm_device *dev); + +extern void cdv_intel_attach_force_audio_property(struct drm_connector *connector); +extern void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector); + #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index a25e4ca5e91..d7778d0472c 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)); } } @@ -267,10 +267,9 @@ static void psb_intel_lvds_save(struct drm_connector *connector) struct drm_device *dev = connector->dev; struct drm_psb_private *dev_priv = (struct drm_psb_private *)dev->dev_private; - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); struct psb_intel_lvds_priv *lvds_priv = - (struct psb_intel_lvds_priv *)psb_intel_encoder->dev_priv; + (struct psb_intel_lvds_priv *)gma_encoder->dev_priv; lvds_priv->savePP_ON = REG_READ(LVDSPP_ON); lvds_priv->savePP_OFF = REG_READ(LVDSPP_OFF); @@ -283,7 +282,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); /* @@ -307,10 +306,9 @@ static void psb_intel_lvds_restore(struct drm_connector *connector) { struct drm_device *dev = connector->dev; u32 pp_status; - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); struct psb_intel_lvds_priv *lvds_priv = - (struct psb_intel_lvds_priv *)psb_intel_encoder->dev_priv; + (struct psb_intel_lvds_priv *)gma_encoder->dev_priv; dev_dbg(dev->dev, "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", lvds_priv->savePP_ON, @@ -349,12 +347,11 @@ int psb_intel_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct drm_psb_private *dev_priv = connector->dev->dev_private; - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); struct drm_display_mode *fixed_mode = dev_priv->mode_dev.panel_fixed_mode; - if (psb_intel_encoder->type == INTEL_OUTPUT_MIPI2) + if (gma_encoder->type == INTEL_OUTPUT_MIPI2) fixed_mode = dev_priv->mode_dev.panel_fixed_mode2; /* just in case */ @@ -375,28 +372,26 @@ int psb_intel_lvds_mode_valid(struct drm_connector *connector, } bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; - struct psb_intel_crtc *psb_intel_crtc = - to_psb_intel_crtc(encoder->crtc); + struct gma_crtc *gma_crtc = to_gma_crtc(encoder->crtc); struct drm_encoder *tmp_encoder; struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode; - struct psb_intel_encoder *psb_intel_encoder = - to_psb_intel_encoder(encoder); + struct gma_encoder *gma_encoder = to_gma_encoder(encoder); - if (psb_intel_encoder->type == INTEL_OUTPUT_MIPI2) + if (gma_encoder->type == INTEL_OUTPUT_MIPI2) panel_fixed_mode = mode_dev->panel_fixed_mode2; /* PSB requires the LVDS is on pipe B, MRST has only one pipe anyway */ - if (!IS_MRST(dev) && psb_intel_crtc->pipe == 0) { + if (!IS_MRST(dev) && gma_crtc->pipe == 0) { printk(KERN_ERR "Can't support LVDS on pipe A\n"); return false; } - if (IS_MRST(dev) && psb_intel_crtc->pipe != 0) { + if (IS_MRST(dev) && gma_crtc->pipe != 0) { printk(KERN_ERR "Must use PIPE A\n"); return false; } @@ -525,9 +520,8 @@ static int psb_intel_lvds_get_modes(struct drm_connector *connector) struct drm_device *dev = connector->dev; struct drm_psb_private *dev_priv = dev->dev_private; struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); - struct psb_intel_lvds_priv *lvds_priv = psb_intel_encoder->dev_priv; + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + struct psb_intel_lvds_priv *lvds_priv = gma_encoder->dev_priv; int ret = 0; if (!IS_MRST(dev)) @@ -564,9 +558,8 @@ static int psb_intel_lvds_get_modes(struct drm_connector *connector) */ void psb_intel_lvds_destroy(struct drm_connector *connector) { - struct psb_intel_encoder *psb_intel_encoder = - psb_intel_attached_encoder(connector); - struct psb_intel_lvds_priv *lvds_priv = psb_intel_encoder->dev_priv; + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + struct psb_intel_lvds_priv *lvds_priv = gma_encoder->dev_priv; if (lvds_priv->ddc_bus) psb_intel_i2c_destroy(lvds_priv->ddc_bus); @@ -585,8 +578,7 @@ int psb_intel_lvds_set_property(struct drm_connector *connector, return -1; if (!strcmp(property->name, "scaling mode")) { - struct psb_intel_crtc *crtc = - to_psb_intel_crtc(encoder->crtc); + struct gma_crtc *crtc = to_gma_crtc(encoder->crtc); uint64_t curval; if (!crtc) @@ -603,7 +595,7 @@ int psb_intel_lvds_set_property(struct drm_connector *connector, goto set_prop_error; } - if (drm_connector_property_get_value(connector, + if (drm_object_property_get_value(&connector->base, property, &curval)) goto set_prop_error; @@ -611,7 +603,7 @@ int psb_intel_lvds_set_property(struct drm_connector *connector, if (curval == value) goto set_prop_done; - if (drm_connector_property_set_value(connector, + if (drm_object_property_set_value(&connector->base, property, value)) goto set_prop_error; @@ -622,25 +614,16 @@ int psb_intel_lvds_set_property(struct drm_connector *connector, &crtc->saved_mode, encoder->crtc->x, encoder->crtc->y, - encoder->crtc->fb)) + encoder->crtc->primary->fb)) goto set_prop_error; } } else if (!strcmp(property->name, "backlight")) { - if (drm_connector_property_set_value(connector, + if (drm_object_property_set_value(&connector->base, property, value)) goto set_prop_error; - else { -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct drm_psb_private *devp = - encoder->dev->dev_private; - struct backlight_device *bd = devp->backlight_device; - if (bd) { - bd->props.brightness = value; - backlight_update_status(bd); - } -#endif - } + else + gma_backlight_set(encoder->dev, value); } else if (!strcmp(property->name, "DPMS")) { struct drm_encoder_helper_funcs *hfuncs = encoder->helper_private; @@ -665,7 +648,7 @@ const struct drm_connector_helper_funcs psb_intel_lvds_connector_helper_funcs = { .get_modes = psb_intel_lvds_get_modes, .mode_valid = psb_intel_lvds_mode_valid, - .best_encoder = psb_intel_best_encoder, + .best_encoder = gma_best_encoder, }; const struct drm_connector_funcs psb_intel_lvds_connector_funcs = { @@ -700,8 +683,8 @@ const struct drm_encoder_funcs psb_intel_lvds_enc_funcs = { void psb_intel_lvds_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev) { - struct psb_intel_encoder *psb_intel_encoder; - struct psb_intel_connector *psb_intel_connector; + struct gma_encoder *gma_encoder; + struct gma_connector *gma_connector; struct psb_intel_lvds_priv *lvds_priv; struct drm_connector *connector; struct drm_encoder *encoder; @@ -711,20 +694,16 @@ void psb_intel_lvds_init(struct drm_device *dev, u32 lvds; int pipe; - psb_intel_encoder = - kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL); - - if (!psb_intel_encoder) { - dev_err(dev->dev, "psb_intel_encoder allocation error\n"); + gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); + if (!gma_encoder) { + dev_err(dev->dev, "gma_encoder allocation error\n"); return; } - psb_intel_connector = - kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL); - - if (!psb_intel_connector) { - kfree(psb_intel_encoder); - dev_err(dev->dev, "psb_intel_connector allocation error\n"); + gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); + if (!gma_connector) { + dev_err(dev->dev, "gma_connector allocation error\n"); + goto failed_encoder; } lvds_priv = kzalloc(sizeof(struct psb_intel_lvds_priv), GFP_KERNEL); @@ -733,10 +712,10 @@ void psb_intel_lvds_init(struct drm_device *dev, goto failed_connector; } - psb_intel_encoder->dev_priv = lvds_priv; + gma_encoder->dev_priv = lvds_priv; - connector = &psb_intel_connector->base; - encoder = &psb_intel_encoder->base; + connector = &gma_connector->base; + encoder = &gma_encoder->base; drm_connector_init(dev, connector, &psb_intel_lvds_connector_funcs, DRM_MODE_CONNECTOR_LVDS); @@ -745,9 +724,8 @@ void psb_intel_lvds_init(struct drm_device *dev, &psb_intel_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS); - psb_intel_connector_attach_encoder(psb_intel_connector, - psb_intel_encoder); - psb_intel_encoder->type = INTEL_OUTPUT_LVDS; + gma_connector_attach_encoder(gma_connector, gma_encoder); + gma_encoder->type = INTEL_OUTPUT_LVDS; drm_encoder_helper_add(encoder, &psb_intel_lvds_helper_funcs); drm_connector_helper_add(connector, @@ -757,10 +735,10 @@ void psb_intel_lvds_init(struct drm_device *dev, connector->doublescan_allowed = false; /*Attach connector properties*/ - drm_connector_attach_property(connector, + drm_object_attach_property(&connector->base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); - drm_connector_attach_property(connector, + drm_object_attach_property(&connector->base, dev_priv->backlight_property, BRIGHTNESS_MAX_LEVEL); @@ -799,6 +777,7 @@ void psb_intel_lvds_init(struct drm_device *dev, * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ + mutex_lock(&dev->mode_config.mutex); psb_intel_ddc_get_modes(connector, &lvds_priv->ddc_bus->adapter); list_for_each_entry(scan, &connector->probed_modes, head) { if (scan->type & DRM_MODE_TYPE_PREFERRED) { @@ -849,10 +828,12 @@ void psb_intel_lvds_init(struct drm_device *dev, * actually having one. */ out: + mutex_unlock(&dev->mode_config.mutex); drm_sysfs_connector_add(connector); return; failed_find: + mutex_unlock(&dev->mode_config.mutex); if (lvds_priv->ddc_bus) psb_intel_i2c_destroy(lvds_priv->ddc_bus); failed_ddc: @@ -862,7 +843,8 @@ failed_blc_i2c: drm_encoder_cleanup(encoder); drm_connector_cleanup(connector); failed_connector: - if (psb_intel_connector) - kfree(psb_intel_connector); + kfree(gma_connector); +failed_encoder: + kfree(gma_encoder); } diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h index fcc0af03d68..0be30e4d146 100644 --- a/drivers/gpu/drm/gma500/psb_intel_reg.h +++ b/drivers/gpu/drm/gma500/psb_intel_reg.h @@ -91,6 +91,9 @@ #define BLC_PWM_CTL 0x61254 #define BLC_PWM_CTL2 0x61250 +#define PWM_ENABLE (1 << 31) +#define PWM_LEGACY_MODE (1 << 30) +#define PWM_PIPE_B (1 << 29) #define BLC_PWM_CTL_C 0x62254 #define BLC_PWM_CTL2_C 0x62250 #define BACKLIGHT_MODULATION_FREQ_SHIFT (17) @@ -170,13 +173,47 @@ #define PP_SEQUENCE_ON (1 << 28) #define PP_SEQUENCE_OFF (2 << 28) #define PP_SEQUENCE_MASK 0x30000000 +#define PP_CYCLE_DELAY_ACTIVE (1 << 27) +#define PP_SEQUENCE_STATE_ON_IDLE (1 << 3) +#define PP_SEQUENCE_STATE_MASK 0x0000000f + #define PP_CONTROL 0x61204 #define POWER_TARGET_ON (1 << 0) - +#define PANEL_UNLOCK_REGS (0xabcd << 16) +#define PANEL_UNLOCK_MASK (0xffff << 16) +#define EDP_FORCE_VDD (1 << 3) +#define EDP_BLC_ENABLE (1 << 2) +#define PANEL_POWER_RESET (1 << 1) +#define PANEL_POWER_OFF (0 << 0) +#define PANEL_POWER_ON (1 << 0) + +/* Poulsbo/Oaktrail */ #define LVDSPP_ON 0x61208 #define LVDSPP_OFF 0x6120c #define PP_CYCLE 0x61210 +/* Cedartrail */ +#define PP_ON_DELAYS 0x61208 /* Cedartrail */ +#define PANEL_PORT_SELECT_MASK (3 << 30) +#define PANEL_PORT_SELECT_LVDS (0 << 30) +#define PANEL_PORT_SELECT_EDP (1 << 30) +#define PANEL_POWER_UP_DELAY_MASK (0x1fff0000) +#define PANEL_POWER_UP_DELAY_SHIFT 16 +#define PANEL_LIGHT_ON_DELAY_MASK (0x1fff) +#define PANEL_LIGHT_ON_DELAY_SHIFT 0 + +#define PP_OFF_DELAYS 0x6120c /* Cedartrail */ +#define PANEL_POWER_DOWN_DELAY_MASK (0x1fff0000) +#define PANEL_POWER_DOWN_DELAY_SHIFT 16 +#define PANEL_LIGHT_OFF_DELAY_MASK (0x1fff) +#define PANEL_LIGHT_OFF_DELAY_SHIFT 0 + +#define PP_DIVISOR 0x61210 /* Cedartrail */ +#define PP_REFERENCE_DIVIDER_MASK (0xffffff00) +#define PP_REFERENCE_DIVIDER_SHIFT 8 +#define PANEL_POWER_CYCLE_DELAY_MASK (0x1f) +#define PANEL_POWER_CYCLE_DELAY_SHIFT 0 + #define PFIT_CONTROL 0x61230 #define PFIT_ENABLE (1 << 31) #define PFIT_PIPE_MASK (3 << 29) @@ -213,7 +250,7 @@ #define DPLLB_LVDS_P2_CLOCK_DIV_14 (0 << 24) /* i915 */ #define DPLLB_LVDS_P2_CLOCK_DIV_7 (1 << 24) /* i915 */ #define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */ -#define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ +#define DPLL_FPA0h1_P1_POST_DIV_MASK 0x00ff0000 /* i915 */ #define DPLL_LOCK (1 << 15) /* CDV */ /* @@ -340,6 +377,9 @@ #define FP_M2_DIV_SHIFT 0 #define PORT_HOTPLUG_EN 0x61110 +#define HDMIB_HOTPLUG_INT_EN (1 << 29) +#define HDMIC_HOTPLUG_INT_EN (1 << 28) +#define HDMID_HOTPLUG_INT_EN (1 << 27) #define SDVOB_HOTPLUG_INT_EN (1 << 26) #define SDVOC_HOTPLUG_INT_EN (1 << 25) #define TV_HOTPLUG_INT_EN (1 << 18) @@ -453,7 +493,6 @@ #define PIPEACONF_DISABLE 0 #define PIPEACONF_DOUBLE_WIDE (1 << 30) #define PIPECONF_ACTIVE (1 << 30) -#define I965_PIPECONF_ACTIVE (1 << 30) #define PIPECONF_DSIPLL_LOCK (1 << 29) #define PIPEACONF_SINGLE_WIDE 0 #define PIPEACONF_PIPE_UNLOCKED 0 @@ -498,10 +537,12 @@ #define PIPE_VBLANK_INTERRUPT_ENABLE (1UL << 17) #define PIPE_START_VBLANK_INTERRUPT_ENABLE (1UL << 18) #define PIPE_TE_ENABLE (1UL << 22) +#define PIPE_LEGACY_BLC_EVENT_ENABLE (1UL << 22) #define PIPE_DPST_EVENT_ENABLE (1UL << 23) #define PIPE_VSYNC_ENABL (1UL << 25) #define PIPE_HDMI_AUDIO_UNDERRUN (1UL << 26) #define PIPE_HDMI_AUDIO_BUFFER_DONE (1UL << 27) +#define PIPE_FIFO_UNDERRUN (1UL << 31) #define PIPE_HDMI_AUDIO_INT_MASK (PIPE_HDMI_AUDIO_UNDERRUN | \ PIPE_HDMI_AUDIO_BUFFER_DONE) #define PIPE_EVENT_MASK ((1 << 29)|(1 << 28)|(1 << 27)|(1 << 26)|(1 << 24)|(1 << 23)|(1 << 22)|(1 << 21)|(1 << 20)|(1 << 16)) @@ -566,12 +607,27 @@ struct dpst_guardband { #define PIPE_PIXEL_MASK 0x00ffffff #define PIPE_PIXEL_SHIFT 0 +#define FW_BLC_SELF 0x20e0 +#define FW_BLC_SELF_EN (1<<15) + #define DSPARB 0x70030 #define DSPFW1 0x70034 +#define DSP_FIFO_SR_WM_MASK 0xFF800000 +#define DSP_FIFO_SR_WM_SHIFT 23 +#define CURSOR_B_FIFO_WM_MASK 0x003F0000 +#define CURSOR_B_FIFO_WM_SHIFT 16 #define DSPFW2 0x70038 +#define CURSOR_A_FIFO_WM_MASK 0x3F00 +#define CURSOR_A_FIFO_WM_SHIFT 8 +#define DSP_PLANE_C_FIFO_WM_MASK 0x7F +#define DSP_PLANE_C_FIFO_WM_SHIFT 0 #define DSPFW3 0x7003c #define DSPFW4 0x70050 #define DSPFW5 0x70054 +#define DSP_PLANE_B_FIFO_WM1_SHIFT 24 +#define DSP_PLANE_A_FIFO_WM1_SHIFT 16 +#define CURSOR_B_FIFO_WM1_SHIFT 8 +#define CURSOR_FIFO_SR_WM1_SHIFT 0 #define DSPFW6 0x70058 #define DSPCHICKENBIT 0x70400 #define DSPACNTR 0x70180 @@ -1252,6 +1308,16 @@ 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 DPUNIT_PIPEB_GATE_DISABLE (1 << 30) +# define DPUNIT_PIPEA_GATE_DISABLE (1 << 25) +# define DPCUNIT_CLOCK_GATE_DISABLE (1 << 24) +# define DPLSUNIT_CLOCK_GATE_DISABLE (1 << 13) + +#define RAMCLK_GATE_D 0x6210 /* 32-bit value read/written from the DPIO reg. */ #define SB_DATA 0x02104 /* cedarview */ @@ -1281,6 +1347,15 @@ No status bits are changed. #define SB_N_CB_TUNE_MASK PSB_MASK(25, 24) #define SB_N_CB_TUNE_SHIFT 24 +/* the bit 14:13 is used to select between the different reference clock for Pipe A/B */ +#define SB_REF_DPLLA 0x8010 +#define SB_REF_DPLLB 0x8030 +#define REF_CLK_MASK (0x3 << 13) +#define REF_CLK_CORE (0 << 13) +#define REF_CLK_DPLL (1 << 13) +#define REF_CLK_DPLLA (2 << 13) +/* For the DPLL B, it will use the reference clk from DPLL A when using (2 << 13) */ + #define _SB_REF_A 0x8018 #define _SB_REF_B 0x8038 #define SB_REF_SFR(pipe) _PIPE(pipe, _SB_REF_A, _SB_REF_B) @@ -1304,6 +1379,167 @@ No status bits are changed. #define LANE_PLL_MASK (0x7 << 20) #define LANE_PLL_ENABLE (0x3 << 20) +#define LANE_PLL_PIPE(p) (((p) == 0) ? (1 << 21) : (0 << 21)) + +#define DP_B 0x64100 +#define DP_C 0x64200 + +#define DP_PORT_EN (1 << 31) +#define DP_PIPEB_SELECT (1 << 30) +#define DP_PIPE_MASK (1 << 30) + +/* Link training mode - select a suitable mode for each stage */ +#define DP_LINK_TRAIN_PAT_1 (0 << 28) +#define DP_LINK_TRAIN_PAT_2 (1 << 28) +#define DP_LINK_TRAIN_PAT_IDLE (2 << 28) +#define DP_LINK_TRAIN_OFF (3 << 28) +#define DP_LINK_TRAIN_MASK (3 << 28) +#define DP_LINK_TRAIN_SHIFT 28 + +/* Signal voltages. These are mostly controlled by the other end */ +#define DP_VOLTAGE_0_4 (0 << 25) +#define DP_VOLTAGE_0_6 (1 << 25) +#define DP_VOLTAGE_0_8 (2 << 25) +#define DP_VOLTAGE_1_2 (3 << 25) +#define DP_VOLTAGE_MASK (7 << 25) +#define DP_VOLTAGE_SHIFT 25 + +/* Signal pre-emphasis levels, like voltages, the other end tells us what + * they want + */ +#define DP_PRE_EMPHASIS_0 (0 << 22) +#define DP_PRE_EMPHASIS_3_5 (1 << 22) +#define DP_PRE_EMPHASIS_6 (2 << 22) +#define DP_PRE_EMPHASIS_9_5 (3 << 22) +#define DP_PRE_EMPHASIS_MASK (7 << 22) +#define DP_PRE_EMPHASIS_SHIFT 22 + +/* How many wires to use. I guess 3 was too hard */ +#define DP_PORT_WIDTH_1 (0 << 19) +#define DP_PORT_WIDTH_2 (1 << 19) +#define DP_PORT_WIDTH_4 (3 << 19) +#define DP_PORT_WIDTH_MASK (7 << 19) + +/* Mystic DPCD version 1.1 special mode */ +#define DP_ENHANCED_FRAMING (1 << 18) + +/** locked once port is enabled */ +#define DP_PORT_REVERSAL (1 << 15) + +/** sends the clock on lane 15 of the PEG for debug */ +#define DP_CLOCK_OUTPUT_ENABLE (1 << 13) + +#define DP_SCRAMBLING_DISABLE (1 << 12) +#define DP_SCRAMBLING_DISABLE_IRONLAKE (1 << 7) + +/** limit RGB values to avoid confusing TVs */ +#define DP_COLOR_RANGE_16_235 (1 << 8) + +/** Turn on the audio link */ +#define DP_AUDIO_OUTPUT_ENABLE (1 << 6) + +/** vs and hs sync polarity */ +#define DP_SYNC_VS_HIGH (1 << 4) +#define DP_SYNC_HS_HIGH (1 << 3) + +/** A fantasy */ +#define DP_DETECTED (1 << 2) + +/** The aux channel provides a way to talk to the + * signal sink for DDC etc. Max packet size supported + * is 20 bytes in each direction, hence the 5 fixed + * data registers + */ +#define DPB_AUX_CH_CTL 0x64110 +#define DPB_AUX_CH_DATA1 0x64114 +#define DPB_AUX_CH_DATA2 0x64118 +#define DPB_AUX_CH_DATA3 0x6411c +#define DPB_AUX_CH_DATA4 0x64120 +#define DPB_AUX_CH_DATA5 0x64124 + +#define DPC_AUX_CH_CTL 0x64210 +#define DPC_AUX_CH_DATA1 0x64214 +#define DPC_AUX_CH_DATA2 0x64218 +#define DPC_AUX_CH_DATA3 0x6421c +#define DPC_AUX_CH_DATA4 0x64220 +#define DPC_AUX_CH_DATA5 0x64224 + +#define DP_AUX_CH_CTL_SEND_BUSY (1 << 31) +#define DP_AUX_CH_CTL_DONE (1 << 30) +#define DP_AUX_CH_CTL_INTERRUPT (1 << 29) +#define DP_AUX_CH_CTL_TIME_OUT_ERROR (1 << 28) +#define DP_AUX_CH_CTL_TIME_OUT_400us (0 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_600us (1 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_800us (2 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_1600us (3 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_MASK (3 << 26) +#define DP_AUX_CH_CTL_RECEIVE_ERROR (1 << 25) +#define DP_AUX_CH_CTL_MESSAGE_SIZE_MASK (0x1f << 20) +#define DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT 20 +#define DP_AUX_CH_CTL_PRECHARGE_2US_MASK (0xf << 16) +#define DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT 16 +#define DP_AUX_CH_CTL_AUX_AKSV_SELECT (1 << 15) +#define DP_AUX_CH_CTL_MANCHESTER_TEST (1 << 14) +#define DP_AUX_CH_CTL_SYNC_TEST (1 << 13) +#define DP_AUX_CH_CTL_DEGLITCH_TEST (1 << 12) +#define DP_AUX_CH_CTL_PRECHARGE_TEST (1 << 11) +#define DP_AUX_CH_CTL_BIT_CLOCK_2X_MASK (0x7ff) +#define DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT 0 + +/* + * Computing GMCH M and N values for the Display Port link + * + * GMCH M/N = dot clock * bytes per pixel / ls_clk * # of lanes + * + * ls_clk (we assume) is the DP link clock (1.62 or 2.7 GHz) + * + * The GMCH value is used internally + * + * bytes_per_pixel is the number of bytes coming out of the plane, + * which is after the LUTs, so we want the bytes for our color format. + * For our current usage, this is always 3, one byte for R, G and B. + */ + +#define _PIPEA_GMCH_DATA_M 0x70050 +#define _PIPEB_GMCH_DATA_M 0x71050 + +/* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */ +#define PIPE_GMCH_DATA_M_TU_SIZE_MASK (0x3f << 25) +#define PIPE_GMCH_DATA_M_TU_SIZE_SHIFT 25 + +#define PIPE_GMCH_DATA_M_MASK (0xffffff) + +#define _PIPEA_GMCH_DATA_N 0x70054 +#define _PIPEB_GMCH_DATA_N 0x71054 +#define PIPE_GMCH_DATA_N_MASK (0xffffff) + +/* + * Computing Link M and N values for the Display Port link + * + * Link M / N = pixel_clock / ls_clk + * + * (the DP spec calls pixel_clock the 'strm_clk') + * + * The Link value is transmitted in the Main Stream + * Attributes and VB-ID. + */ + +#define _PIPEA_DP_LINK_M 0x70060 +#define _PIPEB_DP_LINK_M 0x71060 +#define PIPEA_DP_LINK_M_MASK (0xffffff) + +#define _PIPEA_DP_LINK_N 0x70064 +#define _PIPEB_DP_LINK_N 0x71064 +#define PIPEA_DP_LINK_N_MASK (0xffffff) + +#define PIPE_GMCH_DATA_M(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_M, _PIPEB_GMCH_DATA_M) +#define PIPE_GMCH_DATA_N(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_N, _PIPEB_GMCH_DATA_N) +#define PIPE_DP_LINK_M(pipe) _PIPE(pipe, _PIPEA_DP_LINK_M, _PIPEB_DP_LINK_M) +#define PIPE_DP_LINK_N(pipe) _PIPE(pipe, _PIPEA_DP_LINK_N, _PIPEB_DP_LINK_N) +#define PIPE_BPC_MASK (7 << 5) +#define PIPE_8BPC (0 << 5) +#define PIPE_10BPC (1 << 5) +#define PIPE_6BPC (2 << 5) #endif diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index 88b42971c0f..deeb0829b12 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -29,12 +29,11 @@ #include <linux/i2c.h> #include <linux/slab.h> #include <linux/delay.h> -#include "drmP.h" -#include "drm.h" -#include "drm_crtc.h" -#include "drm_edid.h" +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_edid.h> #include "psb_intel_drv.h" -#include "gma_drm.h" +#include <drm/gma_drm.h> #include "psb_drv.h" #include "psb_intel_sdvo_regs.h" #include "psb_intel_reg.h" @@ -66,7 +65,7 @@ static const char *tv_format_names[] = { #define TV_FORMAT_NUM (sizeof(tv_format_names) / sizeof(*tv_format_names)) struct psb_intel_sdvo { - struct psb_intel_encoder base; + struct gma_encoder base; struct i2c_adapter *i2c; u8 slave_addr; @@ -135,10 +134,13 @@ struct psb_intel_sdvo { /* Input timings for adjusted_mode */ struct psb_intel_sdvo_dtd input_dtd; + + /* Saved SDVO output states */ + uint32_t saveSDVO; /* Can be SDVOB or SDVOC depending on sdvo_reg */ }; struct psb_intel_sdvo_connector { - struct psb_intel_connector base; + struct gma_connector base; /* Mark the type of connector */ uint16_t output_flag; @@ -198,13 +200,13 @@ static struct psb_intel_sdvo *to_psb_intel_sdvo(struct drm_encoder *encoder) static struct psb_intel_sdvo *intel_attached_sdvo(struct drm_connector *connector) { - return container_of(psb_intel_attached_encoder(connector), + return container_of(gma_attached_encoder(connector), struct psb_intel_sdvo, base); } static struct psb_intel_sdvo_connector *to_psb_intel_sdvo_connector(struct drm_connector *connector) { - return container_of(to_psb_intel_connector(connector), struct psb_intel_sdvo_connector, base); + return container_of(to_gma_connector(connector), struct psb_intel_sdvo_connector, base); } static bool @@ -226,24 +228,26 @@ static void psb_intel_sdvo_write_sdvox(struct psb_intel_sdvo *psb_intel_sdvo, u3 { struct drm_device *dev = psb_intel_sdvo->base.base.dev; u32 bval = val, cval = val; - int i; + int i, j; + int need_aux = IS_MRST(dev) ? 1 : 0; - if (psb_intel_sdvo->sdvo_reg == SDVOB) { - cval = REG_READ(SDVOC); - } else { - bval = REG_READ(SDVOB); - } - /* - * Write the registers twice for luck. Sometimes, - * writing them only once doesn't appear to 'stick'. - * The BIOS does this too. Yay, magic - */ - for (i = 0; i < 2; i++) - { - REG_WRITE(SDVOB, bval); - REG_READ(SDVOB); - REG_WRITE(SDVOC, cval); - REG_READ(SDVOC); + for (j = 0; j <= need_aux; j++) { + if (psb_intel_sdvo->sdvo_reg == SDVOB) + cval = REG_READ_WITH_AUX(SDVOC, j); + else + bval = REG_READ_WITH_AUX(SDVOB, j); + + /* + * Write the registers twice for luck. Sometimes, + * writing them only once doesn't appear to 'stick'. + * The BIOS does this too. Yay, magic + */ + for (i = 0; i < 2; i++) { + REG_WRITE_WITH_AUX(SDVOB, bval, j); + REG_READ_WITH_AUX(SDVOB, j); + REG_WRITE_WITH_AUX(SDVOC, cval, j); + REG_READ_WITH_AUX(SDVOC, j); + } } } @@ -402,18 +406,18 @@ static void psb_intel_sdvo_debug_write(struct psb_intel_sdvo *psb_intel_sdvo, u8 DRM_DEBUG_KMS("%s: W: %02X ", SDVO_NAME(psb_intel_sdvo), cmd); for (i = 0; i < args_len; i++) - DRM_LOG_KMS("%02X ", ((u8 *)args)[i]); + DRM_DEBUG_KMS("%02X ", ((u8 *)args)[i]); for (; i < 8; i++) - DRM_LOG_KMS(" "); + DRM_DEBUG_KMS(" "); for (i = 0; i < ARRAY_SIZE(sdvo_cmd_names); i++) { if (cmd == sdvo_cmd_names[i].cmd) { - DRM_LOG_KMS("(%s)", sdvo_cmd_names[i].name); + DRM_DEBUG_KMS("(%s)", sdvo_cmd_names[i].name); break; } } if (i == ARRAY_SIZE(sdvo_cmd_names)) - DRM_LOG_KMS("(%02X)", cmd); - DRM_LOG_KMS("\n"); + DRM_DEBUG_KMS("(%02X)", cmd); + DRM_DEBUG_KMS("\n"); } static const char *cmd_status_names[] = { @@ -498,7 +502,8 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo, &status)) goto log_fail; - while (status == SDVO_CMD_STATUS_PENDING && retry--) { + while ((status == SDVO_CMD_STATUS_PENDING || + status == SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED) && retry--) { udelay(15); if (!psb_intel_sdvo_read_byte(psb_intel_sdvo, SDVO_I2C_CMD_STATUS, @@ -507,9 +512,9 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo, } if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP) - DRM_LOG_KMS("(%s)", cmd_status_names[status]); + DRM_DEBUG_KMS("(%s)", cmd_status_names[status]); else - DRM_LOG_KMS("(??? %d)", status); + DRM_DEBUG_KMS("(??? %d)", status); if (status != SDVO_CMD_STATUS_SUCCESS) goto log_fail; @@ -520,13 +525,13 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo, SDVO_I2C_RETURN_0 + i, &((u8 *)response)[i])) goto log_fail; - DRM_LOG_KMS(" %02X", ((u8 *)response)[i]); + DRM_DEBUG_KMS(" %02X", ((u8 *)response)[i]); } - DRM_LOG_KMS("\n"); + DRM_DEBUG_KMS("\n"); return true; log_fail: - DRM_LOG_KMS("... failed\n"); + DRM_DEBUG_KMS("... failed\n"); return false; } @@ -901,7 +906,7 @@ static bool psb_intel_sdvo_set_tv_format(struct psb_intel_sdvo *psb_intel_sdvo) static bool psb_intel_sdvo_set_output_timings_from_mode(struct psb_intel_sdvo *psb_intel_sdvo, - struct drm_display_mode *mode) + const struct drm_display_mode *mode) { struct psb_intel_sdvo_dtd output_dtd; @@ -918,7 +923,7 @@ psb_intel_sdvo_set_output_timings_from_mode(struct psb_intel_sdvo *psb_intel_sdv static bool psb_intel_sdvo_set_input_timings_for_mode(struct psb_intel_sdvo *psb_intel_sdvo, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { /* Reset the input timing to the screen. Assume always input 0. */ @@ -942,7 +947,7 @@ psb_intel_sdvo_set_input_timings_for_mode(struct psb_intel_sdvo *psb_intel_sdvo, } static bool psb_intel_sdvo_mode_fixup(struct drm_encoder *encoder, - struct drm_display_mode *mode, + const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder); @@ -985,13 +990,14 @@ static void psb_intel_sdvo_mode_set(struct drm_encoder *encoder, { struct drm_device *dev = encoder->dev; struct drm_crtc *crtc = encoder->crtc; - struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc); + struct gma_crtc *gma_crtc = to_gma_crtc(crtc); struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder); u32 sdvox; struct psb_intel_sdvo_in_out_map in_out; struct psb_intel_sdvo_dtd input_dtd; int pixel_multiplier = psb_intel_mode_get_pixel_multiplier(adjusted_mode); int rate; + int need_aux = IS_MRST(dev) ? 1 : 0; if (!mode) return; @@ -1057,7 +1063,11 @@ static void psb_intel_sdvo_mode_set(struct drm_encoder *encoder, return; /* Set the SDVO control regs. */ - sdvox = REG_READ(psb_intel_sdvo->sdvo_reg); + if (need_aux) + sdvox = REG_READ_AUX(psb_intel_sdvo->sdvo_reg); + else + sdvox = REG_READ(psb_intel_sdvo->sdvo_reg); + switch (psb_intel_sdvo->sdvo_reg) { case SDVOB: sdvox &= SDVOB_PRESERVE_MASK; @@ -1068,7 +1078,7 @@ static void psb_intel_sdvo_mode_set(struct drm_encoder *encoder, } sdvox |= (9 << 19) | SDVO_BORDER_ENABLE; - if (psb_intel_crtc->pipe == 1) + if (gma_crtc->pipe == 1) sdvox |= SDVO_PIPE_B_SELECT; if (psb_intel_sdvo->has_hdmi_audio) sdvox |= SDVO_AUDIO_ENABLE; @@ -1087,6 +1097,8 @@ static void psb_intel_sdvo_dpms(struct drm_encoder *encoder, int mode) struct drm_device *dev = encoder->dev; struct psb_intel_sdvo *psb_intel_sdvo = to_psb_intel_sdvo(encoder); u32 temp; + int i; + int need_aux = IS_MRST(dev) ? 1 : 0; switch (mode) { case DRM_MODE_DPMS_ON: @@ -1105,21 +1117,29 @@ static void psb_intel_sdvo_dpms(struct drm_encoder *encoder, int mode) psb_intel_sdvo_set_encoder_power_state(psb_intel_sdvo, mode); if (mode == DRM_MODE_DPMS_OFF) { - temp = REG_READ(psb_intel_sdvo->sdvo_reg); + if (need_aux) + temp = REG_READ_AUX(psb_intel_sdvo->sdvo_reg); + else + temp = REG_READ(psb_intel_sdvo->sdvo_reg); + if ((temp & SDVO_ENABLE) != 0) { psb_intel_sdvo_write_sdvox(psb_intel_sdvo, temp & ~SDVO_ENABLE); } } } else { bool input1, input2; - int i; u8 status; - temp = REG_READ(psb_intel_sdvo->sdvo_reg); + if (need_aux) + temp = REG_READ_AUX(psb_intel_sdvo->sdvo_reg); + else + temp = REG_READ(psb_intel_sdvo->sdvo_reg); + if ((temp & SDVO_ENABLE) == 0) psb_intel_sdvo_write_sdvox(psb_intel_sdvo, temp | SDVO_ENABLE); + for (i = 0; i < 2; i++) - psb_intel_wait_for_vblank(dev); + gma_wait_for_vblank(dev); status = psb_intel_sdvo_get_trained_inputs(psb_intel_sdvo, &input1, &input2); /* Warn if the device reported failure to sync. @@ -1141,7 +1161,6 @@ static void psb_intel_sdvo_dpms(struct drm_encoder *encoder, int mode) static int psb_intel_sdvo_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct drm_psb_private *dev_priv = connector->dev->dev_private; struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector); if (mode->flags & DRM_MODE_FLAG_DBLSCAN) @@ -1161,11 +1180,6 @@ static int psb_intel_sdvo_mode_valid(struct drm_connector *connector, return MODE_PANEL; } - /* We assume worst case scenario of 32 bpp here, since we don't know */ - if ((ALIGN(mode->hdisplay * 4, 64) * mode->vdisplay) > - dev_priv->vram_stolen_size) - return MODE_MEM; - return MODE_OK; } @@ -1298,10 +1312,9 @@ psb_intel_sdvo_get_analog_edid(struct drm_connector *connector) return drm_get_edid(connector, &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter); - 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); @@ -1349,7 +1362,6 @@ psb_intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) } } else status = connector_status_disconnected; - connector->display_info.raw_edid = NULL; kfree(edid); } @@ -1410,7 +1422,6 @@ psb_intel_sdvo_detect(struct drm_connector *connector, bool force) ret = connector_status_disconnected; else ret = connector_status_connected; - connector->display_info.raw_edid = NULL; kfree(edid); } else ret = connector_status_connected; @@ -1459,7 +1470,6 @@ static void psb_intel_sdvo_get_ddc_modes(struct drm_connector *connector) drm_add_edid_modes(connector, edid); } - connector->display_info.raw_edid = NULL; kfree(edid); } } @@ -1705,7 +1715,7 @@ psb_intel_sdvo_set_property(struct drm_connector *connector, uint8_t cmd; int ret; - ret = drm_connector_property_set_value(connector, property, val); + ret = drm_object_property_set_value(&connector->base, property, val); if (ret) return ret; @@ -1760,7 +1770,7 @@ psb_intel_sdvo_set_property(struct drm_connector *connector, } else if (IS_TV_OR_LVDS(psb_intel_sdvo_connector)) { temp_value = val; if (psb_intel_sdvo_connector->left == property) { - drm_connector_property_set_value(connector, + drm_object_property_set_value(&connector->base, psb_intel_sdvo_connector->right, val); if (psb_intel_sdvo_connector->left_margin == temp_value) return 0; @@ -1772,7 +1782,7 @@ psb_intel_sdvo_set_property(struct drm_connector *connector, cmd = SDVO_CMD_SET_OVERSCAN_H; goto set_value; } else if (psb_intel_sdvo_connector->right == property) { - drm_connector_property_set_value(connector, + drm_object_property_set_value(&connector->base, psb_intel_sdvo_connector->left, val); if (psb_intel_sdvo_connector->right_margin == temp_value) return 0; @@ -1784,7 +1794,7 @@ psb_intel_sdvo_set_property(struct drm_connector *connector, cmd = SDVO_CMD_SET_OVERSCAN_H; goto set_value; } else if (psb_intel_sdvo_connector->top == property) { - drm_connector_property_set_value(connector, + drm_object_property_set_value(&connector->base, psb_intel_sdvo_connector->bottom, val); if (psb_intel_sdvo_connector->top_margin == temp_value) return 0; @@ -1796,7 +1806,7 @@ psb_intel_sdvo_set_property(struct drm_connector *connector, cmd = SDVO_CMD_SET_OVERSCAN_V; goto set_value; } else if (psb_intel_sdvo_connector->bottom == property) { - drm_connector_property_set_value(connector, + drm_object_property_set_value(&connector->base, psb_intel_sdvo_connector->top, val); if (psb_intel_sdvo_connector->bottom_margin == temp_value) return 0; @@ -1834,23 +1844,50 @@ done: if (psb_intel_sdvo->base.base.crtc) { struct drm_crtc *crtc = psb_intel_sdvo->base.base.crtc; drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, - crtc->y, crtc->fb); + crtc->y, crtc->primary->fb); } return 0; #undef CHECK_PROPERTY } +static void psb_intel_sdvo_save(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct gma_encoder *gma_encoder = gma_attached_encoder(connector); + struct psb_intel_sdvo *sdvo = to_psb_intel_sdvo(&gma_encoder->base); + + sdvo->saveSDVO = REG_READ(sdvo->sdvo_reg); +} + +static void psb_intel_sdvo_restore(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_encoder *encoder = &gma_attached_encoder(connector)->base; + struct psb_intel_sdvo *sdvo = to_psb_intel_sdvo(encoder); + struct drm_crtc *crtc = encoder->crtc; + + REG_WRITE(sdvo->sdvo_reg, sdvo->saveSDVO); + + /* Force a full mode set on the crtc. We're supposed to have the + mode_config lock already. */ + if (connector->status == connector_status_connected) + drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, + NULL); +} + static const struct drm_encoder_helper_funcs psb_intel_sdvo_helper_funcs = { .dpms = psb_intel_sdvo_dpms, .mode_fixup = psb_intel_sdvo_mode_fixup, - .prepare = psb_intel_encoder_prepare, + .prepare = gma_encoder_prepare, .mode_set = psb_intel_sdvo_mode_set, - .commit = psb_intel_encoder_commit, + .commit = gma_encoder_commit, }; static const struct drm_connector_funcs psb_intel_sdvo_connector_funcs = { .dpms = drm_helper_connector_dpms, + .save = psb_intel_sdvo_save, + .restore = psb_intel_sdvo_restore, .detect = psb_intel_sdvo_detect, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = psb_intel_sdvo_set_property, @@ -1860,7 +1897,7 @@ static const struct drm_connector_funcs psb_intel_sdvo_connector_funcs = { static const struct drm_connector_helper_funcs psb_intel_sdvo_connector_helper_funcs = { .get_modes = psb_intel_sdvo_get_modes, .mode_valid = psb_intel_sdvo_mode_valid, - .best_encoder = psb_intel_best_encoder, + .best_encoder = gma_best_encoder, }; static void psb_intel_sdvo_enc_destroy(struct drm_encoder *encoder) @@ -1872,7 +1909,7 @@ static void psb_intel_sdvo_enc_destroy(struct drm_encoder *encoder) psb_intel_sdvo->sdvo_lvds_fixed_mode); i2c_del_adapter(&psb_intel_sdvo->ddc); - psb_intel_encoder_destroy(encoder); + gma_encoder_destroy(encoder); } static const struct drm_encoder_funcs psb_intel_sdvo_enc_funcs = { @@ -2033,7 +2070,7 @@ psb_intel_sdvo_connector_init(struct psb_intel_sdvo_connector *connector, connector->base.base.doublescan_allowed = 0; connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB; - psb_intel_connector_attach_encoder(&connector->base, &encoder->base); + gma_connector_attach_encoder(&connector->base, &encoder->base); drm_sysfs_connector_add(&connector->base.base); } @@ -2044,8 +2081,7 @@ psb_intel_sdvo_add_hdmi_properties(struct psb_intel_sdvo_connector *connector) struct drm_device *dev = connector->base.base.dev; intel_attach_force_audio_property(&connector->base.base); - if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev)) - intel_attach_broadcast_rgb_property(&connector->base.base); + intel_attach_broadcast_rgb_property(&connector->base.base); */ } @@ -2054,7 +2090,7 @@ psb_intel_sdvo_dvi_init(struct psb_intel_sdvo *psb_intel_sdvo, int device) { struct drm_encoder *encoder = &psb_intel_sdvo->base.base; struct drm_connector *connector; - struct psb_intel_connector *intel_connector; + struct gma_connector *intel_connector; struct psb_intel_sdvo_connector *psb_intel_sdvo_connector; psb_intel_sdvo_connector = kzalloc(sizeof(struct psb_intel_sdvo_connector), GFP_KERNEL); @@ -2094,7 +2130,7 @@ psb_intel_sdvo_tv_init(struct psb_intel_sdvo *psb_intel_sdvo, int type) { struct drm_encoder *encoder = &psb_intel_sdvo->base.base; struct drm_connector *connector; - struct psb_intel_connector *intel_connector; + struct gma_connector *intel_connector; struct psb_intel_sdvo_connector *psb_intel_sdvo_connector; psb_intel_sdvo_connector = kzalloc(sizeof(struct psb_intel_sdvo_connector), GFP_KERNEL); @@ -2133,7 +2169,7 @@ psb_intel_sdvo_analog_init(struct psb_intel_sdvo *psb_intel_sdvo, int device) { struct drm_encoder *encoder = &psb_intel_sdvo->base.base; struct drm_connector *connector; - struct psb_intel_connector *intel_connector; + struct gma_connector *intel_connector; struct psb_intel_sdvo_connector *psb_intel_sdvo_connector; psb_intel_sdvo_connector = kzalloc(sizeof(struct psb_intel_sdvo_connector), GFP_KERNEL); @@ -2167,7 +2203,7 @@ psb_intel_sdvo_lvds_init(struct psb_intel_sdvo *psb_intel_sdvo, int device) { struct drm_encoder *encoder = &psb_intel_sdvo->base.base; struct drm_connector *connector; - struct psb_intel_connector *intel_connector; + struct gma_connector *intel_connector; struct psb_intel_sdvo_connector *psb_intel_sdvo_connector; psb_intel_sdvo_connector = kzalloc(sizeof(struct psb_intel_sdvo_connector), GFP_KERNEL); @@ -2298,7 +2334,7 @@ static bool psb_intel_sdvo_tv_create_property(struct psb_intel_sdvo *psb_intel_s i, tv_format_names[psb_intel_sdvo_connector->tv_format_supported[i]]); psb_intel_sdvo->tv_format_index = psb_intel_sdvo_connector->tv_format_supported[0]; - drm_connector_attach_property(&psb_intel_sdvo_connector->base.base, + drm_object_attach_property(&psb_intel_sdvo_connector->base.base.base, psb_intel_sdvo_connector->tv_format, 0); return true; @@ -2312,11 +2348,9 @@ 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, \ + drm_object_attach_property(&connector->base, \ psb_intel_sdvo_connector->name, \ psb_intel_sdvo_connector->cur_##name); \ DRM_DEBUG_KMS(#name ": max %d, default %d, current %d\n", \ @@ -2349,26 +2383,20 @@ 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, + drm_object_attach_property(&connector->base, 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, + drm_object_attach_property(&connector->base, psb_intel_sdvo_connector->right, psb_intel_sdvo_connector->right_margin); DRM_DEBUG_KMS("h_overscan: max %d, " @@ -2391,26 +2419,20 @@ 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, + drm_object_attach_property(&connector->base, 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, + drm_object_attach_property(&connector->base, psb_intel_sdvo_connector->bottom, psb_intel_sdvo_connector->bottom_margin); DRM_DEBUG_KMS("v_overscan: max %d, " @@ -2438,13 +2460,11 @@ 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, + drm_object_attach_property(&connector->base, psb_intel_sdvo_connector->dot_crawl, psb_intel_sdvo_connector->cur_dot_crawl); DRM_DEBUG_KMS("dot crawl: current %d\n", response); @@ -2535,7 +2555,7 @@ psb_intel_sdvo_init_ddc_proxy(struct psb_intel_sdvo *sdvo, bool psb_intel_sdvo_init(struct drm_device *dev, int sdvo_reg) { struct drm_psb_private *dev_priv = dev->dev_private; - struct psb_intel_encoder *psb_intel_encoder; + struct gma_encoder *gma_encoder; struct psb_intel_sdvo *psb_intel_sdvo; int i; @@ -2552,9 +2572,9 @@ bool psb_intel_sdvo_init(struct drm_device *dev, int sdvo_reg) } /* encoder type will be decided later */ - psb_intel_encoder = &psb_intel_sdvo->base; - psb_intel_encoder->type = INTEL_OUTPUT_SDVO; - drm_encoder_init(dev, &psb_intel_encoder->base, &psb_intel_sdvo_enc_funcs, 0); + gma_encoder = &psb_intel_sdvo->base; + gma_encoder->type = INTEL_OUTPUT_SDVO; + drm_encoder_init(dev, &gma_encoder->base, &psb_intel_sdvo_enc_funcs, 0); /* Read the regs to test if we can talk to the device */ for (i = 0; i < 0x40; i++) { @@ -2572,7 +2592,7 @@ bool psb_intel_sdvo_init(struct drm_device *dev, int sdvo_reg) else dev_priv->hotplug_supported_mask |= SDVOC_HOTPLUG_INT_STATUS; - drm_encoder_helper_add(&psb_intel_encoder->base, &psb_intel_sdvo_helper_funcs); + drm_encoder_helper_add(&gma_encoder->base, &psb_intel_sdvo_helper_funcs); /* In default case sdvo lvds is false */ if (!psb_intel_sdvo_get_capabilities(psb_intel_sdvo, &psb_intel_sdvo->caps)) @@ -2615,7 +2635,7 @@ bool psb_intel_sdvo_init(struct drm_device *dev, int sdvo_reg) return true; err: - drm_encoder_cleanup(&psb_intel_encoder->base); + drm_encoder_cleanup(&gma_encoder->base); i2c_del_adapter(&psb_intel_sdvo->ddc); kfree(psb_intel_sdvo); diff --git a/drivers/gpu/drm/gma500/psb_irq.c b/drivers/gpu/drm/gma500/psb_irq.c index 7be802baceb..624eb36511c 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)) { @@ -188,6 +190,9 @@ static void mid_pipe_event_handler(struct drm_device *dev, int pipe) */ static void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat) { + if (vdc_stat & _PSB_IRQ_ASLE) + psb_intel_opregion_asle_intr(dev); + if (vdc_stat & _PSB_VSYNC_PIPEA_FLAG) mid_pipe_event_handler(dev, 0); @@ -195,20 +200,71 @@ static void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat) mid_pipe_event_handler(dev, 1); } -irqreturn_t psb_irq_handler(DRM_IRQ_ARGS) +/* + * SGX interrupt handler + */ +static void psb_sgx_interrupt(struct drm_device *dev, u32 stat_1, u32 stat_2) { - struct drm_device *dev = (struct drm_device *) arg; - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; + struct drm_psb_private *dev_priv = dev->dev_private; + u32 val, addr; + int error = false; + + if (stat_1 & _PSB_CE_TWOD_COMPLETE) + val = PSB_RSGX32(PSB_CR_2D_BLIT_STATUS); + + if (stat_2 & _PSB_CE2_BIF_REQUESTER_FAULT) { + val = PSB_RSGX32(PSB_CR_BIF_INT_STAT); + addr = PSB_RSGX32(PSB_CR_BIF_FAULT); + if (val) { + if (val & _PSB_CBI_STAT_PF_N_RW) + DRM_ERROR("SGX MMU page fault:"); + else + DRM_ERROR("SGX MMU read / write protection fault:"); + + if (val & _PSB_CBI_STAT_FAULT_CACHE) + DRM_ERROR("\tCache requestor"); + if (val & _PSB_CBI_STAT_FAULT_TA) + DRM_ERROR("\tTA requestor"); + if (val & _PSB_CBI_STAT_FAULT_VDM) + DRM_ERROR("\tVDM requestor"); + if (val & _PSB_CBI_STAT_FAULT_2D) + DRM_ERROR("\t2D requestor"); + if (val & _PSB_CBI_STAT_FAULT_PBE) + DRM_ERROR("\tPBE requestor"); + if (val & _PSB_CBI_STAT_FAULT_TSP) + DRM_ERROR("\tTSP requestor"); + if (val & _PSB_CBI_STAT_FAULT_ISP) + DRM_ERROR("\tISP requestor"); + if (val & _PSB_CBI_STAT_FAULT_USSEPDS) + DRM_ERROR("\tUSSEPDS requestor"); + if (val & _PSB_CBI_STAT_FAULT_HOST) + DRM_ERROR("\tHost requestor"); + + DRM_ERROR("\tMMU failing address is 0x%08x.\n", + (unsigned int)addr); + error = true; + } + } - uint32_t vdc_stat, dsp_int = 0, sgx_int = 0; + /* Clear bits */ + PSB_WSGX32(stat_1, PSB_CR_EVENT_HOST_CLEAR); + PSB_WSGX32(stat_2, PSB_CR_EVENT_HOST_CLEAR2); + PSB_RSGX32(PSB_CR_EVENT_HOST_CLEAR2); +} + +irqreturn_t psb_irq_handler(int irq, void *arg) +{ + struct drm_device *dev = arg; + struct drm_psb_private *dev_priv = dev->dev_private; + uint32_t vdc_stat, dsp_int = 0, sgx_int = 0, hotplug_int = 0; + u32 sgx_stat_1, sgx_stat_2; int handled = 0; spin_lock(&dev_priv->irqmask_lock); vdc_stat = PSB_RVDC32(PSB_INT_IDENTITY_R); - if (vdc_stat & _PSB_PIPE_EVENT_FLAG) + if (vdc_stat & (_PSB_PIPE_EVENT_FLAG|_PSB_IRQ_ASLE)) dsp_int = 1; /* FIXME: Handle Medfield @@ -218,6 +274,8 @@ irqreturn_t psb_irq_handler(DRM_IRQ_ARGS) if (vdc_stat & _PSB_IRQ_SGX_FLAG) sgx_int = 1; + if (vdc_stat & _PSB_IRQ_DISP_HOTSYNC) + hotplug_int = 1; vdc_stat &= dev_priv->vdc_irq_mask; spin_unlock(&dev_priv->irqmask_lock); @@ -228,20 +286,22 @@ irqreturn_t psb_irq_handler(DRM_IRQ_ARGS) } if (sgx_int) { - /* Not expected - we have it masked, shut it up */ - u32 s, s2; - s = PSB_RSGX32(PSB_CR_EVENT_STATUS); - s2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2); - PSB_WSGX32(s, PSB_CR_EVENT_HOST_CLEAR); - PSB_WSGX32(s2, PSB_CR_EVENT_HOST_CLEAR2); - /* if s & _PSB_CE_TWOD_COMPLETE we have 2D done but - we may as well poll even if we add that ! */ + sgx_stat_1 = PSB_RSGX32(PSB_CR_EVENT_STATUS); + sgx_stat_2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2); + psb_sgx_interrupt(dev, sgx_stat_1, sgx_stat_2); handled = 1; } + /* Note: this bit has other meanings on some devices, so we will + need to address that later if it ever matters */ + if (hotplug_int && dev_priv->ops->hotplug) { + handled = dev_priv->ops->hotplug(dev); + REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT)); + } + PSB_WVDC32(vdc_stat, PSB_INT_IDENTITY_R); (void) PSB_RVDC32(PSB_INT_IDENTITY_R); - DRM_READMEMORYBARRIER(); + rmb(); if (!handled) return IRQ_NONE; @@ -257,20 +317,30 @@ void psb_irq_preinstall(struct drm_device *dev) spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); - if (gma_power_is_on(dev)) + if (gma_power_is_on(dev)) { PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); - if (dev->vblank_enabled[0]) + PSB_WVDC32(0x00000000, PSB_INT_MASK_R); + PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R); + PSB_WSGX32(0x00000000, PSB_CR_EVENT_HOST_ENABLE); + PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); + } + if (dev->vblank[0].enabled) dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG; - if (dev->vblank_enabled[1]) + if (dev->vblank[1].enabled) dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG; /* FIXME: Handle Medfield irq mask - if (dev->vblank_enabled[1]) + if (dev->vblank[1].enabled) dev_priv->vdc_irq_mask |= _MDFLD_PIPEB_EVENT_FLAG; - if (dev->vblank_enabled[2]) + if (dev->vblank[2].enabled) dev_priv->vdc_irq_mask |= _MDFLD_PIPEC_EVENT_FLAG; */ + /* Revisit this area - want per device masks ? */ + if (dev_priv->ops->hotplug) + dev_priv->vdc_irq_mask |= _PSB_IRQ_DISP_HOTSYNC; + dev_priv->vdc_irq_mask |= _PSB_IRQ_ASLE | _PSB_IRQ_SGX_FLAG; + /* This register is safe even if display island is off */ PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R); spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); @@ -278,52 +348,61 @@ void psb_irq_preinstall(struct drm_device *dev) int psb_irq_postinstall(struct drm_device *dev) { - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; + struct drm_psb_private *dev_priv = dev->dev_private; unsigned long irqflags; spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + /* Enable 2D and MMU fault interrupts */ + PSB_WSGX32(_PSB_CE2_BIF_REQUESTER_FAULT, PSB_CR_EVENT_HOST_ENABLE2); + PSB_WSGX32(_PSB_CE_TWOD_COMPLETE, PSB_CR_EVENT_HOST_ENABLE); + PSB_RSGX32(PSB_CR_EVENT_HOST_ENABLE); /* Post */ + /* This register is safe even if display island is off */ PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R); PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); - if (dev->vblank_enabled[0]) + if (dev->vblank[0].enabled) psb_enable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE); else psb_disable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE); - if (dev->vblank_enabled[1]) + if (dev->vblank[1].enabled) psb_enable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE); else psb_disable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE); - if (dev->vblank_enabled[2]) + if (dev->vblank[2].enabled) psb_enable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE); else psb_disable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE); + if (dev_priv->ops->hotplug_enable) + dev_priv->ops->hotplug_enable(dev, true); + spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags); return 0; } void psb_irq_uninstall(struct drm_device *dev) { - struct drm_psb_private *dev_priv = - (struct drm_psb_private *) dev->dev_private; + struct drm_psb_private *dev_priv = dev->dev_private; unsigned long irqflags; spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); + if (dev_priv->ops->hotplug_enable) + dev_priv->ops->hotplug_enable(dev, false); + PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM); - if (dev->vblank_enabled[0]) + if (dev->vblank[0].enabled) psb_disable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE); - if (dev->vblank_enabled[1]) + if (dev->vblank[1].enabled) psb_disable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE); - if (dev->vblank_enabled[2]) + if (dev->vblank[2].enabled) psb_disable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE); dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG | @@ -404,7 +483,7 @@ void psb_irq_turn_off_dpst(struct drm_device *dev) psb_disable_pipestat(dev_priv, 0, PIPE_DPST_EVENT_ENABLE); pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); - PSB_WVDC32(pwm_reg & !(PWM_PHASEIN_INT_ENABLE), + PSB_WVDC32(pwm_reg & ~PWM_PHASEIN_INT_ENABLE, PWM_CONTROL_LOGIC); pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC); @@ -428,21 +507,6 @@ int psb_irq_disable_dpst(struct drm_device *dev) return 0; } -#ifdef PSB_FIXME -static int psb_vblank_do_wait(struct drm_device *dev, - unsigned int *sequence, atomic_t *counter) -{ - unsigned int cur_vblank; - int ret = 0; - DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, - (((cur_vblank = atomic_read(counter)) - - *sequence) <= (1 << 23))); - *sequence = cur_vblank; - - return ret; -} -#endif - /* * It is used to enable VBLANK interrupt */ @@ -453,6 +517,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 +554,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 +570,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 216fda38b57..d0b45ffa112 100644 --- a/drivers/gpu/drm/gma500/psb_irq.h +++ b/drivers/gpu/drm/gma500/psb_irq.h @@ -21,8 +21,8 @@ * **************************************************************************/ -#ifndef _SYSIRQ_H_ -#define _SYSIRQ_H_ +#ifndef _PSB_IRQ_H_ +#define _PSB_IRQ_H_ #include <drm/drmP.h> @@ -32,7 +32,7 @@ void sysirq_uninit(struct drm_device *dev); void psb_irq_preinstall(struct drm_device *dev); int psb_irq_postinstall(struct drm_device *dev); void psb_irq_uninstall(struct drm_device *dev); -irqreturn_t psb_irq_handler(DRM_IRQ_ARGS); +irqreturn_t psb_irq_handler(int irq, void *arg); int psb_irq_enable_dpst(struct drm_device *dev); int psb_irq_disable_dpst(struct drm_device *dev); @@ -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); -#endif /* _SYSIRQ_H_ */ +int mdfld_enable_te(struct drm_device *dev, int pipe); +void mdfld_disable_te(struct drm_device *dev, int pipe); +#endif /* _PSB_IRQ_H_ */ diff --git a/drivers/gpu/drm/gma500/psb_lid.c b/drivers/gpu/drm/gma500/psb_lid.c index b867aabe6bf..1d2ebb5e530 100644 --- a/drivers/gpu/drm/gma500/psb_lid.c +++ b/drivers/gpu/drm/gma500/psb_lid.c @@ -29,7 +29,7 @@ static void psb_lid_timer_func(unsigned long data) struct drm_device *dev = (struct drm_device *)dev_priv->dev; struct timer_list *lid_timer = &dev_priv->lid_timer; unsigned long irq_flags; - u32 *lid_state = dev_priv->lid_state; + u32 __iomem *lid_state = dev_priv->opregion.lid_state; u32 pp_status; if (readl(lid_state) == dev_priv->lid_last_state) @@ -40,10 +40,16 @@ static void psb_lid_timer_func(unsigned long data) REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | POWER_TARGET_ON); do { pp_status = REG_READ(PP_STATUS); - } while ((pp_status & PP_ON) == 0); + } while ((pp_status & PP_ON) == 0 && + (pp_status & PP_SEQUENCE_MASK) != 0); - /*FIXME: should be backlight level before*/ - psb_intel_lvds_set_brightness(dev, 100); + if (REG_READ(PP_STATUS) & PP_ON) { + /*FIXME: should be backlight level before*/ + psb_intel_lvds_set_brightness(dev, 100); + } else { + DRM_DEBUG("LVDS panel never powered up"); + return; + } } else { psb_intel_lvds_set_brightness(dev, 0); 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 00000000000..771ff66711a --- /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 = 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 = 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 00000000000..b14b7f9e7d1 --- /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__*/ |
