OSDN Git Service

gma500: Add Poulsbo support
authorAlan Cox <alan@linux.intel.com>
Thu, 3 Nov 2011 18:22:15 +0000 (18:22 +0000)
committerDave Airlie <airlied@redhat.com>
Wed, 16 Nov 2011 11:26:55 +0000 (11:26 +0000)
This provides the specific code for Poulsbo, some of which is also used for
the later chipsets. We support the GTT, the 2D engine (for console), and
the display setup/management. We do not support 3D or the video overlays.

In theory enough public info is available to do the video overlay work
but that represents a large task.

Framebuffer X will run nicely with this but do *NOT* use the VESA X
server at the same time as KMS. With a Dell mini 10 things like Xfce4 are
nice and usable even when compositing as the CPU has a good path to the
memory.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/gma500/psb_device.c [new file with mode: 0644]
drivers/gpu/drm/gma500/psb_gfx.mod.c [new file with mode: 0644]
drivers/gpu/drm/gma500/psb_intel_display.c [new file with mode: 0644]
drivers/gpu/drm/gma500/psb_intel_display.h [new file with mode: 0644]
drivers/gpu/drm/gma500/psb_intel_drv.h [new file with mode: 0644]
drivers/gpu/drm/gma500/psb_intel_lvds.c [new file with mode: 0644]
drivers/gpu/drm/gma500/psb_intel_modes.c [new file with mode: 0644]
drivers/gpu/drm/gma500/psb_intel_reg.h [new file with mode: 0644]
drivers/gpu/drm/gma500/psb_intel_sdvo.c [new file with mode: 0644]
drivers/gpu/drm/gma500/psb_intel_sdvo_regs.h [new file with mode: 0644]
drivers/gpu/drm/gma500/psb_lid.c [new file with mode: 0644]

diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c
new file mode 100644 (file)
index 0000000..4659132
--- /dev/null
@@ -0,0 +1,353 @@
+/**************************************************************************
+ * 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 <linux/backlight.h>
+#include <drm/drmP.h>
+#include <drm/drm.h>
+#include "psb_drm.h"
+#include "psb_drv.h"
+#include "psb_reg.h"
+#include "psb_intel_reg.h"
+#include "intel_bios.h"
+
+
+static int psb_output_init(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       psb_intel_lvds_init(dev, &dev_priv->mode_dev);
+       psb_intel_sdvo_init(dev, SDVOB);
+       return 0;
+}
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+
+/*
+ *     Poulsbo Backlight 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 psb_brightness;
+static struct backlight_device *psb_backlight_device;
+
+static int psb_get_brightness(struct backlight_device *bd)
+{
+       /* return locally cached var instead of HW read (due to DPST etc.) */
+       /* FIXME: ideally return actual value in case firmware fiddled with
+          it */
+       return psb_brightness;
+}
+
+
+static int psb_backlight_setup(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;
+       }
+       bl_max_freq = dev_priv->lvds_bl->freq;
+       blc_pwm_precision_factor = PSB_BLC_PWM_PRECISION_FACTOR;
+
+       core_clock = dev_priv->core_freq;
+
+       value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT;
+       value *= blc_pwm_precision_factor;
+       value /= bl_max_freq;
+       value /= blc_pwm_precision_factor;
+
+       if (value > (unsigned long long)PSB_BLC_MAX_PWM_REG_FREQ ||
+                value < (unsigned long long)PSB_BLC_MIN_PWM_REG_FREQ)
+                               return -ERANGE;
+       else {
+               value &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR;
+               REG_WRITE(BLC_PWM_CTL,
+                       (value << PSB_BACKLIGHT_PWM_CTL_SHIFT) | (value));
+       }
+       return 0;
+}
+
+static int psb_set_brightness(struct backlight_device *bd)
+{
+       struct drm_device *dev = bl_get_data(psb_backlight_device);
+       int level = bd->props.brightness;
+
+       /* Percentage 1-100% being valid */
+       if (level < 1)
+               level = 1;
+
+       psb_intel_lvds_set_brightness(dev, level);
+       psb_brightness = level;
+       return 0;
+}
+
+static const struct backlight_ops psb_ops = {
+       .get_brightness = psb_get_brightness,
+       .update_status  = psb_set_brightness,
+};
+
+static int psb_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));
+       props.max_brightness = 100;
+       props.type = BACKLIGHT_PLATFORM;
+
+       psb_backlight_device = backlight_device_register("psb-bl",
+                                       NULL, (void *)dev, &psb_ops, &props);
+       if (IS_ERR(psb_backlight_device))
+               return PTR_ERR(psb_backlight_device);
+
+       ret = psb_backlight_setup(dev);
+       if (ret < 0) {
+               backlight_device_unregister(psb_backlight_device);
+               psb_backlight_device = NULL;
+               return ret;
+       }
+       psb_backlight_device->props.brightness = 100;
+       psb_backlight_device->props.max_brightness = 100;
+       backlight_update_status(psb_backlight_device);
+       dev_priv->backlight_device = psb_backlight_device;
+       return 0;
+}
+
+#endif
+
+/*
+ *     Provide the Poulsbo specific chip logic and low level methods
+ *     for power management
+ */
+
+static void psb_init_pm(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+
+       u32 gating = PSB_RSGX32(PSB_CR_CLKGATECTL);
+       gating &= ~3;   /* Disable 2D clock gating */
+       gating |= 1;
+       PSB_WSGX32(gating, PSB_CR_CLKGATECTL);
+       PSB_RSGX32(PSB_CR_CLKGATECTL);
+}
+
+/**
+ *     psb_save_display_registers      -       save registers lost on suspend
+ *     @dev: our DRM device
+ *
+ *     Save the state we need in order to be able to restore the interface
+ *     upon resume from suspend
+ */
+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;
+
+       /* 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);
+
+       /* Save crtc and output state */
+       mutex_lock(&dev->mode_config.mutex);
+       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);
+
+       mutex_unlock(&dev->mode_config.mutex);
+       return 0;
+}
+
+/**
+ *     psb_restore_display_registers   -       restore lost register state
+ *     @dev: our DRM device
+ *
+ *     Restore register state that was lost during suspend and resume.
+ */
+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;
+       int pp_stat;
+
+       /* 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);
+
+       /*make sure VGA plane is off. it initializes to on after reset!*/
+       PSB_WVDC32(0x80000000, VGACNTRL);
+
+       mutex_lock(&dev->mode_config.mutex);
+       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);
+
+       mutex_unlock(&dev->mode_config.mutex);
+
+       if (dev_priv->iLVDS_enable) {
+               /*shutdown the panel*/
+               PSB_WVDC32(0, PP_CONTROL);
+               do {
+                       pp_stat = PSB_RVDC32(PP_STATUS);
+               } while (pp_stat & 0x80000000);
+
+               /* Turn off the plane */
+               PSB_WVDC32(0x58000000, DSPACNTR);
+               PSB_WVDC32(0, DSPASURF);/*trigger the plane disable*/
+               /* Wait ~4 ticks */
+               msleep(4);
+               /* Turn off pipe */
+               PSB_WVDC32(0x0, PIPEACONF);
+               /* Wait ~8 ticks */
+               msleep(8);
+
+               /* Turn off PLLs */
+               PSB_WVDC32(0, MRST_DPLL_A);
+       } else {
+               PSB_WVDC32(DPI_SHUT_DOWN, DPI_CONTROL_REG);
+               PSB_WVDC32(0x0, PIPEACONF);
+               PSB_WVDC32(0x2faf0000, BLC_PWM_CTL);
+               while (REG_READ(0x70008) & 0x40000000)
+                       cpu_relax();
+               while ((PSB_RVDC32(GEN_FIFO_STAT_REG) & DPI_FIFO_EMPTY)
+                       != DPI_FIFO_EMPTY)
+                       cpu_relax();
+               PSB_WVDC32(0, DEVICE_READY_REG);
+       }
+       return 0;
+}
+
+static int psb_power_down(struct drm_device *dev)
+{
+       return 0;
+}
+
+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;
+       }
+}
+
+static int psb_chip_setup(struct drm_device *dev)
+{
+       psb_get_core_freq(dev);
+       gma_intel_opregion_init(dev);
+       psb_intel_init_bios(dev);
+       return 0;
+}
+
+const struct psb_ops psb_chip_ops = {
+       .name = "Poulsbo",
+       .accel_2d = 1,
+       .pipes = 2,
+       .crtcs = 2,
+       .sgx_offset = PSB_SGX_OFFSET,
+       .chip_setup = psb_chip_setup,
+
+       .crtc_helper = &psb_intel_helper_funcs,
+       .crtc_funcs = &psb_intel_crtc_funcs,
+
+       .output_init = psb_output_init,
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+       .backlight_init = psb_backlight_init,
+#endif
+
+       .init_pm = psb_init_pm,
+       .save_regs = psb_save_display_registers,
+       .restore_regs = psb_restore_display_registers,
+       .power_down = psb_power_down,
+       .power_up = psb_power_up,
+};
+
diff --git a/drivers/gpu/drm/gma500/psb_gfx.mod.c b/drivers/gpu/drm/gma500/psb_gfx.mod.c
new file mode 100644 (file)
index 0000000..3c0bfdf
--- /dev/null
@@ -0,0 +1,51 @@
+#include <linux/module.h>
+#include <linux/vermagic.h>
+#include <linux/compiler.h>
+
+MODULE_INFO(vermagic, VERMAGIC_STRING);
+
+struct module __this_module
+__attribute__((section(".gnu.linkonce.this_module"))) = {
+ .name = KBUILD_MODNAME,
+ .init = init_module,
+#ifdef CONFIG_MODULE_UNLOAD
+ .exit = cleanup_module,
+#endif
+ .arch = MODULE_ARCH_INIT,
+};
+
+MODULE_INFO(staging, "Y");
+
+static const char __module_depends[]
+__used
+__attribute__((section(".modinfo"))) =
+"depends=drm,drm_kms_helper,video,i2c-algo-bit";
+
+MODULE_ALIAS("pci:v00008086d00008108sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00008109sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00004100sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00004101sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00004102sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00004103sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00004104sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00004105sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00004106sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00004107sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000130sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000131sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000132sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000133sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000134sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000135sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000136sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000137sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000BE0sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000BE1sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000BE2sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000BE3sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000BE4sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000BE5sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000BE6sv*sd*bc*sc*i*");
+MODULE_ALIAS("pci:v00008086d00000BE7sv*sd*bc*sc*i*");
+
+MODULE_INFO(srcversion, "51B3334F342F5EEAC93AF22");
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c
new file mode 100644 (file)
index 0000000..ab65076
--- /dev/null
@@ -0,0 +1,1431 @@
+/*
+ * 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>
+ */
+
+#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 "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_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},
+        },
+       {                       /* 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},
+        },
+       {                       /* 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},
+        /* 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},
+        },
+};
+
+static const struct psb_intel_limit_t *psb_intel_limit(struct drm_crtc *crtc)
+{
+       const struct psb_intel_limit_t *limit;
+
+       if (psb_intel_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)
+{
+       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;
+}
+
+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_output *psb_intel_output =
+                           to_psb_intel_output(l_entry);
+                       if (psb_intel_output->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->pitch + x * (crtc->fb->bits_per_pixel / 8);
+
+       REG_WRITE(dspstride, crtc->fb->pitch);
+
+       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);
+}
+
+static bool psb_intel_crtc_mode_fixup(struct drm_crtc *crtc,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+
+/**
+ * Return the pipe currently connected to the panel fitter,
+ * or -1 if the panel fitter is not present or not in use
+ */
+static int psb_intel_panel_fitter_pipe(struct drm_device *dev)
+{
+       u32 pfit_control;
+
+       pfit_control = REG_READ(PFIT_CONTROL);
+
+       /* See if the panel fitter is in use */
+       if ((pfit_control & PFIT_ENABLE) == 0)
+               return -1;
+       /* Must be on PIPE 1 for PSB */
+       return 1;
+}
+
+static int psb_intel_crtc_mode_set(struct drm_crtc *crtc,
+                              struct drm_display_mode *mode,
+                              struct drm_display_mode *adjusted_mode,
+                              int x, int y,
+                              struct drm_framebuffer *old_fb)
+{
+       struct drm_device *dev = crtc->dev;
+       struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
+       struct drm_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 refclk;
+       struct psb_intel_clock_t clock;
+       u32 dpll = 0, fp = 0, dspcntr, pipeconf;
+       bool ok, is_sdvo = false, is_dvo = false;
+       bool is_crt = false, is_lvds = false, is_tv = false;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct drm_connector *connector;
+
+       /* No scan out no play */
+       if (crtc->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_output *psb_intel_output =
+                   to_psb_intel_output(connector);
+
+               if (!connector->encoder
+                   || connector->encoder->crtc != crtc)
+                       continue;
+
+               switch (psb_intel_output->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,
+                                &clock);
+       if (!ok) {
+               dev_err(dev->dev, "Couldn't find PLL settings for mode!\n");
+               return 0;
+       }
+
+       fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+
+       dpll = DPLL_VGA_MODE_DIS;
+       if (is_lvds) {
+               dpll |= DPLLB_MODE_LVDS;
+               dpll |= DPLL_DVO_HIGH_SPEED;
+       } else
+               dpll |= DPLLB_MODE_DAC_SERIAL;
+       if (is_sdvo) {
+               int sdvo_pixel_multiply =
+                           adjusted_mode->clock / mode->clock;
+               dpll |= DPLL_DVO_HIGH_SPEED;
+               dpll |=
+                   (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
+       }
+
+       /* compute bitmask from p1 value */
+       dpll |= (1 << (clock.p1 - 1)) << 16;
+       switch (clock.p2) {
+       case 5:
+               dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
+               break;
+       case 7:
+               dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
+               break;
+       case 10:
+               dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
+               break;
+       case 14:
+               dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
+               break;
+       }
+
+       if (is_tv) {
+               /* XXX: just matching BIOS for now */
+/*     dpll |= PLL_REF_INPUT_TVCLKINBC; */
+               dpll |= 3;
+       }
+       dpll |= PLL_REF_INPUT_DREFCLK;
+
+       /* setup pipeconf */
+       pipeconf = REG_READ(pipeconf_reg);
+
+       /* Set up the display plane register */
+       dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+       if (pipe == 0)
+               dspcntr |= DISPPLANE_SEL_PIPE_A;
+       else
+               dspcntr |= DISPPLANE_SEL_PIPE_B;
+
+       dspcntr |= DISPLAY_PLANE_ENABLE;
+       pipeconf |= PIPEACONF_ENABLE;
+       dpll |= DPLL_VCO_ENABLE;
+
+
+       /* Disable the panel fitter if it was on our pipe */
+       if (psb_intel_panel_fitter_pipe(dev) == pipe)
+               REG_WRITE(PFIT_CONTROL, 0);
+
+       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);
+               udelay(150);
+       }
+
+       /* The LVDS pin pair needs to be on before the DPLLs are enabled.
+        * This is an exception to the general rule that mode_set doesn't turn
+        * things on.
+        */
+       if (is_lvds) {
+               u32 lvds = REG_READ(LVDS);
+
+               lvds &= ~LVDS_PIPEB_SELECT;
+               if (pipe == 1)
+                       lvds |= LVDS_PIPEB_SELECT;
+
+               lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
+               /* Set the B0-B3 data pairs corresponding to
+                * whether we're going to
+                * set the DPLLs for dual-channel mode or not.
+                */
+               lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
+               if (clock.p2 == 7)
+                       lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
+
+               /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
+                * appropriately here, but we need to look more
+                * thoroughly into how panels behave in the two modes.
+                */
+
+               REG_WRITE(LVDS, lvds);
+               REG_READ(LVDS);
+       }
+
+       REG_WRITE(fp_reg, fp);
+       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(dpll_reg, dpll);
+
+       REG_READ(dpll_reg);
+       /* Wait for the clocks to stabilize. */
+       udelay(150);
+
+       REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
+                 ((adjusted_mode->crtc_htotal - 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(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));
+       /* pipesrc and dspsize control the size that is scaled from,
+        * which should always be the user's requested size.
+        */
+       REG_WRITE(dspsize_reg,
+                 ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
+       REG_WRITE(dsppos_reg, 0);
+       REG_WRITE(pipesrc_reg,
+                 ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+       REG_WRITE(pipeconf_reg, pipeconf);
+       REG_READ(pipeconf_reg);
+
+       psb_intel_wait_for_vblank(dev);
+
+       REG_WRITE(dspcntr_reg, 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);
+
+       /* 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;
+       u32 dpll;
+       u32 fp;
+       struct psb_intel_clock_t clock;
+       bool is_lvds;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+
+       if (gma_power_begin(dev, false)) {
+               dpll = REG_READ((pipe == 0) ? DPLL_A : DPLL_B);
+               if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
+                       fp = REG_READ((pipe == 0) ? FPA0 : FPB0);
+               else
+                       fp = REG_READ((pipe == 0) ? FPA1 : FPB1);
+               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;
+
+               if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
+                       fp = (pipe == 0) ?
+                               dev_priv->saveFPA0 :
+                               dev_priv->saveFPB0;
+               else
+                       fp = (pipe == 0) ?
+                               dev_priv->saveFPA1 :
+                               dev_priv->saveFPB1;
+
+               is_lvds = (pipe == 1) && (dev_priv->saveLVDS & LVDS_PORT_EN);
+       }
+
+       clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
+       clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
+       clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
+
+       if (is_lvds) {
+               clock.p1 =
+                   ffs((dpll &
+                        DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >>
+                       DPLL_FPA01_P1_POST_DIV_SHIFT);
+               clock.p2 = 14;
+
+               if ((dpll & PLL_REF_INPUT_MASK) ==
+                   PLLB_REF_INPUT_SPREADSPECTRUMIN) {
+                       /* XXX: might not be 66MHz */
+                       i8xx_clock(66000, &clock);
+               } else
+                       i8xx_clock(48000, &clock);
+       } else {
+               if (dpll & PLL_P1_DIVIDE_BY_TWO)
+                       clock.p1 = 2;
+               else {
+                       clock.p1 =
+                           ((dpll &
+                             DPLL_FPA01_P1_POST_DIV_MASK_I830) >>
+                            DPLL_FPA01_P1_POST_DIV_SHIFT) + 2;
+               }
+               if (dpll & PLL_P2_DIVIDE_BY_4)
+                       clock.p2 = 4;
+               else
+                       clock.p2 = 2;
+
+               i8xx_clock(48000, &clock);
+       }
+
+       /* XXX: It would be nice to validate the clocks, but we can't reuse
+        * i830PllIsValid() because it relies on the xf86_config connector
+        * configuration being accurate, which it isn't necessarily.
+        */
+
+       return clock.dot;
+}
+
+/** Returns the currently programmed mode of the given pipe. */
+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 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);
+               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;
+       }
+
+       mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+       if (!mode)
+               return NULL;
+
+       mode->clock = psb_intel_crtc_clock_get(dev, crtc);
+       mode->hdisplay = (htot & 0xffff) + 1;
+       mode->htotal = ((htot & 0xffff0000) >> 16) + 1;
+       mode->hsync_start = (hsync & 0xffff) + 1;
+       mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1;
+       mode->vdisplay = (vtot & 0xffff) + 1;
+       mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1;
+       mode->vsync_start = (vsync & 0xffff) + 1;
+       mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1;
+
+       drm_mode_set_name(mode);
+       drm_mode_set_crtcinfo(mode, 0);
+
+       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,
+       .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,
+};
+
+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,
+};
+
+/*
+ * 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)
+{
+       u32 control[3] = { CURACNTR, CURBCNTR, CURCCNTR };
+       u32 base[3] = { CURABASE, CURBBASE, CURCBASE };
+
+       REG_WRITE(control[pipe], 0);
+       REG_WRITE(base[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;
+       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)
+               return;
+
+       psb_intel_crtc->crtc_state =
+               kzalloc(sizeof(struct psb_intel_crtc_state), GFP_KERNEL);
+       if (!psb_intel_crtc->crtc_state) {
+               dev_err(dev->dev, "Crtc state error: No memory\n");
+               kfree(psb_intel_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_mode_crtc_set_gamma_size(&psb_intel_crtc->base, 256);
+       psb_intel_crtc->pipe = pipe;
+       psb_intel_crtc->plane = pipe;
+
+       r_base = psb_intel_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;
+               r_base[i] = i << 8;
+               g_base[i] = i << 8;
+               b_base[i] = i << 8;
+
+               psb_intel_crtc->lut_adj[i] = 0;
+       }
+
+       psb_intel_crtc->mode_dev = mode_dev;
+       psb_intel_crtc->cursor_addr = 0;
+
+       drm_crtc_helper_add(&psb_intel_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;
+       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;
+       }
+
+       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;
+}
+
+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)
+                       break;
+       }
+       return crtc;
+}
+
+int psb_intel_connector_clones(struct drm_device *dev, int type_mask)
+{
+       int index_mask = 0;
+       struct drm_connector *connector;
+       int entry = 0;
+
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           head) {
+               struct psb_intel_output *psb_intel_output =
+                   to_psb_intel_output(connector);
+               if (type_mask & (1 << psb_intel_output->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_output *psb_intel_output =
+                                       to_psb_intel_output(connector);
+
+       return &psb_intel_output->enc;
+}
+
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.h b/drivers/gpu/drm/gma500/psb_intel_display.h
new file mode 100644 (file)
index 0000000..535b49a
--- /dev/null
@@ -0,0 +1,28 @@
+/* copyright (c) 2008, 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>
+ */
+
+#ifndef _INTEL_DISPLAY_H_
+#define _INTEL_DISPLAY_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);
+
+#endif
diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h
new file mode 100644 (file)
index 0000000..ac953ca
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2009-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.
+ *
+ */
+
+#ifndef __INTEL_DRV_H__
+#define __INTEL_DRV_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/gpio.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
+
+/* these are outputs from the chip - integrated only
+ * external chips are via DVO or SDVO output */
+#define INTEL_OUTPUT_UNUSED 0
+#define INTEL_OUTPUT_ANALOG 1
+#define INTEL_OUTPUT_DVO 2
+#define INTEL_OUTPUT_SDVO 3
+#define INTEL_OUTPUT_LVDS 4
+#define INTEL_OUTPUT_TVOUT 5
+#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
+
+/*
+ * Hold information useally put on the device driver privates here,
+ * since it needs to be shared across multiple of devices drivers privates.
+ */
+struct psb_intel_mode_device {
+
+       /*
+        * Abstracted memory manager operations
+        */
+        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 */
+       bool panel_wants_dither;
+       struct drm_display_mode *panel_fixed_mode;
+       struct drm_display_mode *panel_fixed_mode2;
+       struct drm_display_mode *vbt_mode;      /* if any */
+
+       uint32_t saveBLC_PWM_CTL;
+};
+
+struct psb_intel_i2c_chan {
+       /* for getting at dev. private (mmio etc.) */
+       struct drm_device *drm_dev;
+       u32 reg;                /* GPIO reg */
+       struct i2c_adapter adapter;
+       struct i2c_algo_bit_data algo;
+       u8 slave_addr;
+};
+
+struct psb_intel_output {
+       struct drm_connector base;
+
+       struct drm_encoder enc;
+       int type;
+
+       struct psb_intel_i2c_chan *i2c_bus;     /* for control functions */
+       struct psb_intel_i2c_chan *ddc_bus;     /* for DDC only stuff */
+       bool load_detect_temp;
+       void *dev_priv;
+
+       struct psb_intel_mode_device *mode_dev;
+       struct i2c_adapter *hdmi_i2c_adapter;   /* for control functions */
+};
+
+struct psb_intel_crtc_state {
+       uint32_t saveDSPCNTR;
+       uint32_t savePIPECONF;
+       uint32_t savePIPESRC;
+       uint32_t saveDPLL;
+       uint32_t saveFP0;
+       uint32_t saveFP1;
+       uint32_t saveHTOTAL;
+       uint32_t saveHBLANK;
+       uint32_t saveHSYNC;
+       uint32_t saveVTOTAL;
+       uint32_t saveVBLANK;
+       uint32_t saveVSYNC;
+       uint32_t saveDSPSTRIDE;
+       uint32_t saveDSPSIZE;
+       uint32_t saveDSPPOS;
+       uint32_t saveDSPBASE;
+       uint32_t savePalette[256];
+};
+
+struct psb_intel_crtc {
+       struct drm_crtc base;
+       int pipe;
+       int plane;
+       uint32_t cursor_addr;
+       u8 lut_r[256], lut_g[256], lut_b[256];
+       u8 lut_adj[256];
+       struct psb_intel_framebuffer *fbdev_fb;
+       /* a mode_set for fbdev users on this crtc */
+       struct drm_mode_set mode_set;
+
+       /* GEM object that holds our cursor */
+       struct drm_gem_object *cursor_obj;
+
+       struct drm_display_mode saved_mode;
+       struct drm_display_mode saved_adjusted_mode;
+
+       struct psb_intel_mode_device *mode_dev;
+
+       /*crtc mode setting flags*/
+       u32 mode_flags;
+
+       /* Saved Crtc HW states */
+       struct psb_intel_crtc_state *crtc_state;
+};
+
+#define to_psb_intel_crtc(x)   \
+               container_of(x, struct psb_intel_crtc, base)
+#define to_psb_intel_output(x) \
+               container_of(x, struct psb_intel_output, base)
+#define enc_to_psb_intel_output(x)     \
+               container_of(x, struct psb_intel_output, enc)
+#define to_psb_intel_framebuffer(x)    \
+               container_of(x, struct psb_intel_framebuffer, base)
+
+struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev,
+                                       const u32 reg, const char *name);
+void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan);
+int psb_intel_ddc_get_modes(struct psb_intel_output *psb_intel_output);
+extern bool psb_intel_ddc_probe(struct psb_intel_output *psb_intel_output);
+
+extern void psb_intel_crtc_init(struct drm_device *dev, int pipe,
+                           struct psb_intel_mode_device *mode_dev);
+extern void psb_intel_crt_init(struct drm_device *dev);
+extern void psb_intel_sdvo_init(struct drm_device *dev, int output_device);
+extern void psb_intel_dvo_init(struct drm_device *dev);
+extern void psb_intel_tv_init(struct drm_device *dev);
+extern void psb_intel_lvds_init(struct drm_device *dev,
+                           struct psb_intel_mode_device *mode_dev);
+extern void psb_intel_lvds_set_brightness(struct drm_device *dev, int level);
+extern void oaktrail_lvds_init(struct drm_device *dev,
+                          struct psb_intel_mode_device *mode_dev);
+extern void oaktrail_wait_for_INTR_PKT_SENT(struct drm_device *dev);
+extern void oaktrail_dsi_init(struct drm_device *dev,
+                          struct psb_intel_mode_device *mode_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 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,
+                                            int sdvoB);
+extern int psb_intel_sdvo_supports_hotplug(struct drm_connector *connector);
+extern void psb_intel_sdvo_set_hotplug(struct drm_connector *connector,
+                                  int enable);
+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,
+                                     struct drm_display_mode *adjusted_mode);
+extern int psb_intel_lvds_mode_valid(struct drm_connector *connector,
+                                    struct drm_display_mode *mode);
+extern int psb_intel_lvds_set_property(struct drm_connector *connector,
+                                       struct drm_property *property,
+                                       uint64_t value);
+extern void psb_intel_lvds_destroy(struct drm_connector *connector);
+extern const struct drm_encoder_funcs psb_intel_lvds_enc_funcs;
+
+
+#endif                         /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
new file mode 100644 (file)
index 0000000..c6436da
--- /dev/null
@@ -0,0 +1,849 @@
+/*
+ * 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>
+ *     Dave Airlie <airlied@linux.ie>
+ *     Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include <drm/drmP.h>
+
+#include "intel_bios.h"
+#include "psb_drv.h"
+#include "psb_intel_drv.h"
+#include "psb_intel_reg.h"
+#include "power.h"
+#include <linux/pm_runtime.h>
+
+/*
+ * LVDS I2C backlight control macros
+ */
+#define BRIGHTNESS_MAX_LEVEL 100
+#define BRIGHTNESS_MASK 0xFF
+#define BLC_I2C_TYPE   0x01
+#define BLC_PWM_TYPT   0x02
+
+#define BLC_POLARITY_NORMAL 0
+#define BLC_POLARITY_INVERSE 1
+
+#define PSB_BLC_MAX_PWM_REG_FREQ       (0xFFFE)
+#define PSB_BLC_MIN_PWM_REG_FREQ       (0x2)
+#define PSB_BLC_PWM_PRECISION_FACTOR   (10)
+#define PSB_BACKLIGHT_PWM_CTL_SHIFT    (16)
+#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
+
+struct psb_intel_lvds_priv {
+       /*
+        * Saved LVDO output states
+        */
+       uint32_t savePP_ON;
+       uint32_t savePP_OFF;
+       uint32_t saveLVDS;
+       uint32_t savePP_CONTROL;
+       uint32_t savePP_CYCLE;
+       uint32_t savePFIT_CONTROL;
+       uint32_t savePFIT_PGM_RATIOS;
+       uint32_t saveBLC_PWM_CTL;
+};
+
+
+/*
+ * Returns the maximum level of the backlight duty cycle field.
+ */
+static u32 psb_intel_lvds_get_max_backlight(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       u32 retVal;
+
+       if (gma_power_begin(dev, false)) {
+               retVal = ((REG_READ(BLC_PWM_CTL) &
+                         BACKLIGHT_MODULATION_FREQ_MASK) >>
+                         BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+
+               gma_power_end(dev);
+       } else
+               retVal = ((dev_priv->saveBLC_PWM_CTL &
+                         BACKLIGHT_MODULATION_FREQ_MASK) >>
+                         BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+
+       return retVal;
+}
+
+/*
+ * Set LVDS backlight level by I2C command
+ *
+ * FIXME: at some point we need to both track this for PM and also
+ * disable runtime pm on MRST if the brightness is nil (ie blanked)
+ */
+static int psb_lvds_i2c_set_brightness(struct drm_device *dev,
+                                       unsigned int level)
+{
+       struct drm_psb_private *dev_priv =
+               (struct drm_psb_private *)dev->dev_private;
+
+       struct psb_intel_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus;
+       u8 out_buf[2];
+       unsigned int blc_i2c_brightness;
+
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = lvds_i2c_bus->slave_addr,
+                       .flags = 0,
+                       .len = 2,
+                       .buf = out_buf,
+               }
+       };
+
+       blc_i2c_brightness = BRIGHTNESS_MASK & ((unsigned int)level *
+                            BRIGHTNESS_MASK /
+                            BRIGHTNESS_MAX_LEVEL);
+
+       if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE)
+               blc_i2c_brightness = BRIGHTNESS_MASK - blc_i2c_brightness;
+
+       out_buf[0] = dev_priv->lvds_bl->brightnesscmd;
+       out_buf[1] = (u8)blc_i2c_brightness;
+
+       if (i2c_transfer(&lvds_i2c_bus->adapter, msgs, 1) == 1) {
+               dev_dbg(dev->dev, "I2C set brightness.(command, value) (%d, %d)\n",
+                       dev_priv->lvds_bl->brightnesscmd,
+                       blc_i2c_brightness);
+               return 0;
+       }
+
+       dev_err(dev->dev, "I2C transfer error\n");
+       return -1;
+}
+
+
+static int psb_lvds_pwm_set_brightness(struct drm_device *dev, int level)
+{
+       struct drm_psb_private *dev_priv =
+                       (struct drm_psb_private *)dev->dev_private;
+
+       u32 max_pwm_blc;
+       u32 blc_pwm_duty_cycle;
+
+       max_pwm_blc = psb_intel_lvds_get_max_backlight(dev);
+
+       /*BLC_PWM_CTL Should be initiated while backlight device init*/
+       BUG_ON((max_pwm_blc & PSB_BLC_MAX_PWM_REG_FREQ) == 0);
+
+       blc_pwm_duty_cycle = level * max_pwm_blc / BRIGHTNESS_MAX_LEVEL;
+
+       if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE)
+               blc_pwm_duty_cycle = max_pwm_blc - blc_pwm_duty_cycle;
+
+       blc_pwm_duty_cycle &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR;
+       REG_WRITE(BLC_PWM_CTL,
+                 (max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) |
+                 (blc_pwm_duty_cycle));
+
+       return 0;
+}
+
+/*
+ * Set LVDS backlight level either by I2C or PWM
+ */
+void psb_intel_lvds_set_brightness(struct drm_device *dev, int level)
+{
+       /*u32 blc_pwm_ctl;*/
+       struct drm_psb_private *dev_priv =
+                       (struct drm_psb_private *)dev->dev_private;
+
+       dev_dbg(dev->dev, "backlight level is %d\n", level);
+
+       if (!dev_priv->lvds_bl) {
+               dev_err(dev->dev, "NO LVDS Backlight Info\n");
+               return;
+       }
+
+       if (dev_priv->lvds_bl->type == BLC_I2C_TYPE)
+               psb_lvds_i2c_set_brightness(dev, level);
+       else
+               psb_lvds_pwm_set_brightness(dev, level);
+}
+
+/*
+ * Sets the backlight level.
+ *
+ * level: backlight level, from 0 to psb_intel_lvds_get_max_backlight().
+ */
+static void psb_intel_lvds_set_backlight(struct drm_device *dev, int level)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       u32 blc_pwm_ctl;
+
+       if (gma_power_begin(dev, false)) {
+               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)));
+               gma_power_end(dev);
+       } else {
+               blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL &
+                               ~BACKLIGHT_DUTY_CYCLE_MASK;
+               dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl |
+                                       (level << BACKLIGHT_DUTY_CYCLE_SHIFT));
+       }
+}
+
+/*
+ * Sets the power state for the panel.
+ */
+static void psb_intel_lvds_set_power(struct drm_device *dev,
+                                struct psb_intel_output *output, bool on)
+{
+       u32 pp_status;
+
+       if (!gma_power_begin(dev, true))
+               return;
+
+       if (on) {
+               REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
+                         POWER_TARGET_ON);
+               do {
+                       pp_status = REG_READ(PP_STATUS);
+               } while ((pp_status & PP_ON) == 0);
+
+               psb_intel_lvds_set_backlight(dev,
+                                        output->
+                                        mode_dev->backlight_duty_cycle);
+       } else {
+               psb_intel_lvds_set_backlight(dev, 0);
+
+               REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
+                         ~POWER_TARGET_ON);
+               do {
+                       pp_status = REG_READ(PP_STATUS);
+               } while (pp_status & PP_ON);
+       }
+
+       gma_power_end(dev);
+}
+
+static void psb_intel_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+       struct drm_device *dev = encoder->dev;
+       struct psb_intel_output *output = enc_to_psb_intel_output(encoder);
+
+       if (mode == DRM_MODE_DPMS_ON)
+               psb_intel_lvds_set_power(dev, output, true);
+       else
+               psb_intel_lvds_set_power(dev, output, false);
+
+       /* XXX: We never power down the LVDS pairs. */
+}
+
+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_output *psb_intel_output =
+               to_psb_intel_output(connector);
+       struct psb_intel_lvds_priv *lvds_priv =
+               (struct psb_intel_lvds_priv *)psb_intel_output->dev_priv;
+
+       lvds_priv->savePP_ON = REG_READ(LVDSPP_ON);
+       lvds_priv->savePP_OFF = REG_READ(LVDSPP_OFF);
+       lvds_priv->saveLVDS = REG_READ(LVDS);
+       lvds_priv->savePP_CONTROL = REG_READ(PP_CONTROL);
+       lvds_priv->savePP_CYCLE = REG_READ(PP_CYCLE);
+       /*lvds_priv->savePP_DIVISOR = REG_READ(PP_DIVISOR);*/
+       lvds_priv->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
+       lvds_priv->savePFIT_CONTROL = REG_READ(PFIT_CONTROL);
+       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 &
+                                               BACKLIGHT_DUTY_CYCLE_MASK);
+
+       /*
+        * If the light is off at server startup,
+        * just make it full brightness
+        */
+       if (dev_priv->backlight_duty_cycle == 0)
+               dev_priv->backlight_duty_cycle =
+               psb_intel_lvds_get_max_backlight(dev);
+
+       dev_dbg(dev->dev, "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
+                       lvds_priv->savePP_ON,
+                       lvds_priv->savePP_OFF,
+                       lvds_priv->saveLVDS,
+                       lvds_priv->savePP_CONTROL,
+                       lvds_priv->savePP_CYCLE,
+                       lvds_priv->saveBLC_PWM_CTL);
+}
+
+static void psb_intel_lvds_restore(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       u32 pp_status;
+
+       /*struct drm_psb_private *dev_priv =
+                               (struct drm_psb_private *)dev->dev_private;*/
+       struct psb_intel_output *psb_intel_output =
+                                       to_psb_intel_output(connector);
+       struct psb_intel_lvds_priv *lvds_priv =
+               (struct psb_intel_lvds_priv *)psb_intel_output->dev_priv;
+
+       dev_dbg(dev->dev, "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
+                       lvds_priv->savePP_ON,
+                       lvds_priv->savePP_OFF,
+                       lvds_priv->saveLVDS,
+                       lvds_priv->savePP_CONTROL,
+                       lvds_priv->savePP_CYCLE,
+                       lvds_priv->saveBLC_PWM_CTL);
+
+       REG_WRITE(BLC_PWM_CTL, lvds_priv->saveBLC_PWM_CTL);
+       REG_WRITE(PFIT_CONTROL, lvds_priv->savePFIT_CONTROL);
+       REG_WRITE(PFIT_PGM_RATIOS, lvds_priv->savePFIT_PGM_RATIOS);
+       REG_WRITE(LVDSPP_ON, lvds_priv->savePP_ON);
+       REG_WRITE(LVDSPP_OFF, lvds_priv->savePP_OFF);
+       /*REG_WRITE(PP_DIVISOR, lvds_priv->savePP_DIVISOR);*/
+       REG_WRITE(PP_CYCLE, lvds_priv->savePP_CYCLE);
+       REG_WRITE(PP_CONTROL, lvds_priv->savePP_CONTROL);
+       REG_WRITE(LVDS, lvds_priv->saveLVDS);
+
+       if (lvds_priv->savePP_CONTROL & POWER_TARGET_ON) {
+               REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
+                       POWER_TARGET_ON);
+               do {
+                       pp_status = REG_READ(PP_STATUS);
+               } while ((pp_status & PP_ON) == 0);
+       } else {
+               REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
+                       ~POWER_TARGET_ON);
+               do {
+                       pp_status = REG_READ(PP_STATUS);
+               } while (pp_status & PP_ON);
+       }
+}
+
+int psb_intel_lvds_mode_valid(struct drm_connector *connector,
+                                struct drm_display_mode *mode)
+{
+       struct psb_intel_output *psb_intel_output =
+                               to_psb_intel_output(connector);
+       struct drm_display_mode *fixed_mode =
+           psb_intel_output->mode_dev->panel_fixed_mode;
+
+       if (psb_intel_output->type == INTEL_OUTPUT_MIPI2)
+               fixed_mode = psb_intel_output->mode_dev->panel_fixed_mode2;
+
+       /* just in case */
+       if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+               return MODE_NO_DBLESCAN;
+
+       /* just in case */
+       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+               return MODE_NO_INTERLACE;
+
+       if (fixed_mode) {
+               if (mode->hdisplay > fixed_mode->hdisplay)
+                       return MODE_PANEL;
+               if (mode->vdisplay > fixed_mode->vdisplay)
+                       return MODE_PANEL;
+       }
+       return MODE_OK;
+}
+
+bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode)
+{
+       struct psb_intel_mode_device *mode_dev =
+           enc_to_psb_intel_output(encoder)->mode_dev;
+       struct drm_device *dev = encoder->dev;
+       struct psb_intel_crtc *psb_intel_crtc =
+                               to_psb_intel_crtc(encoder->crtc);
+       struct drm_encoder *tmp_encoder;
+       struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode;
+       struct psb_intel_output *psb_intel_output =
+                                       enc_to_psb_intel_output(encoder);
+
+       if (psb_intel_output->type == INTEL_OUTPUT_MIPI2)
+               panel_fixed_mode = mode_dev->panel_fixed_mode2;
+
+       /* FIXME: review for Medfield */
+       /* PSB requires the LVDS is on pipe B, MRST has only one pipe anyway */
+       if (!IS_MRST(dev) && psb_intel_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) {
+               printk(KERN_ERR "Must use PIPE A\n");
+               return false;
+       }
+       /* Should never happen!! */
+       list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list,
+                           head) {
+               if (tmp_encoder != encoder
+                   && tmp_encoder->crtc == encoder->crtc) {
+                       printk(KERN_ERR "Can't enable LVDS and another "
+                              "encoder on the same pipe\n");
+                       return false;
+               }
+       }
+
+       /*
+        * If we have timings from the BIOS for the panel, put them in
+        * to the adjusted mode.  The CRTC will be set up for this mode,
+        * with the panel scaling set up to source from the H/VDisplay
+        * of the original mode.
+        */
+       if (panel_fixed_mode != NULL) {
+               adjusted_mode->hdisplay = panel_fixed_mode->hdisplay;
+               adjusted_mode->hsync_start = panel_fixed_mode->hsync_start;
+               adjusted_mode->hsync_end = panel_fixed_mode->hsync_end;
+               adjusted_mode->htotal = panel_fixed_mode->htotal;
+               adjusted_mode->vdisplay = panel_fixed_mode->vdisplay;
+               adjusted_mode->vsync_start = panel_fixed_mode->vsync_start;
+               adjusted_mode->vsync_end = panel_fixed_mode->vsync_end;
+               adjusted_mode->vtotal = panel_fixed_mode->vtotal;
+               adjusted_mode->clock = panel_fixed_mode->clock;
+               drm_mode_set_crtcinfo(adjusted_mode,
+                                     CRTC_INTERLACE_HALVE_V);
+       }
+
+       /*
+        * XXX: It would be nice to support lower refresh rates on the
+        * panels to reduce power consumption, and perhaps match the
+        * user's requested refresh rate.
+        */
+
+       return true;
+}
+
+static void psb_intel_lvds_prepare(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct psb_intel_output *output = enc_to_psb_intel_output(encoder);
+       struct psb_intel_mode_device *mode_dev = output->mode_dev;
+
+       if (!gma_power_begin(dev, true))
+               return;
+
+       mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
+       mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL &
+                                         BACKLIGHT_DUTY_CYCLE_MASK);
+
+       psb_intel_lvds_set_power(dev, output, false);
+
+       gma_power_end(dev);
+}
+
+static void psb_intel_lvds_commit(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct psb_intel_output *output = enc_to_psb_intel_output(encoder);
+       struct psb_intel_mode_device *mode_dev = output->mode_dev;
+
+       if (mode_dev->backlight_duty_cycle == 0)
+               mode_dev->backlight_duty_cycle =
+                   psb_intel_lvds_get_max_backlight(dev);
+
+       psb_intel_lvds_set_power(dev, output, true);
+}
+
+static void psb_intel_lvds_mode_set(struct drm_encoder *encoder,
+                               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;
+       u32 pfit_control;
+
+       /*
+        * The LVDS pin pair will already have been turned on in the
+        * psb_intel_crtc_mode_set since it has a large impact on the DPLL
+        * settings.
+        */
+
+       /*
+        * Enable automatic panel scaling so that non-native modes fill the
+        * screen.  Should be enabled before the pipe is enabled, according to
+        * register description and PRM.
+        */
+       if (mode->hdisplay != adjusted_mode->hdisplay ||
+           mode->vdisplay != adjusted_mode->vdisplay)
+               pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
+                               HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
+                               HORIZ_INTERP_BILINEAR);
+       else
+               pfit_control = 0;
+
+       if (dev_priv->lvds_dither)
+               pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+
+       REG_WRITE(PFIT_CONTROL, pfit_control);
+}
+
+/*
+ * Detect the LVDS connection.
+ *
+ * This always returns CONNECTOR_STATUS_CONNECTED.
+ * This connector should only have
+ * been set up if the LVDS was actually connected anyway.
+ */
+static enum drm_connector_status psb_intel_lvds_detect(struct drm_connector
+                                                  *connector, bool force)
+{
+       return connector_status_connected;
+}
+
+/*
+ * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
+ */
+static int psb_intel_lvds_get_modes(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct psb_intel_output *psb_intel_output =
+                                       to_psb_intel_output(connector);
+       struct psb_intel_mode_device *mode_dev =
+                                       psb_intel_output->mode_dev;
+       int ret = 0;
+
+       if (!IS_MRST(dev))
+               ret = psb_intel_ddc_get_modes(psb_intel_output);
+
+       if (ret)
+               return ret;
+
+       /* Didn't get an EDID, so
+        * Set wide sync ranges so we get all modes
+        * handed to valid_mode for checking
+        */
+       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 (mode_dev->panel_fixed_mode != NULL) {
+               struct drm_display_mode *mode =
+                   drm_mode_duplicate(dev, mode_dev->panel_fixed_mode);
+               drm_mode_probed_add(connector, mode);
+               return 1;
+       }
+
+       return 0;
+}
+
+/**
+ * psb_intel_lvds_destroy - unregister and free LVDS structures
+ * @connector: connector to free
+ *
+ * Unregister the DDC bus for this connector then free the driver private
+ * structure.
+ */
+void psb_intel_lvds_destroy(struct drm_connector *connector)
+{
+       struct psb_intel_output *psb_intel_output =
+                                       to_psb_intel_output(connector);
+
+       if (psb_intel_output->ddc_bus)
+               psb_intel_i2c_destroy(psb_intel_output->ddc_bus);
+       drm_sysfs_connector_remove(connector);
+       drm_connector_cleanup(connector);
+       kfree(connector);
+}
+
+int psb_intel_lvds_set_property(struct drm_connector *connector,
+                                      struct drm_property *property,
+                                      uint64_t value)
+{
+       struct drm_encoder *encoder = connector->encoder;
+
+       if (!encoder)
+               return -1;
+
+       if (!strcmp(property->name, "scaling mode")) {
+               struct psb_intel_crtc *crtc =
+                                       to_psb_intel_crtc(encoder->crtc);
+               uint64_t curval;
+
+               if (!crtc)
+                       goto set_prop_error;
+
+               switch (value) {
+               case DRM_MODE_SCALE_FULLSCREEN:
+                       break;
+               case DRM_MODE_SCALE_NO_SCALE:
+                       break;
+               case DRM_MODE_SCALE_ASPECT:
+                       break;
+               default:
+                       goto set_prop_error;
+               }
+
+               if (drm_connector_property_get_value(connector,
+                                                    property,
+                                                    &curval))
+                       goto set_prop_error;
+
+               if (curval == value)
+                       goto set_prop_done;
+
+               if (drm_connector_property_set_value(connector,
+                                                       property,
+                                                       value))
+                       goto set_prop_error;
+
+               if (crtc->saved_mode.hdisplay != 0 &&
+                   crtc->saved_mode.vdisplay != 0) {
+                       if (!drm_crtc_helper_set_mode(encoder->crtc,
+                                                     &crtc->saved_mode,
+                                                     encoder->crtc->x,
+                                                     encoder->crtc->y,
+                                                     encoder->crtc->fb))
+                               goto set_prop_error;
+               }
+       } else if (!strcmp(property->name, "backlight")) {
+               if (drm_connector_property_set_value(connector,
+                                                       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 if (!strcmp(property->name, "DPMS")) {
+               struct drm_encoder_helper_funcs *hfuncs
+                                               = encoder->helper_private;
+               hfuncs->dpms(encoder, value);
+       }
+
+set_prop_done:
+       return 0;
+set_prop_error:
+       return -1;
+}
+
+static const struct drm_encoder_helper_funcs psb_intel_lvds_helper_funcs = {
+       .dpms = psb_intel_lvds_encoder_dpms,
+       .mode_fixup = psb_intel_lvds_mode_fixup,
+       .prepare = psb_intel_lvds_prepare,
+       .mode_set = psb_intel_lvds_mode_set,
+       .commit = psb_intel_lvds_commit,
+};
+
+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,
+};
+
+const struct drm_connector_funcs psb_intel_lvds_connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .save = psb_intel_lvds_save,
+       .restore = psb_intel_lvds_restore,
+       .detect = psb_intel_lvds_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .set_property = psb_intel_lvds_set_property,
+       .destroy = psb_intel_lvds_destroy,
+};
+
+
+static void psb_intel_lvds_enc_destroy(struct drm_encoder *encoder)
+{
+       drm_encoder_cleanup(encoder);
+}
+
+const struct drm_encoder_funcs psb_intel_lvds_enc_funcs = {
+       .destroy = psb_intel_lvds_enc_destroy,
+};
+
+
+
+/**
+ * psb_intel_lvds_init - setup LVDS connectors on this device
+ * @dev: drm device
+ *
+ * Create the connector, register the LVDS DDC bus, and try to figure out what
+ * modes we can display on the LVDS panel (if present).
+ */
+void psb_intel_lvds_init(struct drm_device *dev,
+                    struct psb_intel_mode_device *mode_dev)
+{
+       struct psb_intel_output *psb_intel_output;
+       struct psb_intel_lvds_priv *lvds_priv;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       struct drm_display_mode *scan;  /* *modes, *bios_mode; */
+       struct drm_crtc *crtc;
+       struct drm_psb_private *dev_priv =
+                               (struct drm_psb_private *)dev->dev_private;
+       u32 lvds;
+       int pipe;
+
+       psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL);
+       if (!psb_intel_output)
+               return;
+
+       lvds_priv = kzalloc(sizeof(struct psb_intel_lvds_priv), GFP_KERNEL);
+       if (!lvds_priv) {
+               kfree(psb_intel_output);
+               dev_err(dev->dev, "LVDS private allocation error\n");
+               return;
+       }
+
+       psb_intel_output->dev_priv = lvds_priv;
+
+       psb_intel_output->mode_dev = mode_dev;
+       connector = &psb_intel_output->base;
+       encoder = &psb_intel_output->enc;
+       drm_connector_init(dev, &psb_intel_output->base,
+                          &psb_intel_lvds_connector_funcs,
+                          DRM_MODE_CONNECTOR_LVDS);
+
+       drm_encoder_init(dev, &psb_intel_output->enc,
+                        &psb_intel_lvds_enc_funcs,
+                        DRM_MODE_ENCODER_LVDS);
+
+       drm_mode_connector_attach_encoder(&psb_intel_output->base,
+                                         &psb_intel_output->enc);
+       psb_intel_output->type = INTEL_OUTPUT_LVDS;
+
+       drm_encoder_helper_add(encoder, &psb_intel_lvds_helper_funcs);
+       drm_connector_helper_add(connector,
+                                &psb_intel_lvds_connector_helper_funcs);
+       connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+       connector->interlace_allowed = false;
+       connector->doublescan_allowed = false;
+
+       /*Attach connector properties*/
+       drm_connector_attach_property(connector,
+                                     dev->mode_config.scaling_mode_property,
+                                     DRM_MODE_SCALE_FULLSCREEN);
+       drm_connector_attach_property(connector,
+                                     dev_priv->backlight_property,
+                                     BRIGHTNESS_MAX_LEVEL);
+
+       /*
+        * Set up I2C bus
+        * FIXME: distroy i2c_bus when exit
+        */
+       psb_intel_output->i2c_bus = psb_intel_i2c_create(dev,
+                                                        GPIOB,
+                                                        "LVDSBLC_B");
+       if (!psb_intel_output->i2c_bus) {
+               dev_printk(KERN_ERR,
+                       &dev->pdev->dev, "I2C bus registration failed.\n");
+               goto failed_blc_i2c;
+       }
+       psb_intel_output->i2c_bus->slave_addr = 0x2C;
+       dev_priv->lvds_i2c_bus =  psb_intel_output->i2c_bus;
+
+       /*
+        * LVDS discovery:
+        * 1) check for EDID on DDC
+        * 2) check for VBT data
+        * 3) check to see if LVDS is already on
+        *    if none of the above, no panel
+        * 4) make sure lid is open
+        *    if closed, act like it's not there for now
+        */
+
+       /* Set up the DDC bus. */
+       psb_intel_output->ddc_bus = psb_intel_i2c_create(dev,
+                                                        GPIOC,
+                                                        "LVDSDDC_C");
+       if (!psb_intel_output->ddc_bus) {
+               dev_printk(KERN_ERR, &dev->pdev->dev,
+                          "DDC bus registration " "failed.\n");
+               goto failed_ddc;
+       }
+
+       /*
+        * Attempt to get the fixed panel mode from DDC.  Assume that the
+        * preferred mode is the right one.
+        */
+       psb_intel_ddc_get_modes(psb_intel_output);
+       list_for_each_entry(scan, &connector->probed_modes, head) {
+               if (scan->type & DRM_MODE_TYPE_PREFERRED) {
+                       mode_dev->panel_fixed_mode =
+                           drm_mode_duplicate(dev, scan);
+                       goto out;       /* FIXME: check for quirks */
+               }
+       }
+
+       /* Failed to get EDID, what about VBT? do we need this? */
+       if (mode_dev->vbt_mode)
+               mode_dev->panel_fixed_mode =
+                   drm_mode_duplicate(dev, mode_dev->vbt_mode);
+
+       if (!mode_dev->panel_fixed_mode)
+               if (dev_priv->lfp_lvds_vbt_mode)
+                       mode_dev->panel_fixed_mode =
+                               drm_mode_duplicate(dev,
+                                       dev_priv->lfp_lvds_vbt_mode);
+
+       /*
+        * If we didn't get EDID, try checking if the panel is already turned
+        * on.  If so, assume that whatever is currently programmed is the
+        * correct mode.
+        */
+       lvds = REG_READ(LVDS);
+       pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
+       crtc = psb_intel_get_crtc_from_pipe(dev, pipe);
+
+       if (crtc && (lvds & LVDS_PORT_EN)) {
+               mode_dev->panel_fixed_mode =
+                   psb_intel_crtc_mode_get(dev, crtc);
+               if (mode_dev->panel_fixed_mode) {
+                       mode_dev->panel_fixed_mode->type |=
+                           DRM_MODE_TYPE_PREFERRED;
+                       goto out;       /* FIXME: check for quirks */
+               }
+       }
+
+       /* If we still don't have a mode after all that, give up. */
+       if (!mode_dev->panel_fixed_mode) {
+               dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n");
+               goto failed_find;
+       }
+
+       /*
+        * Blacklist machines with BIOSes that list an LVDS panel without
+        * actually having one.
+        */
+out:
+       drm_sysfs_connector_add(connector);
+       return;
+
+failed_find:
+       if (psb_intel_output->ddc_bus)
+               psb_intel_i2c_destroy(psb_intel_output->ddc_bus);
+failed_ddc:
+       if (psb_intel_output->i2c_bus)
+               psb_intel_i2c_destroy(psb_intel_output->i2c_bus);
+failed_blc_i2c:
+       drm_encoder_cleanup(encoder);
+       drm_connector_cleanup(connector);
+       kfree(connector);
+}
+
diff --git a/drivers/gpu/drm/gma500/psb_intel_modes.c b/drivers/gpu/drm/gma500/psb_intel_modes.c
new file mode 100644 (file)
index 0000000..bde1aff
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 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.
+ *
+ * Authers: Jesse Barnes <jesse.barnes@intel.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <drm/drmP.h>
+#include "psb_intel_drv.h"
+
+/**
+ * psb_intel_ddc_probe
+ *
+ */
+bool psb_intel_ddc_probe(struct psb_intel_output *psb_intel_output)
+{
+       u8 out_buf[] = { 0x0, 0x0 };
+       u8 buf[2];
+       int ret;
+       struct i2c_msg msgs[] = {
+               {
+                .addr = 0x50,
+                .flags = 0,
+                .len = 1,
+                .buf = out_buf,
+                },
+               {
+                .addr = 0x50,
+                .flags = I2C_M_RD,
+                .len = 1,
+                .buf = buf,
+                }
+       };
+
+       ret = i2c_transfer(&psb_intel_output->ddc_bus->adapter, msgs, 2);
+       if (ret == 2)
+               return true;
+
+       return false;
+}
+
+/**
+ * psb_intel_ddc_get_modes - get modelist from monitor
+ * @connector: DRM connector device to use
+ *
+ * Fetch the EDID information from @connector using the DDC bus.
+ */
+int psb_intel_ddc_get_modes(struct psb_intel_output *psb_intel_output)
+{
+       struct edid *edid;
+       int ret = 0;
+
+       edid =
+           drm_get_edid(&psb_intel_output->base,
+                        &psb_intel_output->ddc_bus->adapter);
+       if (edid) {
+               drm_mode_connector_update_edid_property(&psb_intel_output->
+                                                       base, edid);
+               ret = drm_add_edid_modes(&psb_intel_output->base, edid);
+               kfree(edid);
+       }
+       return ret;
+}
diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h
new file mode 100644 (file)
index 0000000..1ac16aa
--- /dev/null
@@ -0,0 +1,1235 @@
+/*
+ * Copyright (c) 2009, 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.
+ */
+#ifndef __PSB_INTEL_REG_H__
+#define __PSB_INTEL_REG_H__
+
+#define BLC_PWM_CTL            0x61254
+#define BLC_PWM_CTL2           0x61250
+#define BLC_PWM_CTL_C          0x62254
+#define BLC_PWM_CTL2_C         0x62250
+#define BACKLIGHT_MODULATION_FREQ_SHIFT                (17)
+/*
+ * This is the most significant 15 bits of the number of backlight cycles in a
+ * complete cycle of the modulated backlight control.
+ *
+ * The actual value is this field multiplied by two.
+ */
+#define BACKLIGHT_MODULATION_FREQ_MASK (0x7fff << 17)
+#define BLM_LEGACY_MODE                        (1 << 16)
+/*
+ * This is the number of cycles out of the backlight modulation cycle for which
+ * the backlight is on.
+ *
+ * This field must be no greater than the number of cycles in the complete
+ * backlight modulation cycle.
+ */
+#define BACKLIGHT_DUTY_CYCLE_SHIFT     (0)
+#define BACKLIGHT_DUTY_CYCLE_MASK      (0xffff)
+
+#define I915_GCFGC                     0xf0
+#define I915_LOW_FREQUENCY_ENABLE      (1 << 7)
+#define I915_DISPLAY_CLOCK_190_200_MHZ (0 << 4)
+#define I915_DISPLAY_CLOCK_333_MHZ     (4 << 4)
+#define I915_DISPLAY_CLOCK_MASK                (7 << 4)
+
+#define I855_HPLLCC                    0xc0
+#define I855_CLOCK_CONTROL_MASK                (3 << 0)
+#define I855_CLOCK_133_200             (0 << 0)
+#define I855_CLOCK_100_200             (1 << 0)
+#define I855_CLOCK_100_133             (2 << 0)
+#define I855_CLOCK_166_250             (3 << 0)
+
+/* I830 CRTC registers */
+#define HTOTAL_A               0x60000
+#define HBLANK_A               0x60004
+#define HSYNC_A                        0x60008
+#define VTOTAL_A               0x6000c
+#define VBLANK_A               0x60010
+#define VSYNC_A                        0x60014
+#define PIPEASRC               0x6001c
+#define BCLRPAT_A              0x60020
+#define VSYNCSHIFT_A           0x60028
+
+#define HTOTAL_B               0x61000
+#define HBLANK_B               0x61004
+#define HSYNC_B                        0x61008
+#define VTOTAL_B               0x6100c
+#define VBLANK_B               0x61010
+#define VSYNC_B                        0x61014
+#define PIPEBSRC               0x6101c
+#define BCLRPAT_B              0x61020
+#define VSYNCSHIFT_B           0x61028
+
+#define HTOTAL_C               0x62000
+#define HBLANK_C               0x62004
+#define HSYNC_C                        0x62008
+#define VTOTAL_C               0x6200c
+#define VBLANK_C               0x62010
+#define VSYNC_C                        0x62014
+#define PIPECSRC               0x6201c
+#define BCLRPAT_C              0x62020
+#define VSYNCSHIFT_C           0x62028
+
+#define PP_STATUS              0x61200
+# define PP_ON                         (1 << 31)
+/*
+ * Indicates that all dependencies of the panel are on:
+ *
+ * - PLL enabled
+ * - pipe enabled
+ * - LVDS/DVOB/DVOC on
+ */
+#define PP_READY                       (1 << 30)
+#define PP_SEQUENCE_NONE               (0 << 28)
+#define PP_SEQUENCE_ON                 (1 << 28)
+#define PP_SEQUENCE_OFF                        (2 << 28)
+#define PP_SEQUENCE_MASK               0x30000000
+#define PP_CONTROL             0x61204
+#define POWER_TARGET_ON                        (1 << 0)
+
+#define LVDSPP_ON              0x61208
+#define LVDSPP_OFF             0x6120c
+#define PP_CYCLE               0x61210
+
+#define PFIT_CONTROL           0x61230
+#define PFIT_ENABLE                    (1 << 31)
+#define PFIT_PIPE_MASK                 (3 << 29)
+#define PFIT_PIPE_SHIFT                        29
+#define PFIT_SCALING_MODE_PILLARBOX    (1 << 27)
+#define PFIT_SCALING_MODE_LETTERBOX    (3 << 26)
+#define VERT_INTERP_DISABLE            (0 << 10)
+#define VERT_INTERP_BILINEAR           (1 << 10)
+#define VERT_INTERP_MASK               (3 << 10)
+#define VERT_AUTO_SCALE                        (1 << 9)
+#define HORIZ_INTERP_DISABLE           (0 << 6)
+#define HORIZ_INTERP_BILINEAR          (1 << 6)
+#define HORIZ_INTERP_MASK              (3 << 6)
+#define HORIZ_AUTO_SCALE               (1 << 5)
+#define PANEL_8TO6_DITHER_ENABLE       (1 << 3)
+
+#define PFIT_PGM_RATIOS                0x61234
+#define PFIT_VERT_SCALE_MASK                   0xfff00000
+#define PFIT_HORIZ_SCALE_MASK                  0x0000fff0
+
+#define PFIT_AUTO_RATIOS       0x61238
+
+#define DPLL_A                 0x06014
+#define DPLL_B                 0x06018
+#define DPLL_VCO_ENABLE                        (1 << 31)
+#define DPLL_DVO_HIGH_SPEED            (1 << 30)
+#define DPLL_SYNCLOCK_ENABLE           (1 << 29)
+#define DPLL_VGA_MODE_DIS              (1 << 28)
+#define DPLLB_MODE_DAC_SERIAL          (1 << 26)       /* i915 */
+#define DPLLB_MODE_LVDS                        (2 << 26)       /* i915 */
+#define DPLL_MODE_MASK                 (3 << 26)
+#define DPLL_DAC_SERIAL_P2_CLOCK_DIV_10        (0 << 24)       /* i915 */
+#define DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 (1 << 24)       /* i915 */
+#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_LOCK                      (1 << 15)       /* CDV */
+
+/*
+ *  The i830 generation, in DAC/serial mode, defines p1 as two plus this
+ * bitfield, or just 2 if PLL_P1_DIVIDE_BY_TWO is set.
+ */
+# define DPLL_FPA01_P1_POST_DIV_MASK_I830      0x001f0000
+/*
+ * The i830 generation, in LVDS mode, defines P1 as the bit number set within
+ * this field (only one bit may be set).
+ */
+#define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS  0x003f0000
+#define DPLL_FPA01_P1_POST_DIV_SHIFT   16
+#define PLL_P2_DIVIDE_BY_4             (1 << 23)       /* i830, required
+                                                        * in DVO non-gang */
+# define PLL_P1_DIVIDE_BY_TWO          (1 << 21)       /* i830 */
+#define PLL_REF_INPUT_DREFCLK          (0 << 13)
+#define PLL_REF_INPUT_TVCLKINA         (1 << 13)       /* i830 */
+#define PLL_REF_INPUT_TVCLKINBC                (2 << 13)       /* SDVO
+                                                                * TVCLKIN */
+#define PLLB_REF_INPUT_SPREADSPECTRUMIN        (3 << 13)
+#define PLL_REF_INPUT_MASK             (3 << 13)
+#define PLL_LOAD_PULSE_PHASE_SHIFT     9
+/*
+ * Parallel to Serial Load Pulse phase selection.
+ * Selects the phase for the 10X DPLL clock for the PCIe
+ * digital display port. The range is 4 to 13; 10 or more
+ * is just a flip delay. The default is 6
+ */
+#define PLL_LOAD_PULSE_PHASE_MASK      (0xf << PLL_LOAD_PULSE_PHASE_SHIFT)
+#define DISPLAY_RATE_SELECT_FPA1       (1 << 8)
+
+/*
+ * SDVO multiplier for 945G/GM. Not used on 965.
+ *
+ * DPLL_MD_UDI_MULTIPLIER_MASK
+ */
+#define SDVO_MULTIPLIER_MASK           0x000000ff
+#define SDVO_MULTIPLIER_SHIFT_HIRES    4
+#define SDVO_MULTIPLIER_SHIFT_VGA      0
+
+/*
+ * PLL_MD
+ */
+/* Pipe A SDVO/UDI clock multiplier/divider register for G965. */
+#define DPLL_A_MD              0x0601c
+/* Pipe B SDVO/UDI clock multiplier/divider register for G965. */
+#define DPLL_B_MD              0x06020
+/*
+ * UDI pixel divider, controlling how many pixels are stuffed into a packet.
+ *
+ * Value is pixels minus 1.  Must be set to 1 pixel for SDVO.
+ */
+#define DPLL_MD_UDI_DIVIDER_MASK       0x3f000000
+#define DPLL_MD_UDI_DIVIDER_SHIFT      24
+/* UDI pixel divider for VGA, same as DPLL_MD_UDI_DIVIDER_MASK. */
+#define DPLL_MD_VGA_UDI_DIVIDER_MASK   0x003f0000
+#define DPLL_MD_VGA_UDI_DIVIDER_SHIFT  16
+/*
+ * SDVO/UDI pixel multiplier.
+ *
+ * SDVO requires that the bus clock rate be between 1 and 2 Ghz, and the bus
+ * clock rate is 10 times the DPLL clock.  At low resolution/refresh rate
+ * modes, the bus rate would be below the limits, so SDVO allows for stuffing
+ * dummy bytes in the datastream at an increased clock rate, with both sides of
+ * the link knowing how many bytes are fill.
+ *
+ * So, for a mode with a dotclock of 65Mhz, we would want to double the clock
+ * rate to 130Mhz to get a bus rate of 1.30Ghz.  The DPLL clock rate would be
+ * set to 130Mhz, and the SDVO multiplier set to 2x in this register and
+ * through an SDVO command.
+ *
+ * This register field has values of multiplication factor minus 1, with
+ * a maximum multiplier of 5 for SDVO.
+ */
+#define DPLL_MD_UDI_MULTIPLIER_MASK    0x00003f00
+#define DPLL_MD_UDI_MULTIPLIER_SHIFT   8
+/*
+ * SDVO/UDI pixel multiplier for VGA, same as DPLL_MD_UDI_MULTIPLIER_MASK.
+ * This best be set to the default value (3) or the CRT won't work. No,
+ * I don't entirely understand what this does...
+ */
+#define DPLL_MD_VGA_UDI_MULTIPLIER_MASK        0x0000003f
+#define DPLL_MD_VGA_UDI_MULTIPLIER_SHIFT 0
+
+#define DPLL_TEST              0x606c
+#define DPLLB_TEST_SDVO_DIV_1          (0 << 22)
+#define DPLLB_TEST_SDVO_DIV_2          (1 << 22)
+#define DPLLB_TEST_SDVO_DIV_4          (2 << 22)
+#define DPLLB_TEST_SDVO_DIV_MASK       (3 << 22)
+#define DPLLB_TEST_N_BYPASS            (1 << 19)
+#define DPLLB_TEST_M_BYPASS            (1 << 18)
+#define DPLLB_INPUT_BUFFER_ENABLE      (1 << 16)
+#define DPLLA_TEST_N_BYPASS            (1 << 3)
+#define DPLLA_TEST_M_BYPASS            (1 << 2)
+#define DPLLA_INPUT_BUFFER_ENABLE      (1 << 0)
+
+#define ADPA                   0x61100
+#define ADPA_DAC_ENABLE                        (1 << 31)
+#define ADPA_DAC_DISABLE               0
+#define ADPA_PIPE_SELECT_MASK          (1 << 30)
+#define ADPA_PIPE_A_SELECT             0
+#define ADPA_PIPE_B_SELECT             (1 << 30)
+#define ADPA_USE_VGA_HVPOLARITY                (1 << 15)
+#define ADPA_SETS_HVPOLARITY           0
+#define ADPA_VSYNC_CNTL_DISABLE                (1 << 11)
+#define ADPA_VSYNC_CNTL_ENABLE         0
+#define ADPA_HSYNC_CNTL_DISABLE                (1 << 10)
+#define ADPA_HSYNC_CNTL_ENABLE         0
+#define ADPA_VSYNC_ACTIVE_HIGH         (1 << 4)
+#define ADPA_VSYNC_ACTIVE_LOW          0
+#define ADPA_HSYNC_ACTIVE_HIGH         (1 << 3)
+#define ADPA_HSYNC_ACTIVE_LOW          0
+
+#define FPA0                   0x06040
+#define FPA1                   0x06044
+#define FPB0                   0x06048
+#define FPB1                   0x0604c
+#define FP_N_DIV_MASK                  0x003f0000
+#define FP_N_DIV_SHIFT                 16
+#define FP_M1_DIV_MASK                 0x00003f00
+#define FP_M1_DIV_SHIFT                        8
+#define FP_M2_DIV_MASK                 0x0000003f
+#define FP_M2_DIV_SHIFT                        0
+
+#define PORT_HOTPLUG_EN                0x61110
+#define SDVOB_HOTPLUG_INT_EN           (1 << 26)
+#define SDVOC_HOTPLUG_INT_EN           (1 << 25)
+#define TV_HOTPLUG_INT_EN              (1 << 18)
+#define CRT_HOTPLUG_INT_EN             (1 << 9)
+#define CRT_HOTPLUG_FORCE_DETECT       (1 << 3)
+/* CDV.. */
+#define CRT_HOTPLUG_ACTIVATION_PERIOD_64       (1 << 8)
+#define CRT_HOTPLUG_DAC_ON_TIME_2M             (0 << 7)
+#define CRT_HOTPLUG_DAC_ON_TIME_4M             (1 << 7)
+#define CRT_HOTPLUG_VOLTAGE_COMPARE_40         (0 << 5)
+#define CRT_HOTPLUG_VOLTAGE_COMPARE_50         (1 << 5)
+#define CRT_HOTPLUG_VOLTAGE_COMPARE_60         (2 << 5)
+#define CRT_HOTPLUG_VOLTAGE_COMPARE_70         (3 << 5)
+#define CRT_HOTPLUG_VOLTAGE_COMPARE_MASK       (3 << 5)
+#define CRT_HOTPLUG_DETECT_DELAY_1G            (0 << 4)
+#define CRT_HOTPLUG_DETECT_DELAY_2G            (1 << 4)
+#define CRT_HOTPLUG_DETECT_VOLTAGE_325MV       (0 << 2)
+#define CRT_HOTPLUG_DETECT_VOLTAGE_475MV       (1 << 2)
+#define CRT_HOTPLUG_DETECT_MASK                        0x000000F8
+
+#define PORT_HOTPLUG_STAT      0x61114
+#define CRT_HOTPLUG_INT_STATUS         (1 << 11)
+#define TV_HOTPLUG_INT_STATUS          (1 << 10)
+#define CRT_HOTPLUG_MONITOR_MASK       (3 << 8)
+#define CRT_HOTPLUG_MONITOR_COLOR      (3 << 8)
+#define CRT_HOTPLUG_MONITOR_MONO       (2 << 8)
+#define CRT_HOTPLUG_MONITOR_NONE       (0 << 8)
+#define SDVOC_HOTPLUG_INT_STATUS       (1 << 7)
+#define SDVOB_HOTPLUG_INT_STATUS       (1 << 6)
+
+#define SDVOB                  0x61140
+#define SDVOC                  0x61160
+#define SDVO_ENABLE                    (1 << 31)
+#define SDVO_PIPE_B_SELECT             (1 << 30)
+#define SDVO_STALL_SELECT              (1 << 29)
+#define SDVO_INTERRUPT_ENABLE          (1 << 26)
+
+/**
+ * 915G/GM SDVO pixel multiplier.
+ *
+ * Programmed value is multiplier - 1, up to 5x.
+ *
+ * DPLL_MD_UDI_MULTIPLIER_MASK
+ */
+#define SDVO_PORT_MULTIPLY_MASK                (7 << 23)
+#define SDVO_PORT_MULTIPLY_SHIFT       23
+#define SDVO_PHASE_SELECT_MASK         (15 << 19)
+#define SDVO_PHASE_SELECT_DEFAULT      (6 << 19)
+#define SDVO_CLOCK_OUTPUT_INVERT       (1 << 18)
+#define SDVOC_GANG_MODE                        (1 << 16)
+#define SDVO_BORDER_ENABLE             (1 << 7)
+#define SDVOB_PCIE_CONCURRENCY         (1 << 3)
+#define SDVO_DETECTED                  (1 << 2)
+/* Bits to be preserved when writing */
+#define SDVOB_PRESERVE_MASK            ((1 << 17) | (1 << 16) | (1 << 14))
+#define SDVOC_PRESERVE_MASK            (1 << 17)
+
+/*
+ * This register controls the LVDS output enable, pipe selection, and data
+ * format selection.
+ *
+ * All of the clock/data pairs are force powered down by power sequencing.
+ */
+#define LVDS                   0x61180
+/*
+ * Enables the LVDS port.  This bit must be set before DPLLs are enabled, as
+ * the DPLL semantics change when the LVDS is assigned to that pipe.
+ */
+#define LVDS_PORT_EN                   (1 << 31)
+/* Selects pipe B for LVDS data.  Must be set on pre-965. */
+#define LVDS_PIPEB_SELECT              (1 << 30)
+
+/* Turns on border drawing to allow centered display. */
+#define LVDS_BORDER_EN                 (1 << 15)
+
+/*
+ * Enables the A0-A2 data pairs and CLKA, containing 18 bits of color data per
+ * pixel.
+ */
+#define LVDS_A0A2_CLKA_POWER_MASK      (3 << 8)
+#define LVDS_A0A2_CLKA_POWER_DOWN      (0 << 8)
+#define LVDS_A0A2_CLKA_POWER_UP                (3 << 8)
+/*
+ * Controls the A3 data pair, which contains the additional LSBs for 24 bit
+ * mode.  Only enabled if LVDS_A0A2_CLKA_POWER_UP also indicates it should be
+ * on.
+ */
+#define LVDS_A3_POWER_MASK             (3 << 6)
+#define LVDS_A3_POWER_DOWN             (0 << 6)
+#define LVDS_A3_POWER_UP               (3 << 6)
+/*
+ * Controls the CLKB pair.  This should only be set when LVDS_B0B3_POWER_UP
+ * is set.
+ */
+#define LVDS_CLKB_POWER_MASK           (3 << 4)
+#define LVDS_CLKB_POWER_DOWN           (0 << 4)
+#define LVDS_CLKB_POWER_UP             (3 << 4)
+/*
+ * Controls the B0-B3 data pairs.  This must be set to match the DPLL p2
+ * setting for whether we are in dual-channel mode.  The B3 pair will
+ * additionally only be powered up when LVDS_A3_POWER_UP is set.
+ */
+#define LVDS_B0B3_POWER_MASK           (3 << 2)
+#define LVDS_B0B3_POWER_DOWN           (0 << 2)
+#define LVDS_B0B3_POWER_UP             (3 << 2)
+
+#define PIPEACONF              0x70008
+#define PIPEACONF_ENABLE               (1 << 31)
+#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
+#define PIPEACONF_DSR                  (1 << 26)
+#define PIPEACONF_PIPE_LOCKED          (1 << 25)
+#define PIPEACONF_PALETTE              0
+#define PIPECONF_FORCE_BORDER          (1 << 25)
+#define PIPEACONF_GAMMA                        (1 << 24)
+#define PIPECONF_PROGRESSIVE           (0 << 21)
+#define PIPECONF_INTERLACE_W_FIELD_INDICATION  (6 << 21)
+#define PIPECONF_INTERLACE_FIELD_0_ONLY                (7 << 21)
+#define PIPECONF_PLANE_OFF             (1 << 19)
+#define PIPECONF_CURSOR_OFF            (1 << 18)
+
+#define PIPEBCONF              0x71008
+#define PIPEBCONF_ENABLE               (1 << 31)
+#define PIPEBCONF_DISABLE              0
+#define PIPEBCONF_DOUBLE_WIDE          (1 << 30)
+#define PIPEBCONF_DISABLE              0
+#define PIPEBCONF_GAMMA                        (1 << 24)
+#define PIPEBCONF_PALETTE              0
+
+#define PIPECCONF              0x72008
+
+#define PIPEBGCMAXRED          0x71010
+#define PIPEBGCMAXGREEN                0x71014
+#define PIPEBGCMAXBLUE         0x71018
+
+#define PIPEASTAT              0x70024
+#define PIPEBSTAT              0x71024
+#define PIPECSTAT              0x72024
+#define PIPE_VBLANK_INTERRUPT_STATUS           (1UL << 1)
+#define PIPE_START_VBLANK_INTERRUPT_STATUS     (1UL << 2)
+#define PIPE_VBLANK_CLEAR                      (1 << 1)
+#define PIPE_VBLANK_STATUS                     (1 << 1)
+#define PIPE_TE_STATUS                         (1UL << 6)
+#define PIPE_DPST_EVENT_STATUS                 (1UL << 7)
+#define PIPE_VSYNC_CLEAR                       (1UL << 9)
+#define PIPE_VSYNC_STATUS                      (1UL << 9)
+#define PIPE_HDMI_AUDIO_UNDERRUN_STATUS                (1UL << 10)
+#define PIPE_HDMI_AUDIO_BUFFER_DONE_STATUS     (1UL << 11)
+#define PIPE_VBLANK_INTERRUPT_ENABLE           (1UL << 17)
+#define PIPE_START_VBLANK_INTERRUPT_ENABLE     (1UL << 18)
+#define PIPE_TE_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_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))
+#define PIPE_VBLANK_MASK ((1 << 25)|(1 << 24)|(1 << 18)|(1 << 17))
+#define HISTOGRAM_INT_CONTROL          0x61268
+#define HISTOGRAM_BIN_DATA             0X61264
+#define HISTOGRAM_LOGIC_CONTROL                0x61260
+#define PWM_CONTROL_LOGIC              0x61250
+#define PIPE_HOTPLUG_INTERRUPT_STATUS          (1UL << 10)
+#define HISTOGRAM_INTERRUPT_ENABLE             (1UL << 31)
+#define HISTOGRAM_LOGIC_ENABLE                 (1UL << 31)
+#define PWM_LOGIC_ENABLE                       (1UL << 31)
+#define PWM_PHASEIN_ENABLE                     (1UL << 25)
+#define PWM_PHASEIN_INT_ENABLE                 (1UL << 24)
+#define PWM_PHASEIN_VB_COUNT                   0x00001f00
+#define PWM_PHASEIN_INC                                0x0000001f
+#define HISTOGRAM_INT_CTRL_CLEAR               (1UL << 30)
+#define DPST_YUV_LUMA_MODE                     0
+
+struct dpst_ie_histogram_control {
+       union {
+               uint32_t data;
+               struct {
+                       uint32_t bin_reg_index:7;
+                       uint32_t reserved:4;
+                       uint32_t bin_reg_func_select:1;
+                       uint32_t sync_to_phase_in:1;
+                       uint32_t alt_enhancement_mode:2;
+                       uint32_t reserved1:1;
+                       uint32_t sync_to_phase_in_count:8;
+                       uint32_t histogram_mode_select:1;
+                       uint32_t reserved2:4;
+                       uint32_t ie_pipe_assignment:1;
+                       uint32_t ie_mode_table_enabled:1;
+                       uint32_t ie_histogram_enable:1;
+               };
+       };
+};
+
+struct dpst_guardband {
+       union {
+               uint32_t data;
+               struct {
+                       uint32_t guardband:22;
+                       uint32_t guardband_interrupt_delay:8;
+                       uint32_t interrupt_status:1;
+                       uint32_t interrupt_enable:1;
+               };
+       };
+};
+
+#define PIPEAFRAMEHIGH         0x70040
+#define PIPEAFRAMEPIXEL                0x70044
+#define PIPEBFRAMEHIGH         0x71040
+#define PIPEBFRAMEPIXEL                0x71044
+#define PIPECFRAMEHIGH         0x72040
+#define PIPECFRAMEPIXEL                0x72044
+#define PIPE_FRAME_HIGH_MASK   0x0000ffff
+#define PIPE_FRAME_HIGH_SHIFT  0
+#define PIPE_FRAME_LOW_MASK    0xff000000
+#define PIPE_FRAME_LOW_SHIFT   24
+#define PIPE_PIXEL_MASK                0x00ffffff
+#define PIPE_PIXEL_SHIFT       0
+
+#define DSPARB                 0x70030
+#define DSPFW1                 0x70034
+#define DSPFW2                 0x70038
+#define DSPFW3                 0x7003c
+#define DSPFW4                 0x70050
+#define DSPFW5                 0x70054
+#define DSPFW6                 0x70058
+#define DSPCHICKENBIT          0x70400
+#define DSPACNTR               0x70180
+#define DSPBCNTR               0x71180
+#define DSPCCNTR               0x72180
+#define DISPLAY_PLANE_ENABLE                   (1 << 31)
+#define DISPLAY_PLANE_DISABLE                  0
+#define DISPPLANE_GAMMA_ENABLE                 (1 << 30)
+#define DISPPLANE_GAMMA_DISABLE                        0
+#define DISPPLANE_PIXFORMAT_MASK               (0xf << 26)
+#define DISPPLANE_8BPP                         (0x2 << 26)
+#define DISPPLANE_15_16BPP                     (0x4 << 26)
+#define DISPPLANE_16BPP                                (0x5 << 26)
+#define DISPPLANE_32BPP_NO_ALPHA               (0x6 << 26)
+#define DISPPLANE_32BPP                                (0x7 << 26)
+#define DISPPLANE_STEREO_ENABLE                        (1 << 25)
+#define DISPPLANE_STEREO_DISABLE               0
+#define DISPPLANE_SEL_PIPE_MASK                        (1 << 24)
+#define DISPPLANE_SEL_PIPE_POS                 24
+#define DISPPLANE_SEL_PIPE_A                   0
+#define DISPPLANE_SEL_PIPE_B                   (1 << 24)
+#define DISPPLANE_SRC_KEY_ENABLE               (1 << 22)
+#define DISPPLANE_SRC_KEY_DISABLE              0
+#define DISPPLANE_LINE_DOUBLE                  (1 << 20)
+#define DISPPLANE_NO_LINE_DOUBLE               0
+#define DISPPLANE_STEREO_POLARITY_FIRST                0
+#define DISPPLANE_STEREO_POLARITY_SECOND       (1 << 18)
+/* plane B only */
+#define DISPPLANE_ALPHA_TRANS_ENABLE           (1 << 15)
+#define DISPPLANE_ALPHA_TRANS_DISABLE          0
+#define DISPPLANE_SPRITE_ABOVE_DISPLAYA                0
+#define DISPPLANE_SPRITE_ABOVE_OVERLAY         (1)
+#define DISPPLANE_BOTTOM                       (4)
+
+#define DSPABASE               0x70184
+#define DSPALINOFF             0x70184
+#define DSPASTRIDE             0x70188
+
+#define DSPBBASE               0x71184
+#define DSPBLINOFF             0X71184
+#define DSPBADDR               DSPBBASE
+#define DSPBSTRIDE             0x71188
+
+#define DSPCBASE               0x72184
+#define DSPCLINOFF             0x72184
+#define DSPCSTRIDE             0x72188
+
+#define DSPAKEYVAL             0x70194
+#define DSPAKEYMASK            0x70198
+
+#define DSPAPOS                        0x7018C /* reserved */
+#define DSPASIZE               0x70190
+#define DSPBPOS                        0x7118C
+#define DSPBSIZE               0x71190
+#define DSPCPOS                        0x7218C
+#define DSPCSIZE               0x72190
+
+#define DSPASURF               0x7019C
+#define DSPATILEOFF            0x701A4
+
+#define DSPBSURF               0x7119C
+#define DSPBTILEOFF            0x711A4
+
+#define DSPCSURF               0x7219C
+#define DSPCTILEOFF            0x721A4
+#define DSPCKEYMAXVAL          0x721A0
+#define DSPCKEYMINVAL          0x72194
+#define DSPCKEYMSK             0x72198
+
+#define VGACNTRL               0x71400
+#define VGA_DISP_DISABLE               (1 << 31)
+#define VGA_2X_MODE                    (1 << 30)
+#define VGA_PIPE_B_SELECT              (1 << 29)
+
+/*
+ * Overlay registers
+ */
+#define OV_C_OFFSET            0x08000
+#define OV_OVADD               0x30000
+#define OV_DOVASTA             0x30008
+# define OV_PIPE_SELECT                        ((1 << 6)|(1 << 7))
+# define OV_PIPE_SELECT_POS            6
+# define OV_PIPE_A                     0
+# define OV_PIPE_C                     1
+#define OV_OGAMC5              0x30010
+#define OV_OGAMC4              0x30014
+#define OV_OGAMC3              0x30018
+#define OV_OGAMC2              0x3001C
+#define OV_OGAMC1              0x30020
+#define OV_OGAMC0              0x30024
+#define OVC_OVADD              0x38000
+#define OVC_DOVCSTA            0x38008
+#define OVC_OGAMC5             0x38010
+#define OVC_OGAMC4             0x38014
+#define OVC_OGAMC3             0x38018
+#define OVC_OGAMC2             0x3801C
+#define OVC_OGAMC1             0x38020
+#define OVC_OGAMC0             0x38024
+
+/*
+ * Some BIOS scratch area registers.  The 845 (and 830?) store the amount
+ * of video memory available to the BIOS in SWF1.
+ */
+#define SWF0                   0x71410
+#define SWF1                   0x71414
+#define SWF2                   0x71418
+#define SWF3                   0x7141c
+#define SWF4                   0x71420
+#define SWF5                   0x71424
+#define SWF6                   0x71428
+
+/*
+ * 855 scratch registers.
+ */
+#define SWF00                  0x70410
+#define SWF01                  0x70414
+#define SWF02                  0x70418
+#define SWF03                  0x7041c
+#define SWF04                  0x70420
+#define SWF05                  0x70424
+#define SWF06                  0x70428
+
+#define SWF10                  SWF0
+#define SWF11                  SWF1
+#define SWF12                  SWF2
+#define SWF13                  SWF3
+#define SWF14                  SWF4
+#define SWF15                  SWF5
+#define SWF16                  SWF6
+
+#define SWF30                  0x72414
+#define SWF31                  0x72418
+#define SWF32                  0x7241c
+
+
+/*
+ * Palette registers
+ */
+#define PALETTE_A              0x0a000
+#define PALETTE_B              0x0a800
+#define PALETTE_C              0x0ac00
+
+/* Cursor A & B regs */
+#define CURACNTR               0x70080
+#define CURSOR_MODE_DISABLE            0x00
+#define CURSOR_MODE_64_32B_AX          0x07
+#define CURSOR_MODE_64_ARGB_AX         ((1 << 5) | CURSOR_MODE_64_32B_AX)
+#define MCURSOR_GAMMA_ENABLE           (1 << 26)
+#define CURABASE               0x70084
+#define CURAPOS                        0x70088
+#define CURSOR_POS_MASK                        0x007FF
+#define CURSOR_POS_SIGN                        0x8000
+#define CURSOR_X_SHIFT                 0
+#define CURSOR_Y_SHIFT                 16
+#define CURBCNTR               0x700c0
+#define CURBBASE               0x700c4
+#define CURBPOS                        0x700c8
+#define CURCCNTR               0x700e0
+#define CURCBASE               0x700e4
+#define CURCPOS                        0x700e8
+
+/*
+ * Interrupt Registers
+ */
+#define IER                    0x020a0
+#define IIR                    0x020a4
+#define IMR                    0x020a8
+#define ISR                    0x020ac
+
+/*
+ * MOORESTOWN delta registers
+ */
+#define MRST_DPLL_A            0x0f014
+#define MDFLD_DPLL_B           0x0f018
+#define MDFLD_INPUT_REF_SEL            (1 << 14)
+#define MDFLD_VCO_SEL                  (1 << 16)
+#define DPLLA_MODE_LVDS                        (2 << 26)       /* mrst */
+#define MDFLD_PLL_LATCHEN              (1 << 28)
+#define MDFLD_PWR_GATE_EN              (1 << 30)
+#define MDFLD_P1_MASK                  (0x1FF << 17)
+#define MRST_FPA0              0x0f040
+#define MRST_FPA1              0x0f044
+#define MDFLD_DPLL_DIV0                0x0f048
+#define MDFLD_DPLL_DIV1                0x0f04c
+#define MRST_PERF_MODE         0x020f4
+
+/*
+ * MEDFIELD HDMI registers
+ */
+#define HDMIPHYMISCCTL         0x61134
+#define HDMI_PHY_POWER_DOWN            0x7f
+#define HDMIB_CONTROL          0x61140
+#define HDMIB_PORT_EN                  (1 << 31)
+#define HDMIB_PIPE_B_SELECT            (1 << 30)
+#define HDMIB_NULL_PACKET              (1 << 9)
+#define HDMIB_HDCP_PORT                        (1 << 5)
+
+/* #define LVDS                        0x61180 */
+#define MRST_PANEL_8TO6_DITHER_ENABLE  (1 << 25)
+#define MRST_PANEL_24_DOT_1_FORMAT     (1 << 24)
+#define LVDS_A3_POWER_UP_0_OUTPUT      (1 << 6)
+
+#define MIPI                   0x61190
+#define MIPI_C                 0x62190
+#define MIPI_PORT_EN                   (1 << 31)
+/* Turns on border drawing to allow centered display. */
+#define SEL_FLOPPED_HSTX               (1 << 23)
+#define PASS_FROM_SPHY_TO_AFE          (1 << 16)
+#define MIPI_BORDER_EN                 (1 << 15)
+#define MIPIA_3LANE_MIPIC_1LANE                0x1
+#define MIPIA_2LANE_MIPIC_2LANE                0x2
+#define TE_TRIGGER_DSI_PROTOCOL                (1 << 2)
+#define TE_TRIGGER_GPIO_PIN            (1 << 3)
+#define MIPI_TE_COUNT          0x61194
+
+/* #define PP_CONTROL  0x61204 */
+#define POWER_DOWN_ON_RESET            (1 << 1)
+
+/* #define PFIT_CONTROL        0x61230 */
+#define PFIT_PIPE_SELECT               (3 << 29)
+#define PFIT_PIPE_SELECT_SHIFT         (29)
+
+/* #define BLC_PWM_CTL         0x61254 */
+#define MRST_BACKLIGHT_MODULATION_FREQ_SHIFT   (16)
+#define MRST_BACKLIGHT_MODULATION_FREQ_MASK    (0xffff << 16)
+
+/* #define PIPEACONF 0x70008 */
+#define PIPEACONF_PIPE_STATE           (1 << 30)
+/* #define DSPACNTR            0x70180 */
+
+#define MRST_DSPABASE          0x7019c
+#define MRST_DSPBBASE          0x7119c
+#define MDFLD_DSPCBASE         0x7219c
+
+/*
+ * Moorestown registers.
+ */
+
+/*
+ *     MIPI IP registers
+ */
+#define MIPIC_REG_OFFSET               0x800
+
+#define DEVICE_READY_REG               0xb000
+#define LP_OUTPUT_HOLD                         (1 << 16)
+#define EXIT_ULPS_DEV_READY                    0x3
+#define LP_OUTPUT_HOLD_RELEASE                 0x810000
+# define ENTERING_ULPS                         (2 << 1)
+# define EXITING_ULPS                          (1 << 1)
+# define ULPS_MASK                             (3 << 1)
+# define BUS_POSSESSION                                (1 << 3)
+#define INTR_STAT_REG                  0xb004
+#define RX_SOT_ERROR                           (1 << 0)
+#define RX_SOT_SYNC_ERROR                      (1 << 1)
+#define RX_ESCAPE_MODE_ENTRY_ERROR             (1 << 3)
+#define RX_LP_TX_SYNC_ERROR                    (1 << 4)
+#define RX_HS_RECEIVE_TIMEOUT_ERROR            (1 << 5)
+#define RX_FALSE_CONTROL_ERROR                 (1 << 6)
+#define RX_ECC_SINGLE_BIT_ERROR                        (1 << 7)
+#define RX_ECC_MULTI_BIT_ERROR                 (1 << 8)
+#define RX_CHECKSUM_ERROR                      (1 << 9)
+#define RX_DSI_DATA_TYPE_NOT_RECOGNIZED                (1 << 10)
+#define RX_DSI_VC_ID_INVALID                   (1 << 11)
+#define TX_FALSE_CONTROL_ERROR                 (1 << 12)
+#define TX_ECC_SINGLE_BIT_ERROR                        (1 << 13)
+#define TX_ECC_MULTI_BIT_ERROR                 (1 << 14)
+#define TX_CHECKSUM_ERROR                      (1 << 15)
+#define TX_DSI_DATA_TYPE_NOT_RECOGNIZED                (1 << 16)
+#define TX_DSI_VC_ID_INVALID                   (1 << 17)
+#define HIGH_CONTENTION                                (1 << 18)
+#define LOW_CONTENTION                         (1 << 19)
+#define DPI_FIFO_UNDER_RUN                     (1 << 20)
+#define HS_TX_TIMEOUT                          (1 << 21)
+#define LP_RX_TIMEOUT                          (1 << 22)
+#define TURN_AROUND_ACK_TIMEOUT                        (1 << 23)
+#define ACK_WITH_NO_ERROR                      (1 << 24)
+#define HS_GENERIC_WR_FIFO_FULL                        (1 << 27)
+#define LP_GENERIC_WR_FIFO_FULL                        (1 << 28)
+#define SPL_PKT_SENT                           (1 << 30)
+#define INTR_EN_REG                    0xb008
+#define DSI_FUNC_PRG_REG               0xb00c
+#define DPI_CHANNEL_NUMBER_POS                 0x03
+#define DBI_CHANNEL_NUMBER_POS                 0x05
+#define FMT_DPI_POS                            0x07
+#define FMT_DBI_POS                            0x0A
+#define DBI_DATA_WIDTH_POS                     0x0D
+
+/* DPI PIXEL FORMATS */
+#define RGB_565_FMT                            0x01    /* RGB 565 FORMAT */
+#define RGB_666_FMT                            0x02    /* RGB 666 FORMAT */
+#define LRGB_666_FMT                           0x03    /* RGB LOOSELY PACKED
+                                                        * 666 FORMAT
+                                                        */
+#define RGB_888_FMT                            0x04    /* RGB 888 FORMAT */
+#define VIRTUAL_CHANNEL_NUMBER_0               0x00    /* Virtual channel 0 */
+#define VIRTUAL_CHANNEL_NUMBER_1               0x01    /* Virtual channel 1 */
+#define VIRTUAL_CHANNEL_NUMBER_2               0x02    /* Virtual channel 2 */
+#define VIRTUAL_CHANNEL_NUMBER_3               0x03    /* Virtual channel 3 */
+
+#define DBI_NOT_SUPPORTED                      0x00    /* command mode
+                                                        * is not supported
+                                                        */
+#define DBI_DATA_WIDTH_16BIT                   0x01    /* 16 bit data */
+#define DBI_DATA_WIDTH_9BIT                    0x02    /* 9 bit data */
+#define DBI_DATA_WIDTH_8BIT                    0x03    /* 8 bit data */
+#define DBI_DATA_WIDTH_OPT1                    0x04    /* option 1 */
+#define DBI_DATA_WIDTH_OPT2                    0x05    /* option 2 */
+
+#define HS_TX_TIMEOUT_REG              0xb010
+#define LP_RX_TIMEOUT_REG              0xb014
+#define TURN_AROUND_TIMEOUT_REG                0xb018
+#define DEVICE_RESET_REG               0xb01C
+#define DPI_RESOLUTION_REG             0xb020
+#define RES_V_POS                              0x10
+#define DBI_RESOLUTION_REG             0xb024 /* Reserved for MDFLD */
+#define HORIZ_SYNC_PAD_COUNT_REG       0xb028
+#define HORIZ_BACK_PORCH_COUNT_REG     0xb02C
+#define HORIZ_FRONT_PORCH_COUNT_REG    0xb030
+#define HORIZ_ACTIVE_AREA_COUNT_REG    0xb034
+#define VERT_SYNC_PAD_COUNT_REG                0xb038
+#define VERT_BACK_PORCH_COUNT_REG      0xb03c
+#define VERT_FRONT_PORCH_COUNT_REG     0xb040
+#define HIGH_LOW_SWITCH_COUNT_REG      0xb044
+#define DPI_CONTROL_REG                        0xb048
+#define DPI_SHUT_DOWN                          (1 << 0)
+#define DPI_TURN_ON                            (1 << 1)
+#define DPI_COLOR_MODE_ON                      (1 << 2)
+#define DPI_COLOR_MODE_OFF                     (1 << 3)
+#define DPI_BACK_LIGHT_ON                      (1 << 4)
+#define DPI_BACK_LIGHT_OFF                     (1 << 5)
+#define DPI_LP                                 (1 << 6)
+#define DPI_DATA_REG                   0xb04c
+#define DPI_BACK_LIGHT_ON_DATA                 0x07
+#define DPI_BACK_LIGHT_OFF_DATA                        0x17
+#define INIT_COUNT_REG                 0xb050
+#define MAX_RET_PAK_REG                        0xb054
+#define VIDEO_FMT_REG                  0xb058
+#define COMPLETE_LAST_PCKT                     (1 << 2)
+#define EOT_DISABLE_REG                        0xb05c
+#define ENABLE_CLOCK_STOPPING                  (1 << 1)
+#define LP_BYTECLK_REG                 0xb060
+#define LP_GEN_DATA_REG                        0xb064
+#define HS_GEN_DATA_REG                        0xb068
+#define LP_GEN_CTRL_REG                        0xb06C
+#define HS_GEN_CTRL_REG                        0xb070
+#define DCS_CHANNEL_NUMBER_POS         0x6
+#define MCS_COMMANDS_POS               0x8
+#define WORD_COUNTS_POS                        0x8
+#define MCS_PARAMETER_POS                      0x10
+#define GEN_FIFO_STAT_REG              0xb074
+#define HS_DATA_FIFO_FULL                      (1 << 0)
+#define HS_DATA_FIFO_HALF_EMPTY                        (1 << 1)
+#define HS_DATA_FIFO_EMPTY                     (1 << 2)
+#define LP_DATA_FIFO_FULL                      (1 << 8)
+#define LP_DATA_FIFO_HALF_EMPTY                        (1 << 9)
+#define LP_DATA_FIFO_EMPTY                     (1 << 10)
+#define HS_CTRL_FIFO_FULL                      (1 << 16)
+#define HS_CTRL_FIFO_HALF_EMPTY                        (1 << 17)
+#define HS_CTRL_FIFO_EMPTY                     (1 << 18)
+#define LP_CTRL_FIFO_FULL                      (1 << 24)
+#define LP_CTRL_FIFO_HALF_EMPTY                        (1 << 25)
+#define LP_CTRL_FIFO_EMPTY                     (1 << 26)
+#define DBI_FIFO_EMPTY                         (1 << 27)
+#define DPI_FIFO_EMPTY                         (1 << 28)
+#define HS_LS_DBI_ENABLE_REG           0xb078
+#define TXCLKESC_REG                   0xb07c
+#define DPHY_PARAM_REG                 0xb080
+#define DBI_BW_CTRL_REG                        0xb084
+#define CLK_LANE_SWT_REG               0xb088
+
+/*
+ * MIPI Adapter registers
+ */
+#define MIPI_CONTROL_REG               0xb104
+#define MIPI_2X_CLOCK_BITS                     ((1 << 0) | (1 << 1))
+#define MIPI_DATA_ADDRESS_REG          0xb108
+#define MIPI_DATA_LENGTH_REG           0xb10C
+#define MIPI_COMMAND_ADDRESS_REG       0xb110
+#define MIPI_COMMAND_LENGTH_REG                0xb114
+#define MIPI_READ_DATA_RETURN_REG0     0xb118
+#define MIPI_READ_DATA_RETURN_REG1     0xb11C
+#define MIPI_READ_DATA_RETURN_REG2     0xb120
+#define MIPI_READ_DATA_RETURN_REG3     0xb124
+#define MIPI_READ_DATA_RETURN_REG4     0xb128
+#define MIPI_READ_DATA_RETURN_REG5     0xb12C
+#define MIPI_READ_DATA_RETURN_REG6     0xb130
+#define MIPI_READ_DATA_RETURN_REG7     0xb134
+#define MIPI_READ_DATA_VALID_REG       0xb138
+
+/* DBI COMMANDS */
+#define soft_reset                     0x01
+/*
+ *     The display module performs a software reset.
+ *     Registers are written with their SW Reset default values.
+ */
+#define get_power_mode                 0x0a
+/*
+ *     The display module returns the current power mode
+ */
+#define get_address_mode               0x0b
+/*
+ *     The display module returns the current status.
+ */
+#define get_pixel_format               0x0c
+/*
+ *     This command gets the pixel format for the RGB image data
+ *     used by the interface.
+ */
+#define get_display_mode               0x0d
+/*
+ *     The display module returns the Display Image Mode status.
+ */
+#define get_signal_mode                        0x0e
+/*
+ *     The display module returns the Display Signal Mode.
+ */
+#define get_diagnostic_result          0x0f
+/*
+ *     The display module returns the self-diagnostic results following
+ *     a Sleep Out command.
+ */
+#define enter_sleep_mode               0x10
+/*
+ *     This command causes the display module to enter the Sleep mode.
+ *     In this mode, all unnecessary blocks inside the display module are
+ *     disabled except interface communication. This is the lowest power
+ *     mode the display module supports.
+ */
+#define exit_sleep_mode                        0x11
+/*
+ *     This command causes the display module to exit Sleep mode.
+ *     All blocks inside the display module are enabled.
+ */
+#define enter_partial_mode             0x12
+/*
+ *     This command causes the display module to enter the Partial Display
+ *     Mode. The Partial Display Mode window is described by the
+ *     set_partial_area command.
+ */
+#define enter_normal_mode              0x13
+/*
+ *     This command causes the display module to enter the Normal mode.
+ *     Normal Mode is defined as Partial Display mode and Scroll mode are off
+ */
+#define exit_invert_mode               0x20
+/*
+ *     This command causes the display module to stop inverting the image
+ *     data on the display device. The frame memory contents remain unchanged.
+ *     No status bits are changed.
+ */
+#define enter_invert_mode              0x21
+/*
+ *     This command causes the display module to invert the image data only on
+ *     the display device. The frame memory contents remain unchanged.
+ *     No status bits are changed.
+ */
+#define set_gamma_curve                        0x26
+/*
+ *     This command selects the desired gamma curve for the display device.
+ *     Four fixed gamma curves are defined in section DCS spec.
+ */
+#define set_display_off                        0x28
+/* ************************************************************************* *\
+This command causes the display module to stop displaying the image data
+on the display device. The frame memory contents remain unchanged.
+No status bits are changed.
+\* ************************************************************************* */
+#define set_display_on                 0x29
+/* ************************************************************************* *\
+This command causes the display module to start displaying the image data
+on the display device. The frame memory contents remain unchanged.
+No status bits are changed.
+\* ************************************************************************* */
+#define set_column_address             0x2a
+/*
+ *     This command defines the column extent of the frame memory accessed by
+ *     the hostprocessor with the read_memory_continue and
+ *     write_memory_continue commands.
+ *     No status bits are changed.
+ */
+#define set_page_addr                  0x2b
+/*
+ *     This command defines the page extent of the frame memory accessed by
+ *     the host processor with the write_memory_continue and
+ *     read_memory_continue command.
+ *     No status bits are changed.
+ */
+#define write_mem_start                        0x2c
+/*
+ *     This command transfers image data from the host processor to the
+ *     display modules frame memory starting at the pixel location specified
+ *     by preceding set_column_address and set_page_address commands.
+ */
+#define set_partial_area               0x30
+/*
+ *     This command defines the Partial Display mode s display area.
+ *     There are two parameters associated with this command, the first
+ *     defines the Start Row (SR) and the second the End Row (ER). SR and ER
+ *     refer to the Frame Memory Line Pointer.
+ */
+#define set_scroll_area                        0x33
+/*
+ *     This command defines the display modules Vertical Scrolling Area.
+ */
+#define set_tear_off                   0x34
+/*
+ *     This command turns off the display modules Tearing Effect output
+ *     signal on the TE signal line.
+ */
+#define set_tear_on                    0x35
+/*
+ *     This command turns on the display modules Tearing Effect output signal
+ *     on the TE signal line.
+ */
+#define set_address_mode               0x36
+/*
+ *     This command sets the data order for transfers from the host processor
+ *     to display modules frame memory,bits B[7:5] and B3, and from the
+ *     display modules frame memory to the display device, bits B[2:0] and B4.
+ */
+#define set_scroll_start               0x37
+/*
+ *     This command sets the start of the vertical scrolling area in the frame
+ *     memory. The vertical scrolling area is fully defined when this command
+ *     is used with the set_scroll_area command The set_scroll_start command
+ *     has one parameter, the Vertical Scroll Pointer. The VSP defines the
+ *     line in the frame memory that is written to the display device as the
+ *     first line of the vertical scroll area.
+ */
+#define exit_idle_mode                 0x38
+/*
+ *     This command causes the display module to exit Idle mode.
+ */
+#define enter_idle_mode                        0x39
+/*
+ *     This command causes the display module to enter Idle Mode.
+ *     In Idle Mode, color expression is reduced. Colors are shown on the
+ *     display device using the MSB of each of the R, G and B color
+ *     components in the frame memory
+ */
+#define set_pixel_format               0x3a
+/*
+ *     This command sets the pixel format for the RGB image data used by the
+ *     interface.
+ *     Bits D[6:4]  DPI Pixel Format Definition
+ *     Bits D[2:0]  DBI Pixel Format Definition
+ *     Bits D7 and D3 are not used.
+ */
+#define DCS_PIXEL_FORMAT_3bpp          0x1
+#define DCS_PIXEL_FORMAT_8bpp          0x2
+#define DCS_PIXEL_FORMAT_12bpp         0x3
+#define DCS_PIXEL_FORMAT_16bpp         0x5
+#define DCS_PIXEL_FORMAT_18bpp         0x6
+#define DCS_PIXEL_FORMAT_24bpp         0x7
+
+#define write_mem_cont                 0x3c
+
+/*
+ *     This command transfers image data from the host processor to the
+ *     display module's frame memory continuing from the pixel location
+ *     following the previous write_memory_continue or write_memory_start
+ *     command.
+ */
+#define set_tear_scanline              0x44
+/*
+ *     This command turns on the display modules Tearing Effect output signal
+ *     on the TE signal line when the display module reaches line N.
+ */
+#define get_scanline                   0x45
+/*
+ *     The display module returns the current scanline, N, used to update the
+ *      display device. The total number of scanlines on a display device is
+ *     defined as VSYNC + VBP + VACT + VFP.The first scanline is defined as
+ *     the first line of V Sync and is denoted as Line 0.
+ *     When in Sleep Mode, the value returned by get_scanline is undefined.
+ */
+
+/* MCS or Generic COMMANDS */
+/* MCS/generic data type */
+#define GEN_SHORT_WRITE_0      0x03  /* generic short write, no parameters */
+#define GEN_SHORT_WRITE_1      0x13  /* generic short write, 1 parameters */
+#define GEN_SHORT_WRITE_2      0x23  /* generic short write, 2 parameters */
+#define GEN_READ_0             0x04  /* generic read, no parameters */
+#define GEN_READ_1             0x14  /* generic read, 1 parameters */
+#define GEN_READ_2             0x24  /* generic read, 2 parameters */
+#define GEN_LONG_WRITE         0x29  /* generic long write */
+#define MCS_SHORT_WRITE_0      0x05  /* MCS short write, no parameters */
+#define MCS_SHORT_WRITE_1      0x15  /* MCS short write, 1 parameters */
+#define MCS_READ               0x06  /* MCS read, no parameters */
+#define MCS_LONG_WRITE         0x39  /* MCS long write */
+/* MCS/generic commands */
+/* TPO MCS */
+#define write_display_profile          0x50
+#define write_display_brightness       0x51
+#define write_ctrl_display             0x53
+#define write_ctrl_cabc                        0x55
+  #define UI_IMAGE             0x01
+  #define STILL_IMAGE          0x02
+  #define MOVING_IMAGE         0x03
+#define write_hysteresis               0x57
+#define write_gamma_setting            0x58
+#define write_cabc_min_bright          0x5e
+#define write_kbbc_profile             0x60
+/* TMD MCS */
+#define tmd_write_display_brightness 0x8c
+
+/*
+ *     This command is used to control ambient light, panel backlight
+ *     brightness and gamma settings.
+ */
+#define BRIGHT_CNTL_BLOCK_ON   (1 << 5)
+#define AMBIENT_LIGHT_SENSE_ON (1 << 4)
+#define DISPLAY_DIMMING_ON     (1 << 3)
+#define BACKLIGHT_ON           (1 << 2)
+#define DISPLAY_BRIGHTNESS_AUTO        (1 << 1)
+#define GAMMA_AUTO             (1 << 0)
+
+/* DCS Interface Pixel Formats */
+#define DCS_PIXEL_FORMAT_3BPP  0x1
+#define DCS_PIXEL_FORMAT_8BPP  0x2
+#define DCS_PIXEL_FORMAT_12BPP 0x3
+#define DCS_PIXEL_FORMAT_16BPP 0x5
+#define DCS_PIXEL_FORMAT_18BPP 0x6
+#define DCS_PIXEL_FORMAT_24BPP 0x7
+/* ONE PARAMETER READ DATA */
+#define addr_mode_data         0xfc
+#define diag_res_data          0x00
+#define disp_mode_data         0x23
+#define pxl_fmt_data           0x77
+#define pwr_mode_data          0x74
+#define sig_mode_data          0x00
+/* TWO PARAMETERS READ DATA */
+#define scanline_data1         0xff
+#define scanline_data2         0xff
+#define NON_BURST_MODE_SYNC_PULSE      0x01    /* Non Burst Mode
+                                                * with Sync Pulse
+                                                */
+#define NON_BURST_MODE_SYNC_EVENTS     0x02    /* Non Burst Mode
+                                                * with Sync events
+                                                */
+#define BURST_MODE                     0x03    /* Burst Mode */
+#define DBI_COMMAND_BUFFER_SIZE                0x240   /* 0x32 */    /* 0x120 */
+                                               /* Allocate at least
+                                                * 0x100 Byte with 32
+                                                * byte alignment
+                                                */
+#define DBI_DATA_BUFFER_SIZE           0x120   /* Allocate at least
+                                                * 0x100 Byte with 32
+                                                * byte alignment
+                                                */
+#define DBI_CB_TIME_OUT                        0xFFFF
+
+#define GEN_FB_TIME_OUT                        2000
+
+#define SKU_83                         0x01
+#define SKU_100                                0x02
+#define SKU_100L                       0x04
+#define SKU_BYPASS                     0x08
+
+/* Some handy macros for playing with bitfields. */
+#define PSB_MASK(high, low) (((1<<((high)-(low)+1))-1)<<(low))
+#define SET_FIELD(value, field) (((value) << field ## _SHIFT) & field ## _MASK)
+#define GET_FIELD(word, field) (((word)  & field ## _MASK) >> field ## _SHIFT)
+
+#define _PIPE(pipe, a, b) ((a) + (pipe)*((b)-(a)))
+
+/* PCI config space */
+
+#define SB_PCKT         0x02100 /* cedarview */
+# define SB_OPCODE_MASK                         PSB_MASK(31, 16)
+# define SB_OPCODE_SHIFT                        16
+# define SB_OPCODE_READ                         0
+# define SB_OPCODE_WRITE                        1
+# define SB_DEST_MASK                           PSB_MASK(15, 8)
+# define SB_DEST_SHIFT                          8
+# define SB_DEST_DPLL                           0x88
+# define SB_BYTE_ENABLE_MASK                    PSB_MASK(7, 4)
+# define SB_BYTE_ENABLE_SHIFT                   4
+# define SB_BUSY                                (1 << 0)
+
+
+/* 32-bit value read/written from the DPIO reg. */
+#define SB_DATA                0x02104 /* cedarview */
+/* 32-bit address of the DPIO reg to be read/written. */
+#define SB_ADDR                0x02108 /* cedarview */
+#define DPIO_CFG       0x02110 /* cedarview */
+# define DPIO_MODE_SELECT_1                    (1 << 3)
+# define DPIO_MODE_SELECT_0                    (1 << 2)
+# define DPIO_SFR_BYPASS                       (1 << 1)
+/* reset is active low */
+# define DPIO_CMN_RESET_N                      (1 << 0)
+
+/* Cedarview sideband registers */
+#define _SB_M_A                        0x8008
+#define _SB_M_B                        0x8028
+#define SB_M(pipe) _PIPE(pipe, _SB_M_A, _SB_M_B)
+# define SB_M_DIVIDER_MASK                     (0xFF << 24)
+# define SB_M_DIVIDER_SHIFT                    24
+
+#define _SB_N_VCO_A            0x8014
+#define _SB_N_VCO_B            0x8034
+#define SB_N_VCO(pipe) _PIPE(pipe, _SB_N_VCO_A, _SB_N_VCO_B)
+#define SB_N_VCO_SEL_MASK                      PSB_MASK(31, 30)
+#define SB_N_VCO_SEL_SHIFT                     30
+#define SB_N_DIVIDER_MASK                      PSB_MASK(29, 26)
+#define SB_N_DIVIDER_SHIFT                     26
+#define SB_N_CB_TUNE_MASK                      PSB_MASK(25, 24)
+#define SB_N_CB_TUNE_SHIFT                     24
+
+#define _SB_REF_A              0x8018
+#define _SB_REF_B              0x8038
+#define SB_REF_SFR(pipe)       _PIPE(pipe, _SB_REF_A, _SB_REF_B)
+
+#define _SB_P_A                        0x801c
+#define _SB_P_B                        0x803c
+#define SB_P(pipe) _PIPE(pipe, _SB_P_A, _SB_P_B)
+#define SB_P2_DIVIDER_MASK                     PSB_MASK(31, 30)
+#define SB_P2_DIVIDER_SHIFT                    30
+#define SB_P2_10                               0 /* HDMI, DP, DAC */
+#define SB_P2_5                                1 /* DAC */
+#define SB_P2_14                               2 /* LVDS single */
+#define SB_P2_7                                3 /* LVDS double */
+#define SB_P1_DIVIDER_MASK                     PSB_MASK(15, 12)
+#define SB_P1_DIVIDER_SHIFT                    12
+
+#define PSB_LANE0              0x120
+#define PSB_LANE1              0x220
+#define PSB_LANE2              0x2320
+#define PSB_LANE3              0x2420
+
+#define LANE_PLL_MASK          (0x7 << 20)
+#define LANE_PLL_ENABLE                (0x3 << 20)
+
+
+#endif
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
new file mode 100644 (file)
index 0000000..a4bad1a
--- /dev/null
@@ -0,0 +1,1293 @@
+/*
+ * Copyright (c) 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/delay.h>
+/* #include <drm/drm_crtc.h> */
+#include <drm/drmP.h>
+#include "psb_drv.h"
+#include "psb_intel_drv.h"
+#include "psb_intel_reg.h"
+#include "psb_intel_sdvo_regs.h"
+
+struct psb_intel_sdvo_priv {
+       struct psb_intel_i2c_chan *i2c_bus;
+       int slaveaddr;
+       int output_device;
+
+       u16 active_outputs;
+
+       struct psb_intel_sdvo_caps caps;
+       int pixel_clock_min, pixel_clock_max;
+
+       int save_sdvo_mult;
+       u16 save_active_outputs;
+       struct psb_intel_sdvo_dtd save_input_dtd_1, save_input_dtd_2;
+       struct psb_intel_sdvo_dtd save_output_dtd[16];
+       u32 save_SDVOX;
+       u8 in_out_map[4];
+
+       u8 by_input_wiring;
+       u32 active_device;
+};
+
+/**
+ * Writes the SDVOB or SDVOC with the given value, but always writes both
+ * SDVOB and SDVOC to work around apparent hardware issues (according to
+ * comments in the BIOS).
+ */
+void psb_intel_sdvo_write_sdvox(struct psb_intel_output *psb_intel_output,
+                               u32 val)
+{
+       struct drm_device *dev = psb_intel_output->base.dev;
+       struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv;
+       u32 bval = val, cval = val;
+       int i;
+
+       if (sdvo_priv->output_device == 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);
+       }
+}
+
+static bool psb_intel_sdvo_read_byte(
+                               struct psb_intel_output *psb_intel_output,
+                               u8 addr, u8 *ch)
+{
+       struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv;
+       u8 out_buf[2];
+       u8 buf[2];
+       int ret;
+
+       struct i2c_msg msgs[] = {
+               {
+                .addr = sdvo_priv->i2c_bus->slave_addr,
+                .flags = 0,
+                .len = 1,
+                .buf = out_buf,
+                },
+               {
+                .addr = sdvo_priv->i2c_bus->slave_addr,
+                .flags = I2C_M_RD,
+                .len = 1,
+                .buf = buf,
+                }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = 0;
+
+       ret = i2c_transfer(&sdvo_priv->i2c_bus->adapter, msgs, 2);
+       if (ret == 2) {
+               *ch = buf[0];
+               return true;
+       }
+
+       return false;
+}
+
+static bool psb_intel_sdvo_write_byte(
+                       struct psb_intel_output *psb_intel_output,
+                       int addr, u8 ch)
+{
+       u8 out_buf[2];
+       struct i2c_msg msgs[] = {
+               {
+                .addr = psb_intel_output->i2c_bus->slave_addr,
+                .flags = 0,
+                .len = 2,
+                .buf = out_buf,
+                }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = ch;
+
+       if (i2c_transfer(&psb_intel_output->i2c_bus->adapter, msgs, 1) == 1)
+               return true;
+       return false;
+}
+
+#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
+/** Mapping of command numbers to names, for debug output */
+static const struct _sdvo_cmd_name {
+       u8 cmd;
+       char *name;
+} sdvo_cmd_names[] = {
+SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
+           SDVO_CMD_NAME_ENTRY
+           (SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
+           SDVO_CMD_NAME_ENTRY
+           (SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
+           SDVO_CMD_NAME_ENTRY
+           (SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
+           SDVO_CMD_NAME_ENTRY
+           (SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
+           SDVO_CMD_NAME_ENTRY
+           (SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
+           SDVO_CMD_NAME_ENTRY
+           (SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
+           SDVO_CMD_NAME_ENTRY
+           (SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
+           SDVO_CMD_NAME_ENTRY
+           (SDVO_CMD_SET_TV_RESOLUTION_SUPPORT),
+           SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),};
+
+#define SDVO_NAME(dev_priv) \
+                ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
+#define SDVO_PRIV(output)   ((struct psb_intel_sdvo_priv *) (output)->dev_priv)
+
+static void psb_intel_sdvo_write_cmd(struct psb_intel_output *psb_intel_output,
+                                    u8 cmd,
+                                    void *args,
+                                    int args_len)
+{
+       struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv;
+       int i;
+
+       if (0) {
+               printk(KERN_DEBUG "%s: W: %02X ", SDVO_NAME(sdvo_priv), cmd);
+               for (i = 0; i < args_len; i++)
+                       printk(KERN_CONT "%02X ", ((u8 *) args)[i]);
+               for (; i < 8; i++)
+                       printk(KERN_CONT "   ");
+               for (i = 0;
+                    i <
+                    sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]);
+                    i++) {
+                       if (cmd == sdvo_cmd_names[i].cmd) {
+                               printk(KERN_CONT
+                                       "(%s)", sdvo_cmd_names[i].name);
+                               break;
+                       }
+               }
+               if (i ==
+                   sizeof(sdvo_cmd_names) / sizeof(sdvo_cmd_names[0]))
+                       printk(KERN_CONT "(%02X)", cmd);
+               printk(KERN_CONT "\n");
+       }
+
+       for (i = 0; i < args_len; i++) {
+               psb_intel_sdvo_write_byte(psb_intel_output,
+                                       SDVO_I2C_ARG_0 - i,
+                                       ((u8 *) args)[i]);
+       }
+
+       psb_intel_sdvo_write_byte(psb_intel_output, SDVO_I2C_OPCODE, cmd);
+}
+
+static const char *const cmd_status_names[] = {
+       "Power on",
+       "Success",
+       "Not supported",
+       "Invalid arg",
+       "Pending",
+       "Target not specified",
+       "Scaling not supported"
+};
+
+static u8 psb_intel_sdvo_read_response(
+                               struct psb_intel_output *psb_intel_output,
+                               void *response, int response_len)
+{
+       struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv;
+       int i;
+       u8 status;
+       u8 retry = 50;
+
+       while (retry--) {
+               /* Read the command response */
+               for (i = 0; i < response_len; i++) {
+                       psb_intel_sdvo_read_byte(psb_intel_output,
+                                            SDVO_I2C_RETURN_0 + i,
+                                            &((u8 *) response)[i]);
+               }
+
+               /* read the return status */
+               psb_intel_sdvo_read_byte(psb_intel_output,
+                                        SDVO_I2C_CMD_STATUS,
+                                        &status);
+
+               if (0) {
+                       pr_debug("%s: R: ", SDVO_NAME(sdvo_priv));
+                       for (i = 0; i < response_len; i++)
+                               printk(KERN_CONT "%02X ", ((u8 *) response)[i]);
+                       for (; i < 8; i++)
+                               printk("   ");
+                       if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
+                               printk(KERN_CONT "(%s)",
+                                        cmd_status_names[status]);
+                       else
+                               printk(KERN_CONT "(??? %d)", status);
+                       printk(KERN_CONT "\n");
+               }
+
+               if (status != SDVO_CMD_STATUS_PENDING)
+                       return status;
+
+               mdelay(50);
+       }
+
+       return status;
+}
+
+int psb_intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode)
+{
+       if (mode->clock >= 100000)
+               return 1;
+       else if (mode->clock >= 50000)
+               return 2;
+       else
+               return 4;
+}
+
+/**
+ * Don't check status code from this as it switches the bus back to the
+ * SDVO chips which defeats the purpose of doing a bus switch in the first
+ * place.
+ */
+void psb_intel_sdvo_set_control_bus_switch(
+                               struct psb_intel_output *psb_intel_output,
+                               u8 target)
+{
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                                SDVO_CMD_SET_CONTROL_BUS_SWITCH,
+                                &target,
+                                1);
+}
+
+static bool psb_intel_sdvo_set_target_input(
+                               struct psb_intel_output *psb_intel_output,
+                               bool target_0, bool target_1)
+{
+       struct psb_intel_sdvo_set_target_input_args targets = { 0 };
+       u8 status;
+
+       if (target_0 && target_1)
+               return SDVO_CMD_STATUS_NOTSUPP;
+
+       if (target_1)
+               targets.target_1 = 1;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_TARGET_INPUT,
+                            &targets, sizeof(targets));
+
+       status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0);
+
+       return status == SDVO_CMD_STATUS_SUCCESS;
+}
+
+/**
+ * Return whether each input is trained.
+ *
+ * This function is making an assumption about the layout of the response,
+ * which should be checked against the docs.
+ */
+static bool psb_intel_sdvo_get_trained_inputs(struct psb_intel_output
+                                         *psb_intel_output, bool *input_1,
+                                         bool *input_2)
+{
+       struct psb_intel_sdvo_get_trained_inputs_response response;
+       u8 status;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_GET_TRAINED_INPUTS,
+                            NULL, 0);
+       status =
+           psb_intel_sdvo_read_response(psb_intel_output, &response,
+                                    sizeof(response));
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return false;
+
+       *input_1 = response.input0_trained;
+       *input_2 = response.input1_trained;
+       return true;
+}
+
+static bool psb_intel_sdvo_get_active_outputs(struct psb_intel_output
+                                         *psb_intel_output, u16 *outputs)
+{
+       u8 status;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_GET_ACTIVE_OUTPUTS,
+                            NULL, 0);
+       status =
+           psb_intel_sdvo_read_response(psb_intel_output, outputs,
+                                    sizeof(*outputs));
+
+       return status == SDVO_CMD_STATUS_SUCCESS;
+}
+
+static bool psb_intel_sdvo_set_active_outputs(struct psb_intel_output
+                                         *psb_intel_output, u16 outputs)
+{
+       u8 status;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_ACTIVE_OUTPUTS,
+                            &outputs, sizeof(outputs));
+       status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0);
+       return status == SDVO_CMD_STATUS_SUCCESS;
+}
+
+static bool psb_intel_sdvo_set_encoder_power_state(struct psb_intel_output
+                                              *psb_intel_output, int mode)
+{
+       u8 status, state = SDVO_ENCODER_STATE_ON;
+
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+               state = SDVO_ENCODER_STATE_ON;
+               break;
+       case DRM_MODE_DPMS_STANDBY:
+               state = SDVO_ENCODER_STATE_STANDBY;
+               break;
+       case DRM_MODE_DPMS_SUSPEND:
+               state = SDVO_ENCODER_STATE_SUSPEND;
+               break;
+       case DRM_MODE_DPMS_OFF:
+               state = SDVO_ENCODER_STATE_OFF;
+               break;
+       }
+
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                            SDVO_CMD_SET_ENCODER_POWER_STATE, &state,
+                            sizeof(state));
+       status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0);
+
+       return status == SDVO_CMD_STATUS_SUCCESS;
+}
+
+static bool psb_intel_sdvo_get_input_pixel_clock_range(struct psb_intel_output
+                                                  *psb_intel_output,
+                                                  int *clock_min,
+                                                  int *clock_max)
+{
+       struct psb_intel_sdvo_pixel_clock_range clocks;
+       u8 status;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                            SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE, NULL,
+                            0);
+
+       status =
+           psb_intel_sdvo_read_response(psb_intel_output, &clocks,
+                                    sizeof(clocks));
+
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return false;
+
+       /* Convert the values from units of 10 kHz to kHz. */
+       *clock_min = clocks.min * 10;
+       *clock_max = clocks.max * 10;
+
+       return true;
+}
+
+static bool psb_intel_sdvo_set_target_output(
+                               struct psb_intel_output *psb_intel_output,
+                               u16 outputs)
+{
+       u8 status;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output, SDVO_CMD_SET_TARGET_OUTPUT,
+                            &outputs, sizeof(outputs));
+
+       status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0);
+       return status == SDVO_CMD_STATUS_SUCCESS;
+}
+
+static bool psb_intel_sdvo_get_timing(struct psb_intel_output *psb_intel_output,
+                                 u8 cmd, struct psb_intel_sdvo_dtd *dtd)
+{
+       u8 status;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output, cmd, NULL, 0);
+       status = psb_intel_sdvo_read_response(psb_intel_output, &dtd->part1,
+                                         sizeof(dtd->part1));
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return false;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output, cmd + 1, NULL, 0);
+       status = psb_intel_sdvo_read_response(psb_intel_output, &dtd->part2,
+                                         sizeof(dtd->part2));
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return false;
+
+       return true;
+}
+
+static bool psb_intel_sdvo_get_input_timing(
+                               struct psb_intel_output *psb_intel_output,
+                               struct psb_intel_sdvo_dtd *dtd)
+{
+       return psb_intel_sdvo_get_timing(psb_intel_output,
+                                    SDVO_CMD_GET_INPUT_TIMINGS_PART1,
+                                    dtd);
+}
+
+static bool psb_intel_sdvo_set_timing(
+                               struct psb_intel_output *psb_intel_output,
+                               u8 cmd,
+                               struct psb_intel_sdvo_dtd *dtd)
+{
+       u8 status;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output, cmd, &dtd->part1,
+                            sizeof(dtd->part1));
+       status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0);
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return false;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output, cmd + 1, &dtd->part2,
+                            sizeof(dtd->part2));
+       status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0);
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return false;
+
+       return true;
+}
+
+static bool psb_intel_sdvo_set_input_timing(
+                               struct psb_intel_output *psb_intel_output,
+                               struct psb_intel_sdvo_dtd *dtd)
+{
+       return psb_intel_sdvo_set_timing(psb_intel_output,
+                                    SDVO_CMD_SET_INPUT_TIMINGS_PART1,
+                                    dtd);
+}
+
+static bool psb_intel_sdvo_set_output_timing(
+                               struct psb_intel_output *psb_intel_output,
+                               struct psb_intel_sdvo_dtd *dtd)
+{
+       return psb_intel_sdvo_set_timing(psb_intel_output,
+                                    SDVO_CMD_SET_OUTPUT_TIMINGS_PART1,
+                                    dtd);
+}
+
+static int psb_intel_sdvo_get_clock_rate_mult(struct psb_intel_output
+                                               *psb_intel_output)
+{
+       u8 response, status;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                                SDVO_CMD_GET_CLOCK_RATE_MULT,
+                                NULL,
+                                0);
+
+       status = psb_intel_sdvo_read_response(psb_intel_output, &response, 1);
+
+       if (status != SDVO_CMD_STATUS_SUCCESS) {
+               DRM_DEBUG("Couldn't get SDVO clock rate multiplier\n");
+               return SDVO_CLOCK_RATE_MULT_1X;
+       } else {
+               DRM_DEBUG("Current clock rate multiplier: %d\n", response);
+       }
+
+       return response;
+}
+
+static bool psb_intel_sdvo_set_clock_rate_mult(struct psb_intel_output
+                                               *psb_intel_output, u8 val)
+{
+       u8 status;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                               SDVO_CMD_SET_CLOCK_RATE_MULT,
+                               &val,
+                               1);
+
+       status = psb_intel_sdvo_read_response(psb_intel_output, NULL, 0);
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return false;
+
+       return true;
+}
+
+static bool psb_sdvo_set_current_inoutmap(struct psb_intel_output *output,
+                                         u32 in0outputmask,
+                                         u32 in1outputmask)
+{
+       u8 byArgs[4];
+       u8 status;
+       int i;
+       struct psb_intel_sdvo_priv *sdvo_priv = output->dev_priv;
+
+       /* Make all fields of the  args/ret to zero */
+       memset(byArgs, 0, sizeof(byArgs));
+
+       /* Fill up the argument values; */
+       byArgs[0] = (u8) (in0outputmask & 0xFF);
+       byArgs[1] = (u8) ((in0outputmask >> 8) & 0xFF);
+       byArgs[2] = (u8) (in1outputmask & 0xFF);
+       byArgs[3] = (u8) ((in1outputmask >> 8) & 0xFF);
+
+
+       /*save inoutmap arg here*/
+       for (i = 0; i < 4; i++)
+               sdvo_priv->in_out_map[i] = byArgs[0];
+
+       psb_intel_sdvo_write_cmd(output, SDVO_CMD_SET_IN_OUT_MAP, byArgs, 4);
+       status = psb_intel_sdvo_read_response(output, NULL, 0);
+
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return false;
+       return true;
+}
+
+
+static void psb_intel_sdvo_set_iomap(struct psb_intel_output *output)
+{
+       u32 dwCurrentSDVOIn0 = 0;
+       u32 dwCurrentSDVOIn1 = 0;
+       u32 dwDevMask = 0;
+
+
+       struct psb_intel_sdvo_priv *sdvo_priv = output->dev_priv;
+
+       /* Please DO NOT change the following code. */
+       /* SDVOB_IN0 or SDVOB_IN1 ==> sdvo_in0 */
+       /* SDVOC_IN0 or SDVOC_IN1 ==> sdvo_in1 */
+       if (sdvo_priv->by_input_wiring & (SDVOB_IN0 | SDVOC_IN0)) {
+               switch (sdvo_priv->active_device) {
+               case SDVO_DEVICE_LVDS:
+                       dwDevMask = SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1;
+                       break;
+               case SDVO_DEVICE_TMDS:
+                       dwDevMask = SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1;
+                       break;
+               case SDVO_DEVICE_TV:
+                       dwDevMask =
+                       SDVO_OUTPUT_YPRPB0 | SDVO_OUTPUT_SVID0 |
+                       SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_YPRPB1 |
+                       SDVO_OUTPUT_SVID1 | SDVO_OUTPUT_CVBS1 |
+                       SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_SCART1;
+                       break;
+               case SDVO_DEVICE_CRT:
+                       dwDevMask = SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1;
+                       break;
+               }
+               dwCurrentSDVOIn0 = (sdvo_priv->active_outputs & dwDevMask);
+       } else if (sdvo_priv->by_input_wiring & (SDVOB_IN1 | SDVOC_IN1)) {
+               switch (sdvo_priv->active_device) {
+               case SDVO_DEVICE_LVDS:
+                       dwDevMask = SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1;
+                       break;
+               case SDVO_DEVICE_TMDS:
+                       dwDevMask = SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1;
+                       break;
+               case SDVO_DEVICE_TV:
+                       dwDevMask =
+                       SDVO_OUTPUT_YPRPB0 | SDVO_OUTPUT_SVID0 |
+                       SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_YPRPB1 |
+                       SDVO_OUTPUT_SVID1 | SDVO_OUTPUT_CVBS1 |
+                       SDVO_OUTPUT_SCART0 | SDVO_OUTPUT_SCART1;
+                       break;
+               case SDVO_DEVICE_CRT:
+                       dwDevMask = SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1;
+                       break;
+               }
+               dwCurrentSDVOIn1 = (sdvo_priv->active_outputs & dwDevMask);
+       }
+
+       psb_sdvo_set_current_inoutmap(output, dwCurrentSDVOIn0,
+                                         dwCurrentSDVOIn1);
+}
+
+
+static bool psb_intel_sdvo_mode_fixup(struct drm_encoder *encoder,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode)
+{
+       /* Make the CRTC code factor in the SDVO pixel multiplier.  The SDVO
+        * device will be told of the multiplier during mode_set.
+        */
+       adjusted_mode->clock *= psb_intel_sdvo_get_pixel_multiplier(mode);
+       return true;
+}
+
+static void psb_intel_sdvo_mode_set(struct drm_encoder *encoder,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode)
+{
+       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 psb_intel_output *psb_intel_output =
+                                       enc_to_psb_intel_output(encoder);
+       struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv;
+       u16 width, height;
+       u16 h_blank_len, h_sync_len, v_blank_len, v_sync_len;
+       u16 h_sync_offset, v_sync_offset;
+       u32 sdvox;
+       struct psb_intel_sdvo_dtd output_dtd;
+       int sdvo_pixel_multiply;
+
+       if (!mode)
+               return;
+
+       psb_intel_sdvo_set_target_output(psb_intel_output, 0);
+
+       width = mode->crtc_hdisplay;
+       height = mode->crtc_vdisplay;
+
+       /* do some mode translations */
+       h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start;
+       h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
+
+       v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start;
+       v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
+
+       h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start;
+       v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start;
+
+       output_dtd.part1.clock = mode->clock / 10;
+       output_dtd.part1.h_active = width & 0xff;
+       output_dtd.part1.h_blank = h_blank_len & 0xff;
+       output_dtd.part1.h_high = (((width >> 8) & 0xf) << 4) |
+           ((h_blank_len >> 8) & 0xf);
+       output_dtd.part1.v_active = height & 0xff;
+       output_dtd.part1.v_blank = v_blank_len & 0xff;
+       output_dtd.part1.v_high = (((height >> 8) & 0xf) << 4) |
+           ((v_blank_len >> 8) & 0xf);
+
+       output_dtd.part2.h_sync_off = h_sync_offset;
+       output_dtd.part2.h_sync_width = h_sync_len & 0xff;
+       output_dtd.part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
+           (v_sync_len & 0xf);
+       output_dtd.part2.sync_off_width_high =
+           ((h_sync_offset & 0x300) >> 2) | ((h_sync_len & 0x300) >> 4) |
+           ((v_sync_offset & 0x30) >> 2) | ((v_sync_len & 0x30) >> 4);
+
+       output_dtd.part2.dtd_flags = 0x18;
+       if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+               output_dtd.part2.dtd_flags |= 0x2;
+       if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+               output_dtd.part2.dtd_flags |= 0x4;
+
+       output_dtd.part2.sdvo_flags = 0;
+       output_dtd.part2.v_sync_off_high = v_sync_offset & 0xc0;
+       output_dtd.part2.reserved = 0;
+
+       /* Set the output timing to the screen */
+       psb_intel_sdvo_set_target_output(psb_intel_output,
+                                    sdvo_priv->active_outputs);
+
+       /* Set the input timing to the screen. Assume always input 0. */
+       psb_intel_sdvo_set_target_input(psb_intel_output, true, false);
+
+       psb_intel_sdvo_set_output_timing(psb_intel_output, &output_dtd);
+
+       /* We would like to use i830_sdvo_create_preferred_input_timing() to
+        * provide the device with a timing it can support, if it supports that
+        * feature.  However, presumably we would need to adjust the CRTC to
+        * output the preferred timing, and we don't support that currently.
+        */
+       psb_intel_sdvo_set_input_timing(psb_intel_output, &output_dtd);
+
+       switch (psb_intel_sdvo_get_pixel_multiplier(mode)) {
+       case 1:
+               psb_intel_sdvo_set_clock_rate_mult(psb_intel_output,
+                                              SDVO_CLOCK_RATE_MULT_1X);
+               break;
+       case 2:
+               psb_intel_sdvo_set_clock_rate_mult(psb_intel_output,
+                                              SDVO_CLOCK_RATE_MULT_2X);
+               break;
+       case 4:
+               psb_intel_sdvo_set_clock_rate_mult(psb_intel_output,
+                                              SDVO_CLOCK_RATE_MULT_4X);
+               break;
+       }
+
+       /* Set the SDVO control regs. */
+       sdvox = REG_READ(sdvo_priv->output_device);
+       switch (sdvo_priv->output_device) {
+       case SDVOB:
+               sdvox &= SDVOB_PRESERVE_MASK;
+               break;
+       case SDVOC:
+               sdvox &= SDVOC_PRESERVE_MASK;
+               break;
+       }
+       sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
+       if (psb_intel_crtc->pipe == 1)
+               sdvox |= SDVO_PIPE_B_SELECT;
+
+       sdvo_pixel_multiply = psb_intel_sdvo_get_pixel_multiplier(mode);
+
+       psb_intel_sdvo_write_sdvox(psb_intel_output, sdvox);
+
+        psb_intel_sdvo_set_iomap(psb_intel_output);
+}
+
+static void psb_intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
+{
+       struct drm_device *dev = encoder->dev;
+       struct psb_intel_output *psb_intel_output =
+                                       enc_to_psb_intel_output(encoder);
+       struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv;
+       u32 temp;
+
+       if (mode != DRM_MODE_DPMS_ON) {
+               psb_intel_sdvo_set_active_outputs(psb_intel_output, 0);
+               if (0)
+                       psb_intel_sdvo_set_encoder_power_state(
+                                                       psb_intel_output,
+                                                       mode);
+
+               if (mode == DRM_MODE_DPMS_OFF) {
+                       temp = REG_READ(sdvo_priv->output_device);
+                       if ((temp & SDVO_ENABLE) != 0) {
+                               psb_intel_sdvo_write_sdvox(psb_intel_output,
+                                                      temp &
+                                                      ~SDVO_ENABLE);
+                       }
+               }
+       } else {
+               bool input1, input2;
+               int i;
+               u8 status;
+
+               temp = REG_READ(sdvo_priv->output_device);
+               if ((temp & SDVO_ENABLE) == 0)
+                       psb_intel_sdvo_write_sdvox(psb_intel_output,
+                                              temp | SDVO_ENABLE);
+               for (i = 0; i < 2; i++)
+                       psb_intel_wait_for_vblank(dev);
+
+               status =
+                   psb_intel_sdvo_get_trained_inputs(psb_intel_output,
+                                                       &input1,
+                                                       &input2);
+
+
+               /* Warn if the device reported failure to sync.
+                * A lot of SDVO devices fail to notify of sync, but it's
+                * a given it the status is a success, we succeeded.
+                */
+               if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
+                       DRM_DEBUG
+                           ("First %s output reported failure to sync\n",
+                            SDVO_NAME(sdvo_priv));
+               }
+
+               if (0)
+                       psb_intel_sdvo_set_encoder_power_state(
+                                                       psb_intel_output,
+                                                       mode);
+               psb_intel_sdvo_set_active_outputs(psb_intel_output,
+                                             sdvo_priv->active_outputs);
+       }
+       return;
+}
+
+static void psb_intel_sdvo_save(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct psb_intel_output *psb_intel_output =
+                                       to_psb_intel_output(connector);
+       struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv;
+       /*int o;*/
+
+       sdvo_priv->save_sdvo_mult =
+           psb_intel_sdvo_get_clock_rate_mult(psb_intel_output);
+       psb_intel_sdvo_get_active_outputs(psb_intel_output,
+                                     &sdvo_priv->save_active_outputs);
+
+       if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+               psb_intel_sdvo_set_target_input(psb_intel_output,
+                                               true,
+                                               false);
+               psb_intel_sdvo_get_input_timing(psb_intel_output,
+                                           &sdvo_priv->save_input_dtd_1);
+       }
+
+       if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+               psb_intel_sdvo_set_target_input(psb_intel_output,
+                                               false,
+                                               true);
+               psb_intel_sdvo_get_input_timing(psb_intel_output,
+                                           &sdvo_priv->save_input_dtd_2);
+       }
+       sdvo_priv->save_SDVOX = REG_READ(sdvo_priv->output_device);
+
+       /*TODO: save the in_out_map state*/
+}
+
+static void psb_intel_sdvo_restore(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct psb_intel_output *psb_intel_output =
+                                       to_psb_intel_output(connector);
+       struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv;
+       /*int o;*/
+       int i;
+       bool input1, input2;
+       u8 status;
+
+       psb_intel_sdvo_set_active_outputs(psb_intel_output, 0);
+
+       if (sdvo_priv->caps.sdvo_inputs_mask & 0x1) {
+               psb_intel_sdvo_set_target_input(psb_intel_output, true, false);
+               psb_intel_sdvo_set_input_timing(psb_intel_output,
+                                           &sdvo_priv->save_input_dtd_1);
+       }
+
+       if (sdvo_priv->caps.sdvo_inputs_mask & 0x2) {
+               psb_intel_sdvo_set_target_input(psb_intel_output, false, true);
+               psb_intel_sdvo_set_input_timing(psb_intel_output,
+                                           &sdvo_priv->save_input_dtd_2);
+       }
+
+       psb_intel_sdvo_set_clock_rate_mult(psb_intel_output,
+                                      sdvo_priv->save_sdvo_mult);
+
+       REG_WRITE(sdvo_priv->output_device, sdvo_priv->save_SDVOX);
+
+       if (sdvo_priv->save_SDVOX & SDVO_ENABLE) {
+               for (i = 0; i < 2; i++)
+                       psb_intel_wait_for_vblank(dev);
+               status =
+                   psb_intel_sdvo_get_trained_inputs(psb_intel_output,
+                                                       &input1,
+                                                       &input2);
+               if (status == SDVO_CMD_STATUS_SUCCESS && !input1)
+                       DRM_DEBUG
+                           ("First %s output reported failure to sync\n",
+                            SDVO_NAME(sdvo_priv));
+       }
+
+       psb_intel_sdvo_set_active_outputs(psb_intel_output,
+                                     sdvo_priv->save_active_outputs);
+
+       /*TODO: restore in_out_map*/
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                                SDVO_CMD_SET_IN_OUT_MAP,
+                                sdvo_priv->in_out_map,
+                                4);
+
+       psb_intel_sdvo_read_response(psb_intel_output, NULL, 0);
+}
+
+static int psb_intel_sdvo_mode_valid(struct drm_connector *connector,
+                                struct drm_display_mode *mode)
+{
+       struct psb_intel_output *psb_intel_output =
+                               to_psb_intel_output(connector);
+       struct psb_intel_sdvo_priv *sdvo_priv = psb_intel_output->dev_priv;
+
+       if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+               return MODE_NO_DBLESCAN;
+
+       if (sdvo_priv->pixel_clock_min > mode->clock)
+               return MODE_CLOCK_LOW;
+
+       if (sdvo_priv->pixel_clock_max < mode->clock)
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+static bool psb_intel_sdvo_get_capabilities(
+                               struct psb_intel_output *psb_intel_output,
+                               struct psb_intel_sdvo_caps *caps)
+{
+       u8 status;
+
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                                SDVO_CMD_GET_DEVICE_CAPS,
+                                NULL,
+                                0);
+       status = psb_intel_sdvo_read_response(psb_intel_output,
+                                               caps,
+                                               sizeof(*caps));
+       if (status != SDVO_CMD_STATUS_SUCCESS)
+               return false;
+
+       return true;
+}
+
+struct drm_connector *psb_intel_sdvo_find(struct drm_device *dev, int sdvoB)
+{
+       struct drm_connector *connector = NULL;
+       struct psb_intel_output *iout = NULL;
+       struct psb_intel_sdvo_priv *sdvo;
+
+       /* find the sdvo connector */
+       list_for_each_entry(connector, &dev->mode_config.connector_list,
+                           head) {
+               iout = to_psb_intel_output(connector);
+
+               if (iout->type != INTEL_OUTPUT_SDVO)
+                       continue;
+
+               sdvo = iout->dev_priv;
+
+               if (sdvo->output_device == SDVOB && sdvoB)
+                       return connector;
+
+               if (sdvo->output_device == SDVOC && !sdvoB)
+                       return connector;
+
+       }
+
+       return NULL;
+}
+
+int psb_intel_sdvo_supports_hotplug(struct drm_connector *connector)
+{
+       u8 response[2];
+       u8 status;
+       struct psb_intel_output *psb_intel_output;
+
+       if (!connector)
+               return 0;
+
+       psb_intel_output = to_psb_intel_output(connector);
+
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                                SDVO_CMD_GET_HOT_PLUG_SUPPORT,
+                                NULL,
+                                0);
+       status = psb_intel_sdvo_read_response(psb_intel_output,
+                                               &response,
+                                               2);
+
+       if (response[0] != 0)
+               return 1;
+
+       return 0;
+}
+
+void psb_intel_sdvo_set_hotplug(struct drm_connector *connector, int on)
+{
+       u8 response[2];
+       u8 status;
+       struct psb_intel_output *psb_intel_output =
+                                       to_psb_intel_output(connector);
+
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                                SDVO_CMD_GET_ACTIVE_HOT_PLUG,
+                                NULL,
+                                0);
+       psb_intel_sdvo_read_response(psb_intel_output, &response, 2);
+
+       if (on) {
+               psb_intel_sdvo_write_cmd(psb_intel_output,
+                                    SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL,
+                                    0);
+               status = psb_intel_sdvo_read_response(psb_intel_output,
+                                                     &response,
+                                                     2);
+
+               psb_intel_sdvo_write_cmd(psb_intel_output,
+                                    SDVO_CMD_SET_ACTIVE_HOT_PLUG,
+                                    &response, 2);
+       } else {
+               response[0] = 0;
+               response[1] = 0;
+               psb_intel_sdvo_write_cmd(psb_intel_output,
+                                    SDVO_CMD_SET_ACTIVE_HOT_PLUG,
+                                    &response, 2);
+       }
+
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                                SDVO_CMD_GET_ACTIVE_HOT_PLUG,
+                                NULL,
+                                0);
+       psb_intel_sdvo_read_response(psb_intel_output, &response, 2);
+}
+
+static enum drm_connector_status psb_intel_sdvo_detect(struct drm_connector
+                                                  *connector, bool force)
+{
+       u8 response[2];
+       u8 status;
+       struct psb_intel_output *psb_intel_output =
+                                       to_psb_intel_output(connector);
+
+       psb_intel_sdvo_write_cmd(psb_intel_output,
+                                SDVO_CMD_GET_ATTACHED_DISPLAYS,
+                                NULL,
+                                0);
+       status = psb_intel_sdvo_read_response(psb_intel_output, &response, 2);
+
+       DRM_DEBUG("SDVO response %d %d\n", response[0], response[1]);
+       if ((response[0] != 0) || (response[1] != 0))
+               return connector_status_connected;
+       else
+               return connector_status_disconnected;
+}
+
+static int psb_intel_sdvo_get_modes(struct drm_connector *connector)
+{
+       struct psb_intel_output *psb_intel_output =
+                                       to_psb_intel_output(connector);
+
+       /* set the bus switch and get the modes */
+       psb_intel_sdvo_set_control_bus_switch(psb_intel_output,
+                                         SDVO_CONTROL_BUS_DDC2);
+       psb_intel_ddc_get_modes(psb_intel_output);
+
+       if (list_empty(&connector->probed_modes))
+               return 0;
+       return 1;
+}
+
+static void psb_intel_sdvo_destroy(struct drm_connector *connector)
+{
+       struct psb_intel_output *psb_intel_output =
+                               to_psb_intel_output(connector);
+
+       if (psb_intel_output->i2c_bus)
+               psb_intel_i2c_destroy(psb_intel_output->i2c_bus);
+       drm_sysfs_connector_remove(connector);
+       drm_connector_cleanup(connector);
+       kfree(psb_intel_output);
+}
+
+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,
+       .mode_set = psb_intel_sdvo_mode_set,
+       .commit = psb_intel_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,
+       .destroy = psb_intel_sdvo_destroy,
+};
+
+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,
+};
+
+void psb_intel_sdvo_enc_destroy(struct drm_encoder *encoder)
+{
+       drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs psb_intel_sdvo_enc_funcs = {
+       .destroy = psb_intel_sdvo_enc_destroy,
+};
+
+
+void psb_intel_sdvo_init(struct drm_device *dev, int output_device)
+{
+       struct drm_connector *connector;
+       struct psb_intel_output *psb_intel_output;
+       struct psb_intel_sdvo_priv *sdvo_priv;
+       struct psb_intel_i2c_chan *i2cbus = NULL;
+       int connector_type;
+       u8 ch[0x40];
+       int i;
+       int encoder_type, output_id;
+
+       psb_intel_output =
+           kcalloc(sizeof(struct psb_intel_output) +
+                   sizeof(struct psb_intel_sdvo_priv), 1, GFP_KERNEL);
+       if (!psb_intel_output)
+               return;
+
+       connector = &psb_intel_output->base;
+
+       drm_connector_init(dev, connector, &psb_intel_sdvo_connector_funcs,
+                          DRM_MODE_CONNECTOR_Unknown);
+       drm_connector_helper_add(connector,
+                                &psb_intel_sdvo_connector_helper_funcs);
+       sdvo_priv = (struct psb_intel_sdvo_priv *) (psb_intel_output + 1);
+       psb_intel_output->type = INTEL_OUTPUT_SDVO;
+
+       connector->interlace_allowed = 0;
+       connector->doublescan_allowed = 0;
+
+       /* setup the DDC bus. */
+       if (output_device == SDVOB)
+               i2cbus =
+                   psb_intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOB");
+       else
+               i2cbus =
+                   psb_intel_i2c_create(dev, GPIOE, "SDVOCTRL_E for SDVOC");
+
+       if (!i2cbus)
+               goto err_connector;
+
+       sdvo_priv->i2c_bus = i2cbus;
+
+       if (output_device == SDVOB) {
+               output_id = 1;
+               sdvo_priv->by_input_wiring = SDVOB_IN0;
+               sdvo_priv->i2c_bus->slave_addr = 0x38;
+       } else {
+               output_id = 2;
+               sdvo_priv->i2c_bus->slave_addr = 0x39;
+       }
+
+       sdvo_priv->output_device = output_device;
+       psb_intel_output->i2c_bus = i2cbus;
+       psb_intel_output->dev_priv = sdvo_priv;
+
+
+       /* Read the regs to test if we can talk to the device */
+       for (i = 0; i < 0x40; i++) {
+               if (!psb_intel_sdvo_read_byte(psb_intel_output, i, &ch[i])) {
+                       dev_dbg(dev->dev, "No SDVO device found on SDVO%c\n",
+                                 output_device == SDVOB ? 'B' : 'C');
+                       goto err_i2c;
+               }
+       }
+
+       psb_intel_sdvo_get_capabilities(psb_intel_output, &sdvo_priv->caps);
+
+       memset(&sdvo_priv->active_outputs, 0,
+              sizeof(sdvo_priv->active_outputs));
+
+       /* TODO, CVBS, SVID, YPRPB & SCART outputs. */
+       if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB0) {
+               sdvo_priv->active_outputs = SDVO_OUTPUT_RGB0;
+               sdvo_priv->active_device = SDVO_DEVICE_CRT;
+               connector->display_info.subpixel_order =
+                   SubPixelHorizontalRGB;
+               encoder_type = DRM_MODE_ENCODER_DAC;
+               connector_type = DRM_MODE_CONNECTOR_VGA;
+       } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_RGB1) {
+               sdvo_priv->active_outputs = SDVO_OUTPUT_RGB1;
+               sdvo_priv->active_outputs = SDVO_DEVICE_CRT;
+               connector->display_info.subpixel_order =
+                   SubPixelHorizontalRGB;
+               encoder_type = DRM_MODE_ENCODER_DAC;
+               connector_type = DRM_MODE_CONNECTOR_VGA;
+       } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS0) {
+               sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS0;
+               sdvo_priv->active_device = SDVO_DEVICE_TMDS;
+               connector->display_info.subpixel_order =
+                   SubPixelHorizontalRGB;
+               encoder_type = DRM_MODE_ENCODER_TMDS;
+               connector_type = DRM_MODE_CONNECTOR_DVID;
+       } else if (sdvo_priv->caps.output_flags & SDVO_OUTPUT_TMDS1) {
+               sdvo_priv->active_outputs = SDVO_OUTPUT_TMDS1;
+               sdvo_priv->active_device = SDVO_DEVICE_TMDS;
+               connector->display_info.subpixel_order =
+                   SubPixelHorizontalRGB;
+               encoder_type = DRM_MODE_ENCODER_TMDS;
+               connector_type = DRM_MODE_CONNECTOR_DVID;
+       } else {
+               unsigned char bytes[2];
+
+               memcpy(bytes, &sdvo_priv->caps.output_flags, 2);
+               dev_dbg(dev->dev, "%s: No active RGB or TMDS outputs (0x%02x%02x)\n",
+                    SDVO_NAME(sdvo_priv), bytes[0], bytes[1]);
+               goto err_i2c;
+       }
+
+       drm_encoder_init(dev, &psb_intel_output->enc, &psb_intel_sdvo_enc_funcs,
+                        encoder_type);
+       drm_encoder_helper_add(&psb_intel_output->enc,
+                              &psb_intel_sdvo_helper_funcs);
+       connector->connector_type = connector_type;
+
+       drm_mode_connector_attach_encoder(&psb_intel_output->base,
+                                         &psb_intel_output->enc);
+       drm_sysfs_connector_add(connector);
+
+       /* Set the input timing to the screen. Assume always input 0. */
+       psb_intel_sdvo_set_target_input(psb_intel_output, true, false);
+
+       psb_intel_sdvo_get_input_pixel_clock_range(psb_intel_output,
+                                              &sdvo_priv->pixel_clock_min,
+                                              &sdvo_priv->
+                                              pixel_clock_max);
+
+
+       dev_dbg(dev->dev, "%s device VID/DID: %02X:%02X.%02X, "
+                 "clock range %dMHz - %dMHz, "
+                 "input 1: %c, input 2: %c, "
+                 "output 1: %c, output 2: %c\n",
+                 SDVO_NAME(sdvo_priv),
+                 sdvo_priv->caps.vendor_id, sdvo_priv->caps.device_id,
+                 sdvo_priv->caps.device_rev_id,
+                 sdvo_priv->pixel_clock_min / 1000,
+                 sdvo_priv->pixel_clock_max / 1000,
+                 (sdvo_priv->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
+                 (sdvo_priv->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
+                 /* check currently supported outputs */
+                 sdvo_priv->caps.output_flags &
+                 (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N',
+                 sdvo_priv->caps.output_flags &
+                 (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N');
+
+       psb_intel_output->ddc_bus = i2cbus;
+
+       return;
+
+err_i2c:
+       psb_intel_i2c_destroy(psb_intel_output->i2c_bus);
+err_connector:
+       drm_connector_cleanup(connector);
+       kfree(psb_intel_output);
+
+       return;
+}
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo_regs.h b/drivers/gpu/drm/gma500/psb_intel_sdvo_regs.h
new file mode 100644 (file)
index 0000000..96862ea
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * SDVO command definitions and structures.
+ *
+ * Copyright (c) 2008, 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>
+ */
+
+#define SDVO_OUTPUT_FIRST   (0)
+#define SDVO_OUTPUT_TMDS0   (1 << 0)
+#define SDVO_OUTPUT_RGB0    (1 << 1)
+#define SDVO_OUTPUT_CVBS0   (1 << 2)
+#define SDVO_OUTPUT_SVID0   (1 << 3)
+#define SDVO_OUTPUT_YPRPB0  (1 << 4)
+#define SDVO_OUTPUT_SCART0  (1 << 5)
+#define SDVO_OUTPUT_LVDS0   (1 << 6)
+#define SDVO_OUTPUT_TMDS1   (1 << 8)
+#define SDVO_OUTPUT_RGB1    (1 << 9)
+#define SDVO_OUTPUT_CVBS1   (1 << 10)
+#define SDVO_OUTPUT_SVID1   (1 << 11)
+#define SDVO_OUTPUT_YPRPB1  (1 << 12)
+#define SDVO_OUTPUT_SCART1  (1 << 13)
+#define SDVO_OUTPUT_LVDS1   (1 << 14)
+#define SDVO_OUTPUT_LAST    (14)
+
+struct psb_intel_sdvo_caps {
+       u8 vendor_id;
+       u8 device_id;
+       u8 device_rev_id;
+       u8 sdvo_version_major;
+       u8 sdvo_version_minor;
+       unsigned int sdvo_inputs_mask:2;
+       unsigned int smooth_scaling:1;
+       unsigned int sharp_scaling:1;
+       unsigned int up_scaling:1;
+       unsigned int down_scaling:1;
+       unsigned int stall_support:1;
+       unsigned int pad:1;
+       u16 output_flags;
+} __packed;
+
+/** This matches the EDID DTD structure, more or less */
+struct psb_intel_sdvo_dtd {
+       struct {
+               u16 clock;      /**< pixel clock, in 10kHz units */
+               u8 h_active;    /**< lower 8 bits (pixels) */
+               u8 h_blank;     /**< lower 8 bits (pixels) */
+               u8 h_high;      /**< upper 4 bits each h_active, h_blank */
+               u8 v_active;    /**< lower 8 bits (lines) */
+               u8 v_blank;     /**< lower 8 bits (lines) */
+               u8 v_high;      /**< upper 4 bits each v_active, v_blank */
+       } part1;
+
+       struct {
+               u8 h_sync_off;
+                       /**< lower 8 bits, from hblank start */
+               u8 h_sync_width;/**< lower 8 bits (pixels) */
+       /** lower 4 bits each vsync offset, vsync width */
+               u8 v_sync_off_width;
+       /**
+        * 2 high bits of hsync offset, 2 high bits of hsync width,
+        * bits 4-5 of vsync offset, and 2 high bits of vsync width.
+        */
+               u8 sync_off_width_high;
+               u8 dtd_flags;
+               u8 sdvo_flags;
+       /** bits 6-7 of vsync offset at bits 6-7 */
+               u8 v_sync_off_high;
+               u8 reserved;
+       } part2;
+} __packed;
+
+struct psb_intel_sdvo_pixel_clock_range {
+       u16 min;                /**< pixel clock, in 10kHz units */
+       u16 max;                /**< pixel clock, in 10kHz units */
+} __packed;
+
+struct psb_intel_sdvo_preferred_input_timing_args {
+       u16 clock;
+       u16 width;
+       u16 height;
+} __packed;
+
+/* I2C registers for SDVO */
+#define SDVO_I2C_ARG_0                         0x07
+#define SDVO_I2C_ARG_1                         0x06
+#define SDVO_I2C_ARG_2                         0x05
+#define SDVO_I2C_ARG_3                         0x04
+#define SDVO_I2C_ARG_4                         0x03
+#define SDVO_I2C_ARG_5                         0x02
+#define SDVO_I2C_ARG_6                         0x01
+#define SDVO_I2C_ARG_7                         0x00
+#define SDVO_I2C_OPCODE                                0x08
+#define SDVO_I2C_CMD_STATUS                    0x09
+#define SDVO_I2C_RETURN_0                      0x0a
+#define SDVO_I2C_RETURN_1                      0x0b
+#define SDVO_I2C_RETURN_2                      0x0c
+#define SDVO_I2C_RETURN_3                      0x0d
+#define SDVO_I2C_RETURN_4                      0x0e
+#define SDVO_I2C_RETURN_5                      0x0f
+#define SDVO_I2C_RETURN_6                      0x10
+#define SDVO_I2C_RETURN_7                      0x11
+#define SDVO_I2C_VENDOR_BEGIN                  0x20
+
+/* Status results */
+#define SDVO_CMD_STATUS_POWER_ON               0x0
+#define SDVO_CMD_STATUS_SUCCESS                        0x1
+#define SDVO_CMD_STATUS_NOTSUPP                        0x2
+#define SDVO_CMD_STATUS_INVALID_ARG            0x3
+#define SDVO_CMD_STATUS_PENDING                        0x4
+#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED   0x5
+#define SDVO_CMD_STATUS_SCALING_NOT_SUPP       0x6
+
+/* SDVO commands, argument/result registers */
+
+#define SDVO_CMD_RESET                                 0x01
+
+/** Returns a struct psb_intel_sdvo_caps */
+#define SDVO_CMD_GET_DEVICE_CAPS                       0x02
+
+#define SDVO_CMD_GET_FIRMWARE_REV                      0x86
+# define SDVO_DEVICE_FIRMWARE_MINOR                    SDVO_I2C_RETURN_0
+# define SDVO_DEVICE_FIRMWARE_MAJOR                    SDVO_I2C_RETURN_1
+# define SDVO_DEVICE_FIRMWARE_PATCH                    SDVO_I2C_RETURN_2
+
+/**
+ * Reports which inputs are trained (managed to sync).
+ *
+ * Devices must have trained within 2 vsyncs of a mode change.
+ */
+#define SDVO_CMD_GET_TRAINED_INPUTS                    0x03
+struct psb_intel_sdvo_get_trained_inputs_response {
+       unsigned int input0_trained:1;
+       unsigned int input1_trained:1;
+       unsigned int pad:6;
+} __packed;
+
+/** Returns a struct psb_intel_sdvo_output_flags of active outputs. */
+#define SDVO_CMD_GET_ACTIVE_OUTPUTS                    0x04
+
+/**
+ * Sets the current set of active outputs.
+ *
+ * Takes a struct psb_intel_sdvo_output_flags.
+ * Must be preceded by a SET_IN_OUT_MAP
+ * on multi-output devices.
+ */
+#define SDVO_CMD_SET_ACTIVE_OUTPUTS                    0x05
+
+/**
+ * Returns the current mapping of SDVO inputs to outputs on the device.
+ *
+ * Returns two struct psb_intel_sdvo_output_flags structures.
+ */
+#define SDVO_CMD_GET_IN_OUT_MAP                                0x06
+
+/**
+ * Sets the current mapping of SDVO inputs to outputs on the device.
+ *
+ * Takes two struct i380_sdvo_output_flags structures.
+ */
+#define SDVO_CMD_SET_IN_OUT_MAP                                0x07
+
+/**
+ * Returns a struct psb_intel_sdvo_output_flags of attached displays.
+ */
+#define SDVO_CMD_GET_ATTACHED_DISPLAYS                 0x0b
+
+/**
+ * Returns a struct psb_intel_sdvo_ouptut_flags of displays supporting hot plugging.
+ */
+#define SDVO_CMD_GET_HOT_PLUG_SUPPORT                  0x0c
+
+/**
+ * Takes a struct psb_intel_sdvo_output_flags.
+ */
+#define SDVO_CMD_SET_ACTIVE_HOT_PLUG                   0x0d
+
+/**
+ * Returns a struct psb_intel_sdvo_output_flags of displays with hot plug
+ * interrupts enabled.
+ */
+#define SDVO_CMD_GET_ACTIVE_HOT_PLUG                   0x0e
+
+#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE            0x0f
+struct psb_intel_sdvo_get_interrupt_event_source_response {
+       u16 interrupt_status;
+       unsigned int ambient_light_interrupt:1;
+       unsigned int pad:7;
+} __packed;
+
+/**
+ * Selects which input is affected by future input commands.
+ *
+ * Commands affected include SET_INPUT_TIMINGS_PART[12],
+ * GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12],
+ * GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS.
+ */
+#define SDVO_CMD_SET_TARGET_INPUT                      0x10
+struct psb_intel_sdvo_set_target_input_args {
+       unsigned int target_1:1;
+       unsigned int pad:7;
+} __packed;
+
+/**
+ * Takes a struct psb_intel_sdvo_output_flags of which outputs are targeted by
+ * future output commands.
+ *
+ * Affected commands inclue SET_OUTPUT_TIMINGS_PART[12],
+ * GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE.
+ */
+#define SDVO_CMD_SET_TARGET_OUTPUT                     0x11
+
+#define SDVO_CMD_GET_INPUT_TIMINGS_PART1               0x12
+#define SDVO_CMD_GET_INPUT_TIMINGS_PART2               0x13
+#define SDVO_CMD_SET_INPUT_TIMINGS_PART1               0x14
+#define SDVO_CMD_SET_INPUT_TIMINGS_PART2               0x15
+#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1              0x16
+#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2              0x17
+#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1              0x18
+#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2              0x19
+/* Part 1 */
+# define SDVO_DTD_CLOCK_LOW                            SDVO_I2C_ARG_0
+# define SDVO_DTD_CLOCK_HIGH                           SDVO_I2C_ARG_1
+# define SDVO_DTD_H_ACTIVE                             SDVO_I2C_ARG_2
+# define SDVO_DTD_H_BLANK                              SDVO_I2C_ARG_3
+# define SDVO_DTD_H_HIGH                               SDVO_I2C_ARG_4
+# define SDVO_DTD_V_ACTIVE                             SDVO_I2C_ARG_5
+# define SDVO_DTD_V_BLANK                              SDVO_I2C_ARG_6
+# define SDVO_DTD_V_HIGH                               SDVO_I2C_ARG_7
+/* Part 2 */
+# define SDVO_DTD_HSYNC_OFF                            SDVO_I2C_ARG_0
+# define SDVO_DTD_HSYNC_WIDTH                          SDVO_I2C_ARG_1
+# define SDVO_DTD_VSYNC_OFF_WIDTH                      SDVO_I2C_ARG_2
+# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH                  SDVO_I2C_ARG_3
+# define SDVO_DTD_DTD_FLAGS                            SDVO_I2C_ARG_4
+# define SDVO_DTD_DTD_FLAG_INTERLACED                          (1 << 7)
+# define SDVO_DTD_DTD_FLAG_STEREO_MASK                         (3 << 5)
+# define SDVO_DTD_DTD_FLAG_INPUT_MASK                          (3 << 3)
+# define SDVO_DTD_DTD_FLAG_SYNC_MASK                           (3 << 1)
+# define SDVO_DTD_SDVO_FLAS                            SDVO_I2C_ARG_5
+# define SDVO_DTD_SDVO_FLAG_STALL                              (1 << 7)
+# define SDVO_DTD_SDVO_FLAG_CENTERED                           (0 << 6)
+# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT                         (1 << 6)
+# define SDVO_DTD_SDVO_FLAG_SCALING_MASK                       (3 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_NONE                       (0 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP                      (1 << 4)
+# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH                     (2 << 4)
+# define SDVO_DTD_VSYNC_OFF_HIGH                       SDVO_I2C_ARG_6
+
+/**
+ * Generates a DTD based on the given width, height, and flags.
+ *
+ * This will be supported by any device supporting scaling or interlaced
+ * modes.
+ */
+#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING         0x1a
+# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW         SDVO_I2C_ARG_0
+# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH                SDVO_I2C_ARG_1
+# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW         SDVO_I2C_ARG_2
+# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH                SDVO_I2C_ARG_3
+# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW                SDVO_I2C_ARG_4
+# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH       SDVO_I2C_ARG_5
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS             SDVO_I2C_ARG_6
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED          (1 << 0)
+# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED              (1 << 1)
+
+#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1      0x1b
+#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2      0x1c
+
+/** Returns a struct psb_intel_sdvo_pixel_clock_range */
+#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE           0x1d
+/** Returns a struct psb_intel_sdvo_pixel_clock_range */
+#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE          0x1e
+
+/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */
+#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS                0x1f
+
+/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
+#define SDVO_CMD_GET_CLOCK_RATE_MULT                   0x20
+/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
+#define SDVO_CMD_SET_CLOCK_RATE_MULT                   0x21
+# define SDVO_CLOCK_RATE_MULT_1X                               (1 << 0)
+# define SDVO_CLOCK_RATE_MULT_2X                               (1 << 1)
+# define SDVO_CLOCK_RATE_MULT_4X                               (1 << 3)
+
+#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS              0x27
+
+#define SDVO_CMD_GET_TV_FORMAT                         0x28
+
+#define SDVO_CMD_SET_TV_FORMAT                         0x29
+
+#define SDVO_CMD_GET_SUPPORTED_POWER_STATES            0x2a
+#define SDVO_CMD_GET_ENCODER_POWER_STATE               0x2b
+#define SDVO_CMD_SET_ENCODER_POWER_STATE               0x2c
+# define SDVO_ENCODER_STATE_ON                                 (1 << 0)
+# define SDVO_ENCODER_STATE_STANDBY                            (1 << 1)
+# define SDVO_ENCODER_STATE_SUSPEND                            (1 << 2)
+# define SDVO_ENCODER_STATE_OFF                                        (1 << 3)
+
+#define SDVO_CMD_SET_TV_RESOLUTION_SUPPORT             0x93
+
+#define SDVO_CMD_SET_CONTROL_BUS_SWITCH                        0x7a
+# define SDVO_CONTROL_BUS_PROM                         0x0
+# define SDVO_CONTROL_BUS_DDC1                         0x1
+# define SDVO_CONTROL_BUS_DDC2                         0x2
+# define SDVO_CONTROL_BUS_DDC3                         0x3
+
+/* SDVO Bus & SDVO Inputs wiring details*/
+/* Bit 0: Is SDVOB connected to In0 (1 = yes, 0 = no*/
+/* Bit 1: Is SDVOB connected to In1 (1 = yes, 0 = no*/
+/* Bit 2: Is SDVOC connected to In0 (1 = yes, 0 = no*/
+/* Bit 3: Is SDVOC connected to In1 (1 = yes, 0 = no*/
+#define SDVOB_IN0 0x01
+#define SDVOB_IN1 0x02
+#define SDVOC_IN0 0x04
+#define SDVOC_IN1 0x08
+
+#define SDVO_DEVICE_NONE 0x00
+#define        SDVO_DEVICE_CRT 0x01
+#define        SDVO_DEVICE_TV 0x02
+#define        SDVO_DEVICE_LVDS 0x04
+#define        SDVO_DEVICE_TMDS 0x08
+
diff --git a/drivers/gpu/drm/gma500/psb_lid.c b/drivers/gpu/drm/gma500/psb_lid.c
new file mode 100644 (file)
index 0000000..af32851
--- /dev/null
@@ -0,0 +1,90 @@
+/**************************************************************************
+ * Copyright (c) 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: Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
+ **************************************************************************/
+
+#include <drm/drmP.h>
+#include "psb_drv.h"
+#include "psb_reg.h"
+#include "psb_intel_reg.h"
+#include <linux/spinlock.h>
+
+static void psb_lid_timer_func(unsigned long data)
+{
+       struct drm_psb_private * dev_priv = (struct drm_psb_private *)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 pp_status;
+
+       if (readl(lid_state) == dev_priv->lid_last_state)
+               goto lid_timer_schedule;
+
+       if ((readl(lid_state)) & 0x01) {
+               /*lid state is open*/
+               REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | POWER_TARGET_ON);
+               do {
+                       pp_status = REG_READ(PP_STATUS);
+               } while ((pp_status & PP_ON) == 0);
+
+               /*FIXME: should be backlight level before*/
+               psb_intel_lvds_set_brightness(dev, 100);
+       } else {
+               psb_intel_lvds_set_brightness(dev, 0);
+
+               REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & ~POWER_TARGET_ON);
+               do {
+                       pp_status = REG_READ(PP_STATUS);
+               } while ((pp_status & PP_ON) == 0);
+       }
+               /* printk(KERN_INFO"%s: lid: closed\n", __FUNCTION__); */
+
+       dev_priv->lid_last_state =  readl(lid_state);
+
+lid_timer_schedule:
+       spin_lock_irqsave(&dev_priv->lid_lock, irq_flags);
+       if (!timer_pending(lid_timer)) {
+               lid_timer->expires = jiffies + PSB_LID_DELAY;
+               add_timer(lid_timer);
+       }
+       spin_unlock_irqrestore(&dev_priv->lid_lock, irq_flags);
+}
+
+void psb_lid_timer_init(struct drm_psb_private *dev_priv)
+{
+       struct timer_list *lid_timer = &dev_priv->lid_timer;
+       unsigned long irq_flags;
+
+       spin_lock_init(&dev_priv->lid_lock);
+       spin_lock_irqsave(&dev_priv->lid_lock, irq_flags);
+
+       init_timer(lid_timer);
+
+       lid_timer->data = (unsigned long)dev_priv;
+       lid_timer->function = psb_lid_timer_func;
+       lid_timer->expires = jiffies + PSB_LID_DELAY;
+
+       add_timer(lid_timer);
+       spin_unlock_irqrestore(&dev_priv->lid_lock, irq_flags);
+}
+
+void psb_lid_timer_takedown(struct drm_psb_private *dev_priv)
+{
+       del_timer_sync(&dev_priv->lid_timer);
+}
+