]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
gma500: Add Oaktrail support
authorAlan Cox <alan@linux.intel.com>
Thu, 3 Nov 2011 18:22:26 +0000 (18:22 +0000)
committerDave Airlie <airlied@redhat.com>
Wed, 16 Nov 2011 11:27:12 +0000 (11:27 +0000)
Oaktrail (GMA600) is found on some tablet/slate PC type systems. It's a bit
different to the GMA500 but similar enough it makes sense to plug it into
the same driver.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/gma500/oaktrail.h [new file with mode: 0644]
drivers/gpu/drm/gma500/oaktrail_crtc.c [new file with mode: 0644]
drivers/gpu/drm/gma500/oaktrail_device.c [new file with mode: 0644]
drivers/gpu/drm/gma500/oaktrail_hdmi.c [new file with mode: 0644]
drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c [new file with mode: 0644]
drivers/gpu/drm/gma500/oaktrail_lvds.c [new file with mode: 0644]

diff --git a/drivers/gpu/drm/gma500/oaktrail.h b/drivers/gpu/drm/gma500/oaktrail.h
new file mode 100644 (file)
index 0000000..2da1f36
--- /dev/null
@@ -0,0 +1,252 @@
+/**************************************************************************
+ * Copyright (c) 2007-2011, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * 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.
+ *
+ **************************************************************************/
+
+/* MID device specific descriptors */
+
+struct oaktrail_vbt {
+       s8 signature[4];        /*4 bytes,"$GCT" */
+       u8 revision;
+       u8 size;
+       u8 checksum;
+       void *oaktrail_gct;
+} __packed;
+
+struct oaktrail_timing_info {
+       u16 pixel_clock;
+       u8 hactive_lo;
+       u8 hblank_lo;
+       u8 hblank_hi:4;
+       u8 hactive_hi:4;
+       u8 vactive_lo;
+       u8 vblank_lo;
+       u8 vblank_hi:4;
+       u8 vactive_hi:4;
+       u8 hsync_offset_lo;
+       u8 hsync_pulse_width_lo;
+       u8 vsync_pulse_width_lo:4;
+       u8 vsync_offset_lo:4;
+       u8 vsync_pulse_width_hi:2;
+       u8 vsync_offset_hi:2;
+       u8 hsync_pulse_width_hi:2;
+       u8 hsync_offset_hi:2;
+       u8 width_mm_lo;
+       u8 height_mm_lo;
+       u8 height_mm_hi:4;
+       u8 width_mm_hi:4;
+       u8 hborder;
+       u8 vborder;
+       u8 unknown0:1;
+       u8 hsync_positive:1;
+       u8 vsync_positive:1;
+       u8 separate_sync:2;
+       u8 stereo:1;
+       u8 unknown6:1;
+       u8 interlaced:1;
+} __packed;
+
+struct gct_r10_timing_info {
+       u16 pixel_clock;
+       u32 hactive_lo:8;
+       u32 hactive_hi:4;
+       u32 hblank_lo:8;
+       u32 hblank_hi:4;
+       u32 hsync_offset_lo:8;
+       u16 hsync_offset_hi:2;
+       u16 hsync_pulse_width_lo:8;
+       u16 hsync_pulse_width_hi:2;
+       u16 hsync_positive:1;
+       u16 rsvd_1:3;
+       u8  vactive_lo:8;
+       u16 vactive_hi:4;
+       u16 vblank_lo:8;
+       u16 vblank_hi:4;
+       u16 vsync_offset_lo:4;
+       u16 vsync_offset_hi:2;
+       u16 vsync_pulse_width_lo:4;
+       u16 vsync_pulse_width_hi:2;
+       u16 vsync_positive:1;
+       u16 rsvd_2:3;
+} __packed;
+
+struct oaktrail_panel_descriptor_v1 {
+       u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */
+                               /* 0x61190 if MIPI */
+       u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/
+       u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/
+       u32 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 dword */
+                                               /* Register 0x61210 */
+       struct oaktrail_timing_info DTD;/*18 bytes, Standard definition */
+       u16 Panel_Backlight_Inverter_Descriptor;/* 16 bits, as follows */
+                               /* Bit 0, Frequency, 15 bits,0 - 32767Hz */
+                       /* Bit 15, Polarity, 1 bit, 0: Normal, 1: Inverted */
+       u16 Panel_MIPI_Display_Descriptor;
+                       /*16 bits, Defined as follows: */
+                       /* if MIPI, 0x0000 if LVDS */
+                       /* Bit 0, Type, 2 bits, */
+                       /* 0: Type-1, */
+                       /* 1: Type-2, */
+                       /* 2: Type-3, */
+                       /* 3: Type-4 */
+                       /* Bit 2, Pixel Format, 4 bits */
+                       /* Bit0: 16bpp (not supported in LNC), */
+                       /* Bit1: 18bpp loosely packed, */
+                       /* Bit2: 18bpp packed, */
+                       /* Bit3: 24bpp */
+                       /* Bit 6, Reserved, 2 bits, 00b */
+                       /* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */
+                       /* Bit 14, Reserved, 2 bits, 00b */
+} __packed;
+
+struct oaktrail_panel_descriptor_v2 {
+       u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */
+                               /* 0x61190 if MIPI */
+       u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/
+       u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/
+       u8 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 byte */
+                                               /* Register 0x61210 */
+       struct oaktrail_timing_info DTD;/*18 bytes, Standard definition */
+       u16 Panel_Backlight_Inverter_Descriptor;/*16 bits, as follows*/
+                               /*Bit 0, Frequency, 16 bits, 0 - 32767Hz*/
+       u8 Panel_Initial_Brightness;/* [7:0] 0 - 100% */
+                       /*Bit 7, Polarity, 1 bit,0: Normal, 1: Inverted*/
+       u16 Panel_MIPI_Display_Descriptor;
+                       /*16 bits, Defined as follows: */
+                       /* if MIPI, 0x0000 if LVDS */
+                       /* Bit 0, Type, 2 bits, */
+                       /* 0: Type-1, */
+                       /* 1: Type-2, */
+                       /* 2: Type-3, */
+                       /* 3: Type-4 */
+                       /* Bit 2, Pixel Format, 4 bits */
+                       /* Bit0: 16bpp (not supported in LNC), */
+                       /* Bit1: 18bpp loosely packed, */
+                       /* Bit2: 18bpp packed, */
+                       /* Bit3: 24bpp */
+                       /* Bit 6, Reserved, 2 bits, 00b */
+                       /* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */
+                       /* Bit 14, Reserved, 2 bits, 00b */
+} __packed;
+
+union oaktrail_panel_rx {
+       struct {
+               u16 NumberOfLanes:2; /*Num of Lanes, 2 bits,0 = 1 lane,*/
+                       /* 1 = 2 lanes, 2 = 3 lanes, 3 = 4 lanes. */
+               u16 MaxLaneFreq:3; /* 0: 100MHz, 1: 200MHz, 2: 300MHz, */
+               /*3: 400MHz, 4: 500MHz, 5: 600MHz, 6: 700MHz, 7: 800MHz.*/
+               u16 SupportedVideoTransferMode:2; /*0: Non-burst only */
+                                       /* 1: Burst and non-burst */
+                                       /* 2/3: Reserved */
+               u16 HSClkBehavior:1; /*0: Continuous, 1: Non-continuous*/
+               u16 DuoDisplaySupport:1; /*1 bit,0: No, 1: Yes*/
+               u16 ECC_ChecksumCapabilities:1;/*1 bit,0: No, 1: Yes*/
+               u16 BidirectionalCommunication:1;/*1 bit,0: No, 1: Yes */
+               u16 Rsvd:5;/*5 bits,00000b */
+       } panelrx;
+       u16 panel_receiver;
+} __packed;
+
+struct oaktrail_gct_v1 {
+       union { /*8 bits,Defined as follows: */
+               struct {
+                       u8 PanelType:4; /*4 bits, Bit field for panels*/
+                                       /* 0 - 3: 0 = LVDS, 1 = MIPI*/
+                                       /*2 bits,Specifies which of the*/
+                       u8 BootPanelIndex:2;
+                                       /* 4 panels to use by default*/
+                       u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/
+                                       /* the 4 MIPI DSI receivers to use*/
+               } PD;
+               u8 PanelDescriptor;
+       };
+       struct oaktrail_panel_descriptor_v1 panel[4];/*panel descrs,38 bytes each*/
+       union oaktrail_panel_rx panelrx[4]; /* panel receivers*/
+} __packed;
+
+struct oaktrail_gct_v2 {
+       union { /*8 bits,Defined as follows: */
+               struct {
+                       u8 PanelType:4; /*4 bits, Bit field for panels*/
+                                       /* 0 - 3: 0 = LVDS, 1 = MIPI*/
+                                       /*2 bits,Specifies which of the*/
+                       u8 BootPanelIndex:2;
+                                       /* 4 panels to use by default*/
+                       u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/
+                                       /* the 4 MIPI DSI receivers to use*/
+               } PD;
+               u8 PanelDescriptor;
+       };
+       struct oaktrail_panel_descriptor_v2 panel[4];/*panel descrs,38 bytes each*/
+       union oaktrail_panel_rx panelrx[4]; /* panel receivers*/
+} __packed;
+
+struct oaktrail_gct_data {
+       u8 bpi; /* boot panel index, number of panel used during boot */
+       u8 pt; /* panel type, 4 bit field, 0=lvds, 1=mipi */
+       struct oaktrail_timing_info DTD; /* timing info for the selected panel */
+       u32 Panel_Port_Control;
+       u32 PP_On_Sequencing;/*1 dword,Register 0x61208,*/
+       u32 PP_Off_Sequencing;/*1 dword,Register 0x6120C,*/
+       u32 PP_Cycle_Delay;
+       u16 Panel_Backlight_Inverter_Descriptor;
+       u16 Panel_MIPI_Display_Descriptor;
+} __packed;
+
+#define MODE_SETTING_IN_CRTC           0x1
+#define MODE_SETTING_IN_ENCODER                0x2
+#define MODE_SETTING_ON_GOING          0x3
+#define MODE_SETTING_IN_DSR            0x4
+#define MODE_SETTING_ENCODER_DONE      0x8
+
+#define GCT_R10_HEADER_SIZE            16
+#define GCT_R10_DISPLAY_DESC_SIZE      28
+
+/*
+ *     Moorestown HDMI interfaces
+ */
+
+struct oaktrail_hdmi_dev {
+       struct pci_dev *dev;
+       void __iomem *regs;
+       unsigned int mmio, mmio_len;
+       int dpms_mode;
+       struct hdmi_i2c_dev *i2c_dev;
+
+       /* register state */
+       u32 saveDPLL_CTRL;
+       u32 saveDPLL_DIV_CTRL;
+       u32 saveDPLL_ADJUST;
+       u32 saveDPLL_UPDATE;
+       u32 saveDPLL_CLK_ENABLE;
+       u32 savePCH_HTOTAL_B;
+       u32 savePCH_HBLANK_B;
+       u32 savePCH_HSYNC_B;
+       u32 savePCH_VTOTAL_B;
+       u32 savePCH_VBLANK_B;
+       u32 savePCH_VSYNC_B;
+       u32 savePCH_PIPEBCONF;
+       u32 savePCH_PIPEBSRC;
+};
+
+extern void oaktrail_hdmi_setup(struct drm_device *dev);
+extern void oaktrail_hdmi_teardown(struct drm_device *dev);
+extern int  oaktrail_hdmi_i2c_init(struct pci_dev *dev);
+extern void oaktrail_hdmi_i2c_exit(struct pci_dev *dev);
+extern void oaktrail_hdmi_save(struct drm_device *dev);
+extern void oaktrail_hdmi_restore(struct drm_device *dev);
+extern void oaktrail_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev);
diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c
new file mode 100644 (file)
index 0000000..7f9c791
--- /dev/null
@@ -0,0 +1,610 @@
+/*
+ * Copyright © 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.
+ */
+
+#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_range_t {
+       int min, max;
+};
+
+struct oaktrail_limit_t {
+       struct psb_intel_range_t dot, m, p1;
+};
+
+struct oaktrail_clock_t {
+       /* derived values */
+       int dot;
+       int m;
+       int p1;
+};
+
+#define MRST_LIMIT_LVDS_100L       0
+#define MRST_LIMIT_LVDS_83         1
+#define MRST_LIMIT_LVDS_100        2
+
+#define MRST_DOT_MIN             19750
+#define MRST_DOT_MAX             120000
+#define MRST_M_MIN_100L                    20
+#define MRST_M_MIN_100             10
+#define MRST_M_MIN_83              12
+#define MRST_M_MAX_100L                    34
+#define MRST_M_MAX_100             17
+#define MRST_M_MAX_83              20
+#define MRST_P1_MIN                2
+#define MRST_P1_MAX_0              7
+#define MRST_P1_MAX_1              8
+
+static const struct oaktrail_limit_t oaktrail_limits[] = {
+       {                       /* MRST_LIMIT_LVDS_100L */
+        .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX},
+        .m = {.min = MRST_M_MIN_100L, .max = MRST_M_MAX_100L},
+        .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1},
+        },
+       {                       /* MRST_LIMIT_LVDS_83L */
+        .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX},
+        .m = {.min = MRST_M_MIN_83, .max = MRST_M_MAX_83},
+        .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_0},
+        },
+       {                       /* MRST_LIMIT_LVDS_100 */
+        .dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX},
+        .m = {.min = MRST_M_MIN_100, .max = MRST_M_MAX_100},
+        .p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1},
+        },
+};
+
+#define MRST_M_MIN         10
+static const u32 oaktrail_m_converts[] = {
+       0x2B, 0x15, 0x2A, 0x35, 0x1A, 0x0D, 0x26, 0x33, 0x19, 0x2C,
+       0x36, 0x3B, 0x1D, 0x2E, 0x37, 0x1B, 0x2D, 0x16, 0x0B, 0x25,
+       0x12, 0x09, 0x24, 0x32, 0x39, 0x1c,
+};
+
+static const struct oaktrail_limit_t *oaktrail_limit(struct drm_crtc *crtc)
+{
+       const struct oaktrail_limit_t *limit = NULL;
+       struct drm_device *dev = crtc->dev;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+
+       if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)
+           || psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) {
+               switch (dev_priv->core_freq) {
+               case 100:
+                       limit = &oaktrail_limits[MRST_LIMIT_LVDS_100L];
+                       break;
+               case 166:
+                       limit = &oaktrail_limits[MRST_LIMIT_LVDS_83];
+                       break;
+               case 200:
+                       limit = &oaktrail_limits[MRST_LIMIT_LVDS_100];
+                       break;
+               }
+       } else {
+               limit = NULL;
+               dev_err(dev->dev, "oaktrail_limit Wrong display type.\n");
+       }
+
+       return limit;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
+static void oaktrail_clock(int refclk, struct oaktrail_clock_t *clock)
+{
+       clock->dot = (refclk * clock->m) / (14 * clock->p1);
+}
+
+void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock)
+{
+       pr_debug("%s: dotclock = %d,  m = %d, p1 = %d.\n",
+            prefix, clock->dot, clock->m, clock->p1);
+}
+
+/**
+ * Returns a set of divisors for the desired target clock with the given refclk,
+ * or FALSE.  Divisor values are the actual divisors for
+ */
+static bool
+mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk,
+               struct oaktrail_clock_t *best_clock)
+{
+       struct oaktrail_clock_t clock;
+       const struct oaktrail_limit_t *limit = oaktrail_limit(crtc);
+       int err = target;
+
+       memset(best_clock, 0, sizeof(*best_clock));
+
+       for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
+               for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max;
+                    clock.p1++) {
+                       int this_err;
+
+                       oaktrail_clock(refclk, &clock);
+
+                       this_err = abs(clock.dot - target);
+                       if (this_err < err) {
+                               *best_clock = clock;
+                               err = this_err;
+                       }
+               }
+       }
+       dev_dbg(crtc->dev->dev, "mrstFindBestPLL err = %d.\n", err);
+       return err != target;
+}
+
+/**
+ * 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 oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
+       int pipe = psb_intel_crtc->pipe;
+       int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B;
+       int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+       int dspbase_reg = (pipe == 0) ? MRST_DSPABASE : DSPBBASE;
+       int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+       u32 temp;
+       bool enabled;
+
+       if (!gma_power_begin(dev, true))
+               return;
+
+       /* XXX: When our outputs are all unaware of DPMS modes other than off
+        * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
+        */
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+               /* Enable the DPLL */
+               temp = REG_READ(dpll_reg);
+               if ((temp & DPLL_VCO_ENABLE) == 0) {
+                       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 for the pipe 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, 0x3FFF);
+       REG_WRITE(DSPFW1, 0x3F88080A);
+       REG_WRITE(DSPFW2, 0x0b060808);
+       REG_WRITE(DSPFW3, 0x0);
+       REG_WRITE(DSPFW4, 0x08030404);
+       REG_WRITE(DSPFW5, 0x04040404);
+       REG_WRITE(DSPFW6, 0x78);
+       REG_WRITE(0x70400, REG_READ(0x70400) | 0x4000);
+       /* Must write Bit 14 of the Chicken Bit Register */
+
+       gma_power_end(dev);
+}
+
+/**
+ * Return the pipe currently connected to the panel fitter,
+ * or -1 if the panel fitter is not present or not in use
+ */
+static int oaktrail_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;
+       return (pfit_control >> 29) & 3;
+}
+
+static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
+                             struct drm_display_mode *mode,
+                             struct drm_display_mode *adjusted_mode,
+                             int x, int y,
+                             struct drm_framebuffer *old_fb)
+{
+       struct drm_device *dev = crtc->dev;
+       struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       int pipe = psb_intel_crtc->pipe;
+       int fp_reg = (pipe == 0) ? MRST_FPA0 : FPB0;
+       int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B;
+       int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+       int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+       int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+       int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+       int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+       int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+       int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+       int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+       int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
+       int refclk = 0;
+       struct oaktrail_clock_t clock;
+       u32 dpll = 0, fp = 0, dspcntr, pipeconf;
+       bool ok, is_sdvo = false;
+       bool is_crt = false, is_lvds = false, is_tv = false;
+       bool is_mipi = false;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct psb_intel_output *psb_intel_output = NULL;
+       uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN;
+       struct drm_encoder *encoder;
+
+       if (!gma_power_begin(dev, true))
+               return 0;
+
+       memcpy(&psb_intel_crtc->saved_mode,
+               mode,
+               sizeof(struct drm_display_mode));
+       memcpy(&psb_intel_crtc->saved_adjusted_mode,
+               adjusted_mode,
+               sizeof(struct drm_display_mode));
+
+       list_for_each_entry(encoder, &mode_config->encoder_list, head) {
+
+               if (encoder->crtc != crtc)
+                       continue;
+
+               psb_intel_output = enc_to_psb_intel_output(encoder);
+               switch (psb_intel_output->type) {
+               case INTEL_OUTPUT_LVDS:
+                       is_lvds = true;
+                       break;
+               case INTEL_OUTPUT_SDVO:
+                       is_sdvo = true;
+                       break;
+               case INTEL_OUTPUT_TVOUT:
+                       is_tv = true;
+                       break;
+               case INTEL_OUTPUT_ANALOG:
+                       is_crt = true;
+                       break;
+               case INTEL_OUTPUT_MIPI:
+                       is_mipi = true;
+                       break;
+               }
+       }
+
+       /* Disable the VGA plane that we never use */
+       REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+
+       /* Disable the panel fitter if it was on our pipe */
+       if (oaktrail_panel_fitter_pipe(dev) == pipe)
+               REG_WRITE(PFIT_CONTROL, 0);
+
+       REG_WRITE(pipesrc_reg,
+                 ((mode->crtc_hdisplay - 1) << 16) |
+                 (mode->crtc_vdisplay - 1));
+
+       if (psb_intel_output)
+               drm_connector_property_get_value(&psb_intel_output->base,
+                       dev->mode_config.scaling_mode_property, &scalingType);
+
+       if (scalingType == DRM_MODE_SCALE_NO_SCALE) {
+               /* Moorestown doesn't have register support for centering so
+                * we need to mess with the h/vblank and h/vsync start and
+                * ends to get centering */
+               int offsetX = 0, offsetY = 0;
+
+               offsetX = (adjusted_mode->crtc_hdisplay -
+                          mode->crtc_hdisplay) / 2;
+               offsetY = (adjusted_mode->crtc_vdisplay -
+                          mode->crtc_vdisplay) / 2;
+
+               REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) |
+                       ((adjusted_mode->crtc_htotal - 1) << 16));
+               REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) |
+                       ((adjusted_mode->crtc_vtotal - 1) << 16));
+               REG_WRITE(hblank_reg,
+                       (adjusted_mode->crtc_hblank_start - offsetX - 1) |
+                       ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16));
+               REG_WRITE(hsync_reg,
+                       (adjusted_mode->crtc_hsync_start - offsetX - 1) |
+                       ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16));
+               REG_WRITE(vblank_reg,
+                       (adjusted_mode->crtc_vblank_start - offsetY - 1) |
+                       ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16));
+               REG_WRITE(vsync_reg,
+                       (adjusted_mode->crtc_vsync_start - offsetY - 1) |
+                       ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16));
+       } else {
+               REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
+                       ((adjusted_mode->crtc_htotal - 1) << 16));
+               REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
+                       ((adjusted_mode->crtc_vtotal - 1) << 16));
+               REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) |
+                       ((adjusted_mode->crtc_hblank_end - 1) << 16));
+               REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) |
+                       ((adjusted_mode->crtc_hsync_end - 1) << 16));
+               REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) |
+                       ((adjusted_mode->crtc_vblank_end - 1) << 16));
+               REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) |
+                       ((adjusted_mode->crtc_vsync_end - 1) << 16));
+       }
+
+       /* Flush the plane changes */
+       {
+               struct drm_crtc_helper_funcs *crtc_funcs =
+                   crtc->helper_private;
+               crtc_funcs->mode_set_base(crtc, x, y, old_fb);
+       }
+
+       /* setup pipeconf */
+       pipeconf = REG_READ(pipeconf_reg);
+
+       /* Set up the display plane register */
+       dspcntr = REG_READ(dspcntr_reg);
+       dspcntr |= DISPPLANE_GAMMA_ENABLE;
+
+       if (pipe == 0)
+               dspcntr |= DISPPLANE_SEL_PIPE_A;
+       else
+               dspcntr |= DISPPLANE_SEL_PIPE_B;
+
+       dev_priv->dspcntr = dspcntr |= DISPLAY_PLANE_ENABLE;
+       dev_priv->pipeconf = pipeconf |= PIPEACONF_ENABLE;
+
+       if (is_mipi)
+               goto oaktrail_crtc_mode_set_exit;
+
+       refclk = dev_priv->core_freq * 1000;
+
+       dpll = 0;               /*BIT16 = 0 for 100MHz reference */
+
+       ok = mrstFindBestPLL(crtc, adjusted_mode->clock, refclk, &clock);
+
+       if (!ok) {
+               dev_dbg(dev->dev, "mrstFindBestPLL fail in oaktrail_crtc_mode_set.\n");
+       } else {
+               dev_dbg(dev->dev, "oaktrail_crtc_mode_set pixel clock = %d,"
+                        "m = %x, p1 = %x.\n", clock.dot, clock.m,
+                        clock.p1);
+       }
+
+       fp = oaktrail_m_converts[(clock.m - MRST_M_MIN)] << 8;
+
+       dpll |= DPLL_VGA_MODE_DIS;
+
+
+       dpll |= DPLL_VCO_ENABLE;
+
+       if (is_lvds)
+               dpll |= DPLLA_MODE_LVDS;
+       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 - 2)) << 17;
+
+       dpll |= DPLL_VCO_ENABLE;
+
+       mrstPrintPll("chosen", &clock);
+
+       if (dpll & DPLL_VCO_ENABLE) {
+               REG_WRITE(fp_reg, fp);
+               REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
+               REG_READ(dpll_reg);
+               /* Check the DPLLA lock bit PIPEACONF[29] */
+               udelay(150);
+       }
+
+       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(pipeconf_reg, pipeconf);
+       REG_READ(pipeconf_reg);
+       psb_intel_wait_for_vblank(dev);
+
+       REG_WRITE(dspcntr_reg, dspcntr);
+       psb_intel_wait_for_vblank(dev);
+
+oaktrail_crtc_mode_set_exit:
+       gma_power_end(dev);
+       return 0;
+}
+
+static bool oaktrail_crtc_mode_fixup(struct drm_crtc *crtc,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+int oaktrail_pipe_set_base(struct drm_crtc *crtc,
+                           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;
+       /* FIXME: check if we need this surely MRST is pipe 0 only */
+       int dspbase = (pipe == 0 ? DSPALINOFF : DSPBBASE);
+       int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
+       int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
+       int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+       u32 dspcntr;
+       int ret = 0;
+
+       /* no fb bound */
+       if (!crtc->fb) {
+               dev_dbg(dev->dev, "No FB bound\n");
+               return 0;
+       }
+
+       if (!gma_power_begin(dev, true))
+               return 0;
+
+       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;
+               goto 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);
+       }
+
+pipe_set_base_exit:
+       gma_power_end(dev);
+       return ret;
+}
+
+static void oaktrail_crtc_prepare(struct drm_crtc *crtc)
+{
+       struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void oaktrail_crtc_commit(struct drm_crtc *crtc)
+{
+       struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+const struct drm_crtc_helper_funcs oaktrail_helper_funcs = {
+       .dpms = oaktrail_crtc_dpms,
+       .mode_fixup = oaktrail_crtc_mode_fixup,
+       .mode_set = oaktrail_crtc_mode_set,
+       .mode_set_base = oaktrail_pipe_set_base,
+       .prepare = oaktrail_crtc_prepare,
+       .commit = oaktrail_crtc_commit,
+};
+
diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c
new file mode 100644 (file)
index 0000000..41c418f
--- /dev/null
@@ -0,0 +1,489 @@
+/**************************************************************************
+ * 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 <linux/module.h>
+#include <linux/dmi.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 <asm/mrst.h>
+#include <asm/intel_scu_ipc.h>
+#include "mid_bios.h"
+
+static int oaktrail_output_init(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       if (dev_priv->iLVDS_enable)
+               oaktrail_lvds_init(dev, &dev_priv->mode_dev);
+       else
+               dev_err(dev->dev, "DSI is not supported\n");
+       if (dev_priv->hdmi_priv)
+               oaktrail_hdmi_init(dev, &dev_priv->mode_dev);
+       return 0;
+}
+
+/*
+ *     Provide the low level interfaces for the Moorestown backlight
+ */
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+
+#define MRST_BLC_MAX_PWM_REG_FREQ          0xFFFF
+#define BLC_PWM_PRECISION_FACTOR 100   /* 10000000 */
+#define BLC_PWM_FREQ_CALC_CONSTANT 32
+#define MHz 1000000
+#define BLC_ADJUSTMENT_MAX 100
+
+static struct backlight_device *oaktrail_backlight_device;
+static int oaktrail_brightness;
+
+static int oaktrail_set_brightness(struct backlight_device *bd)
+{
+       struct drm_device *dev = bl_get_data(oaktrail_backlight_device);
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       int level = bd->props.brightness;
+       u32 blc_pwm_ctl;
+       u32 max_pwm_blc;
+
+       /* Percentage 1-100% being valid */
+       if (level < 1)
+               level = 1;
+
+       if (gma_power_begin(dev, 0)) {
+               /* Calculate and set the brightness value */
+               max_pwm_blc = REG_READ(BLC_PWM_CTL) >> 16;
+               blc_pwm_ctl = level * max_pwm_blc / 100;
+
+               /* Adjust the backlight level with the percent in
+                * dev_priv->blc_adj1;
+                */
+               blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj1;
+               blc_pwm_ctl = blc_pwm_ctl / 100;
+
+               /* Adjust the backlight level with the percent in
+                * dev_priv->blc_adj2;
+                */
+               blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj2;
+               blc_pwm_ctl = blc_pwm_ctl / 100;
+
+               /* force PWM bit on */
+               REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2)));
+               REG_WRITE(BLC_PWM_CTL, (max_pwm_blc << 16) | blc_pwm_ctl);
+               gma_power_end(dev);
+       }
+       oaktrail_brightness = level;
+       return 0;
+}
+
+static int oaktrail_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 oaktrail_brightness;
+}
+
+static int device_backlight_init(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       unsigned long core_clock;
+       u16 bl_max_freq;
+       uint32_t value;
+       uint32_t blc_pwm_precision_factor;
+
+       dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX;
+       dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX;
+       bl_max_freq = 256;
+       /* this needs to be set elsewhere */
+       blc_pwm_precision_factor = 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)MRST_BLC_MAX_PWM_REG_FREQ)
+                       return -ERANGE;
+
+       if (gma_power_begin(dev, false)) {
+               REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2)));
+               REG_WRITE(BLC_PWM_CTL, value | (value << 16));
+               gma_power_end(dev);
+       }
+       return 0;
+}
+
+static const struct backlight_ops oaktrail_ops = {
+       .get_brightness = oaktrail_get_brightness,
+       .update_status  = oaktrail_set_brightness,
+};
+
+int oaktrail_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;
+
+       oaktrail_backlight_device = backlight_device_register("oaktrail-bl",
+                               NULL, (void *)dev, &oaktrail_ops, &props);
+
+       if (IS_ERR(oaktrail_backlight_device))
+               return PTR_ERR(oaktrail_backlight_device);
+
+       ret = device_backlight_init(dev);
+       if (ret < 0) {
+               backlight_device_unregister(oaktrail_backlight_device);
+               return ret;
+       }
+       oaktrail_backlight_device->props.brightness = 100;
+       oaktrail_backlight_device->props.max_brightness = 100;
+       backlight_update_status(oaktrail_backlight_device);
+       dev_priv->backlight_device = oaktrail_backlight_device;
+       return 0;
+}
+
+#endif
+
+/*
+ *     Provide the Moorestown specific chip logic and low level methods
+ *     for power management
+ */
+
+static void oaktrail_init_pm(struct drm_device *dev)
+{
+}
+
+/**
+ *     oaktrail_save_display_registers -       save registers lost on suspend
+ *     @dev: our DRM device
+ *
+ *     Save the state we need in order to be able to restore the interface
+ *     upon resume from suspend
+ */
+static int oaktrail_save_display_registers(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       int i;
+       u32 pp_stat;
+
+       /* Display arbitration control + watermarks */
+       dev_priv->saveDSPARB = PSB_RVDC32(DSPARB);
+       dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1);
+       dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2);
+       dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3);
+       dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4);
+       dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5);
+       dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6);
+       dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT);
+
+       /* Pipe & plane A info */
+       dev_priv->savePIPEACONF = PSB_RVDC32(PIPEACONF);
+       dev_priv->savePIPEASRC = PSB_RVDC32(PIPEASRC);
+       dev_priv->saveFPA0 = PSB_RVDC32(MRST_FPA0);
+       dev_priv->saveFPA1 = PSB_RVDC32(MRST_FPA1);
+       dev_priv->saveDPLL_A = PSB_RVDC32(MRST_DPLL_A);
+       dev_priv->saveHTOTAL_A = PSB_RVDC32(HTOTAL_A);
+       dev_priv->saveHBLANK_A = PSB_RVDC32(HBLANK_A);
+       dev_priv->saveHSYNC_A = PSB_RVDC32(HSYNC_A);
+       dev_priv->saveVTOTAL_A = PSB_RVDC32(VTOTAL_A);
+       dev_priv->saveVBLANK_A = PSB_RVDC32(VBLANK_A);
+       dev_priv->saveVSYNC_A = PSB_RVDC32(VSYNC_A);
+       dev_priv->saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A);
+       dev_priv->saveDSPACNTR = PSB_RVDC32(DSPACNTR);
+       dev_priv->saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE);
+       dev_priv->saveDSPAADDR = PSB_RVDC32(DSPABASE);
+       dev_priv->saveDSPASURF = PSB_RVDC32(DSPASURF);
+       dev_priv->saveDSPALINOFF = PSB_RVDC32(DSPALINOFF);
+       dev_priv->saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF);
+
+       /* Save cursor regs */
+       dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR);
+       dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE);
+       dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS);
+
+       /* Save palette (gamma) */
+       for (i = 0; i < 256; i++)
+               dev_priv->save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2));
+
+       if (dev_priv->hdmi_priv)
+               oaktrail_hdmi_save(dev);
+
+       /* Save performance state */
+       dev_priv->savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE);
+
+       /* LVDS state */
+       dev_priv->savePP_CONTROL = PSB_RVDC32(PP_CONTROL);
+       dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS);
+       dev_priv->savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS);
+       dev_priv->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL);
+       dev_priv->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2);
+       dev_priv->saveLVDS = PSB_RVDC32(LVDS);
+       dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL);
+       dev_priv->savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON);
+       dev_priv->savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF);
+       dev_priv->savePP_DIVISOR = PSB_RVDC32(PP_CYCLE);
+
+       /* HW overlay */
+       dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD);
+       dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0);
+       dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1);
+       dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2);
+       dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3);
+       dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4);
+       dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5);
+
+       /* DPST registers */
+       dev_priv->saveHISTOGRAM_INT_CONTROL_REG =
+                                       PSB_RVDC32(HISTOGRAM_INT_CONTROL);
+       dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG =
+                                       PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL);
+       dev_priv->savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC);
+
+       if (dev_priv->iLVDS_enable) {
+               /* Shut down 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);
+               /* Trigger the plane disable */
+               PSB_WVDC32(0, DSPASURF);
+
+               /* 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);
+       }
+       return 0;
+}
+
+/**
+ *     oaktrail_restore_display_registers      -       restore lost register state
+ *     @dev: our DRM device
+ *
+ *     Restore register state that was lost during suspend and resume.
+ */
+static int oaktrail_restore_display_registers(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       u32 pp_stat;
+       int i;
+
+       /* Display arbitration + watermarks */
+       PSB_WVDC32(dev_priv->saveDSPARB, DSPARB);
+       PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1);
+       PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2);
+       PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3);
+       PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4);
+       PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5);
+       PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6);
+       PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT);
+
+       /* Make sure VGA plane is off. it initializes to on after reset!*/
+       PSB_WVDC32(0x80000000, VGACNTRL);
+
+       /* set the plls */
+       PSB_WVDC32(dev_priv->saveFPA0, MRST_FPA0);
+       PSB_WVDC32(dev_priv->saveFPA1, MRST_FPA1);
+
+       /* Actually enable it */
+       PSB_WVDC32(dev_priv->saveDPLL_A, MRST_DPLL_A);
+       DRM_UDELAY(150);
+
+       /* Restore mode */
+       PSB_WVDC32(dev_priv->saveHTOTAL_A, HTOTAL_A);
+       PSB_WVDC32(dev_priv->saveHBLANK_A, HBLANK_A);
+       PSB_WVDC32(dev_priv->saveHSYNC_A, HSYNC_A);
+       PSB_WVDC32(dev_priv->saveVTOTAL_A, VTOTAL_A);
+       PSB_WVDC32(dev_priv->saveVBLANK_A, VBLANK_A);
+       PSB_WVDC32(dev_priv->saveVSYNC_A, VSYNC_A);
+       PSB_WVDC32(dev_priv->savePIPEASRC, PIPEASRC);
+       PSB_WVDC32(dev_priv->saveBCLRPAT_A, BCLRPAT_A);
+
+       /* Restore performance mode*/
+       PSB_WVDC32(dev_priv->savePERF_MODE, MRST_PERF_MODE);
+
+       /* Enable the pipe*/
+       if (dev_priv->iLVDS_enable)
+               PSB_WVDC32(dev_priv->savePIPEACONF, PIPEACONF);
+
+       /* Set up the plane*/
+       PSB_WVDC32(dev_priv->saveDSPALINOFF, DSPALINOFF);
+       PSB_WVDC32(dev_priv->saveDSPASTRIDE, DSPASTRIDE);
+       PSB_WVDC32(dev_priv->saveDSPATILEOFF, DSPATILEOFF);
+
+       /* Enable the plane */
+       PSB_WVDC32(dev_priv->saveDSPACNTR, DSPACNTR);
+       PSB_WVDC32(dev_priv->saveDSPASURF, DSPASURF);
+
+       /* Enable Cursor A */
+       PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR);
+       PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS);
+       PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE);
+
+       /* Restore palette (gamma) */
+       for (i = 0; i < 256; i++)
+               PSB_WVDC32(dev_priv->save_palette_a[i], PALETTE_A + (i << 2));
+
+       if (dev_priv->hdmi_priv)
+               oaktrail_hdmi_restore(dev);
+
+       if (dev_priv->iLVDS_enable) {
+               PSB_WVDC32(dev_priv->saveBLC_PWM_CTL2, BLC_PWM_CTL2);
+               PSB_WVDC32(dev_priv->saveLVDS, LVDS); /*port 61180h*/
+               PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL);
+               PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS);
+               PSB_WVDC32(dev_priv->savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS);
+               PSB_WVDC32(dev_priv->saveBLC_PWM_CTL, BLC_PWM_CTL);
+               PSB_WVDC32(dev_priv->savePP_ON_DELAYS, LVDSPP_ON);
+               PSB_WVDC32(dev_priv->savePP_OFF_DELAYS, LVDSPP_OFF);
+               PSB_WVDC32(dev_priv->savePP_DIVISOR, PP_CYCLE);
+               PSB_WVDC32(dev_priv->savePP_CONTROL, PP_CONTROL);
+       }
+
+       /* Wait for cycle delay */
+       do {
+               pp_stat = PSB_RVDC32(PP_STATUS);
+       } while (pp_stat & 0x08000000);
+
+       /* Wait for panel power up */
+       do {
+               pp_stat = PSB_RVDC32(PP_STATUS);
+       } while (pp_stat & 0x10000000);
+
+       /* Restore HW overlay */
+       PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD);
+       PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0);
+       PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1);
+       PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2);
+       PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3);
+       PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4);
+       PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5);
+
+       /* DPST registers */
+       PSB_WVDC32(dev_priv->saveHISTOGRAM_INT_CONTROL_REG,
+                                               HISTOGRAM_INT_CONTROL);
+       PSB_WVDC32(dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG,
+                                               HISTOGRAM_LOGIC_CONTROL);
+       PSB_WVDC32(dev_priv->savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC);
+
+       return 0;
+}
+
+/**
+ *     oaktrail_power_down     -       power down the display island
+ *     @dev: our DRM device
+ *
+ *     Power down the display interface of our device
+ */
+static int oaktrail_power_down(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       u32 pwr_mask ;
+       u32 pwr_sts;
+
+       pwr_mask = PSB_PWRGT_DISPLAY_MASK;
+       outl(pwr_mask, dev_priv->ospm_base + PSB_PM_SSC);
+
+       while (true) {
+               pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS);
+               if ((pwr_sts & pwr_mask) == pwr_mask)
+                       break;
+               else
+                       udelay(10);
+       }
+       return 0;
+}
+
+/*
+ * oaktrail_power_up
+ *
+ * Restore power to the specified island(s) (powergating)
+ */
+static int oaktrail_power_up(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       u32 pwr_mask = PSB_PWRGT_DISPLAY_MASK;
+       u32 pwr_sts, pwr_cnt;
+
+       pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC);
+       pwr_cnt &= ~pwr_mask;
+       outl(pwr_cnt, (dev_priv->ospm_base + PSB_PM_SSC));
+
+       while (true) {
+               pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS);
+               if ((pwr_sts & pwr_mask) == 0)
+                       break;
+               else
+                       udelay(10);
+       }
+       return 0;
+}
+
+
+static void oaktrail_teardown(struct drm_device *dev)
+{
+       oaktrail_hdmi_teardown(dev);
+}
+
+const struct psb_ops oaktrail_chip_ops = {
+       .name = "Oaktrail",
+       .accel_2d = 1,
+       .pipes = 2,
+       .crtcs = 2,
+       .sgx_offset = MRST_SGX_OFFSET,
+
+       .chip_setup = mid_chip_setup,
+       .chip_teardown = oaktrail_teardown,
+       .crtc_helper = &oaktrail_helper_funcs,
+       .crtc_funcs = &psb_intel_crtc_funcs,
+
+       .output_init = oaktrail_output_init,
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+       .backlight_init = oaktrail_backlight_init,
+#endif
+
+       .init_pm = oaktrail_init_pm,
+       .save_regs = oaktrail_save_display_registers,
+       .restore_regs = oaktrail_restore_display_registers,
+       .power_down = oaktrail_power_down,
+       .power_up = oaktrail_power_up,
+
+       .i2c_bus = 1,
+};
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
new file mode 100644 (file)
index 0000000..6f423c0
--- /dev/null
@@ -0,0 +1,852 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *     Li Peng <peng.li@intel.com>
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm.h>
+#include "psb_intel_drv.h"
+#include "psb_intel_reg.h"
+#include "psb_drv.h"
+
+#define HDMI_READ(reg)         readl(hdmi_dev->regs + (reg))
+#define HDMI_WRITE(reg, val)   writel(val, hdmi_dev->regs + (reg))
+
+#define HDMI_HCR       0x1000
+#define HCR_ENABLE_HDCP                (1 << 5)
+#define HCR_ENABLE_AUDIO       (1 << 2)
+#define HCR_ENABLE_PIXEL       (1 << 1)
+#define HCR_ENABLE_TMDS                (1 << 0)
+
+#define HDMI_HICR      0x1004
+#define HDMI_HSR       0x1008
+#define HDMI_HISR      0x100C
+#define HDMI_DETECT_HDP                (1 << 0)
+
+#define HDMI_VIDEO_REG 0x3000
+#define HDMI_UNIT_EN           (1 << 7)
+#define HDMI_MODE_OUTPUT       (1 << 0)
+#define HDMI_HBLANK_A  0x3100
+
+#define HDMI_AUDIO_CTRL        0x4000
+#define HDMI_ENABLE_AUDIO      (1 << 0)
+
+#define PCH_HTOTAL_B   0x3100
+#define PCH_HBLANK_B   0x3104
+#define PCH_HSYNC_B    0x3108
+#define PCH_VTOTAL_B   0x310C
+#define PCH_VBLANK_B   0x3110
+#define PCH_VSYNC_B    0x3114
+#define PCH_PIPEBSRC   0x311C
+
+#define PCH_PIPEB_DSL  0x3800
+#define PCH_PIPEB_SLC  0x3804
+#define PCH_PIPEBCONF  0x3808
+#define PCH_PIPEBSTAT  0x3824
+
+#define CDVO_DFT       0x5000
+#define CDVO_SLEWRATE  0x5004
+#define CDVO_STRENGTH  0x5008
+#define CDVO_RCOMP     0x500C
+
+#define DPLL_CTRL       0x6000
+#define DPLL_PDIV_SHIFT                16
+#define DPLL_PDIV_MASK         (0xf << 16)
+#define DPLL_PWRDN             (1 << 4)
+#define DPLL_RESET             (1 << 3)
+#define DPLL_FASTEN            (1 << 2)
+#define DPLL_ENSTAT            (1 << 1)
+#define DPLL_DITHEN            (1 << 0)
+
+#define DPLL_DIV_CTRL   0x6004
+#define DPLL_CLKF_MASK         0xffffffc0
+#define DPLL_CLKR_MASK         (0x3f)
+
+#define DPLL_CLK_ENABLE 0x6008
+#define DPLL_EN_DISP           (1 << 31)
+#define DPLL_SEL_HDMI          (1 << 8)
+#define DPLL_EN_HDMI           (1 << 1)
+#define DPLL_EN_VGA            (1 << 0)
+
+#define DPLL_ADJUST     0x600C
+#define DPLL_STATUS     0x6010
+#define DPLL_UPDATE     0x6014
+#define DPLL_DFT        0x6020
+
+struct intel_range {
+       int     min, max;
+};
+
+struct oaktrail_hdmi_limit {
+       struct intel_range vco, np, nr, nf;
+};
+
+struct oaktrail_hdmi_clock {
+       int np;
+       int nr;
+       int nf;
+       int dot;
+};
+
+#define VCO_MIN                320000
+#define VCO_MAX                1650000
+#define        NP_MIN          1
+#define        NP_MAX          15
+#define        NR_MIN          1
+#define        NR_MAX          64
+#define NF_MIN         2
+#define NF_MAX         4095
+
+static const struct oaktrail_hdmi_limit oaktrail_hdmi_limit = {
+       .vco = { .min = VCO_MIN,                .max = VCO_MAX },
+       .np  = { .min = NP_MIN,                 .max = NP_MAX  },
+       .nr  = { .min = NR_MIN,                 .max = NR_MAX  },
+       .nf  = { .min = NF_MIN,                 .max = NF_MAX  },
+};
+
+static void wait_for_vblank(struct drm_device *dev)
+{
+       /* FIXME: Can we do this as a sleep ? */
+       /* Wait for 20ms, i.e. one cycle at 50hz. */
+       mdelay(20);
+}
+
+static void scu_busy_loop(void *scu_base)
+{
+       u32 status = 0;
+       u32 loop_count = 0;
+
+       status = readl(scu_base + 0x04);
+       while (status & 1) {
+               udelay(1); /* scu processing time is in few u secods */
+               status = readl(scu_base + 0x04);
+               loop_count++;
+               /* break if scu doesn't reset busy bit after huge retry */
+               if (loop_count > 1000) {
+                       DRM_DEBUG_KMS("SCU IPC timed out");
+                       return;
+               }
+       }
+}
+
+static void oaktrail_hdmi_reset(struct drm_device *dev)
+{
+       void *base;
+       /* FIXME: at least make these defines */
+       unsigned int scu_ipc_mmio = 0xff11c000;
+       int scu_len = 1024;
+
+       base = ioremap((resource_size_t)scu_ipc_mmio, scu_len);
+       if (base == NULL) {
+               DRM_ERROR("failed to map SCU mmio\n");
+               return;
+       }
+
+       /* scu ipc: assert hdmi controller reset */
+       writel(0xff11d118, base + 0x0c);
+       writel(0x7fffffdf, base + 0x80);
+       writel(0x42005, base + 0x0);
+       scu_busy_loop(base);
+
+       /* scu ipc: de-assert hdmi controller reset */
+       writel(0xff11d118, base + 0x0c);
+       writel(0x7fffffff, base + 0x80);
+       writel(0x42005, base + 0x0);
+       scu_busy_loop(base);
+
+       iounmap(base);
+}
+
+static void oaktrail_hdmi_audio_enable(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
+
+       HDMI_WRITE(HDMI_HCR, 0x67);
+       HDMI_READ(HDMI_HCR);
+
+       HDMI_WRITE(0x51a8, 0x10);
+       HDMI_READ(0x51a8);
+
+       HDMI_WRITE(HDMI_AUDIO_CTRL, 0x1);
+       HDMI_READ(HDMI_AUDIO_CTRL);
+}
+
+static void oaktrail_hdmi_audio_disable(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
+
+       HDMI_WRITE(0x51a8, 0x0);
+       HDMI_READ(0x51a8);
+
+       HDMI_WRITE(HDMI_AUDIO_CTRL, 0x0);
+       HDMI_READ(HDMI_AUDIO_CTRL);
+
+       HDMI_WRITE(HDMI_HCR, 0x47);
+       HDMI_READ(HDMI_HCR);
+}
+
+void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct drm_device *dev = crtc->dev;
+       u32 temp;
+
+       switch (mode) {
+       case DRM_MODE_DPMS_OFF:
+               /* Disable VGACNTRL */
+               REG_WRITE(VGACNTRL, 0x80000000);
+
+               /* Disable plane */
+               temp = REG_READ(DSPBCNTR);
+               if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+                       REG_WRITE(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE);
+                       REG_READ(DSPBCNTR);
+                       /* Flush the plane changes */
+                       REG_WRITE(DSPBSURF, REG_READ(DSPBSURF));
+                       REG_READ(DSPBSURF);
+               }
+
+               /* Disable pipe B */
+               temp = REG_READ(PIPEBCONF);
+               if ((temp & PIPEACONF_ENABLE) != 0) {
+                       REG_WRITE(PIPEBCONF, temp & ~PIPEACONF_ENABLE);
+                       REG_READ(PIPEBCONF);
+               }
+
+               /* Disable LNW Pipes, etc */
+               temp = REG_READ(PCH_PIPEBCONF);
+               if ((temp & PIPEACONF_ENABLE) != 0) {
+                       REG_WRITE(PCH_PIPEBCONF, temp & ~PIPEACONF_ENABLE);
+                       REG_READ(PCH_PIPEBCONF);
+               }
+               /* wait for pipe off */
+               udelay(150);
+               /* Disable dpll */
+               temp = REG_READ(DPLL_CTRL);
+               if ((temp & DPLL_PWRDN) == 0) {
+                       REG_WRITE(DPLL_CTRL, temp | (DPLL_PWRDN | DPLL_RESET));
+                       REG_WRITE(DPLL_STATUS, 0x1);
+               }
+               /* wait for dpll off */
+               udelay(150);
+               break;
+       case DRM_MODE_DPMS_ON:
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+               /* Enable dpll */
+               temp = REG_READ(DPLL_CTRL);
+               if ((temp & DPLL_PWRDN) != 0) {
+                       REG_WRITE(DPLL_CTRL, temp & ~(DPLL_PWRDN | DPLL_RESET));
+                       temp = REG_READ(DPLL_CLK_ENABLE);
+                       REG_WRITE(DPLL_CLK_ENABLE, temp | DPLL_EN_DISP | DPLL_SEL_HDMI | DPLL_EN_HDMI);
+                       REG_READ(DPLL_CLK_ENABLE);
+               }
+               /* wait for dpll warm up */
+               udelay(150);
+
+               /* Enable pipe B */
+               temp = REG_READ(PIPEBCONF);
+               if ((temp & PIPEACONF_ENABLE) == 0) {
+                       REG_WRITE(PIPEBCONF, temp | PIPEACONF_ENABLE);
+                       REG_READ(PIPEBCONF);
+               }
+
+               /* Enable LNW Pipe B */
+               temp = REG_READ(PCH_PIPEBCONF);
+               if ((temp & PIPEACONF_ENABLE) == 0) {
+                       REG_WRITE(PCH_PIPEBCONF, temp | PIPEACONF_ENABLE);
+                       REG_READ(PCH_PIPEBCONF);
+               }
+               wait_for_vblank(dev);
+
+               /* Enable plane */
+               temp = REG_READ(DSPBCNTR);
+               if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+                       REG_WRITE(DSPBCNTR, temp | DISPLAY_PLANE_ENABLE);
+                       /* Flush the plane changes */
+                       REG_WRITE(DSPBSURF, REG_READ(DSPBSURF));
+                       REG_READ(DSPBSURF);
+               }
+               psb_intel_crtc_load_lut(crtc);
+       }
+       /* DSPARB */
+       REG_WRITE(DSPARB, 0x00003fbf);
+       /* FW1 */
+       REG_WRITE(0x70034, 0x3f880a0a);
+       /* FW2 */
+       REG_WRITE(0x70038, 0x0b060808);
+       /* FW4 */
+       REG_WRITE(0x70050, 0x08030404);
+       /* FW5 */
+       REG_WRITE(0x70054, 0x04040404);
+       /* LNC Chicken Bits */
+       REG_WRITE(0x70400, 0x4000);
+}
+
+
+static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode)
+{
+       static int dpms_mode = -1;
+
+       struct drm_device *dev = encoder->dev;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
+       u32 temp;
+
+       if (dpms_mode == mode)
+               return;
+
+       if (mode != DRM_MODE_DPMS_ON)
+               temp = 0x0;
+       else
+               temp = 0x99;
+
+       dpms_mode = mode;
+       HDMI_WRITE(HDMI_VIDEO_REG, temp);
+}
+
+static unsigned int htotal_calculate(struct drm_display_mode *mode)
+{
+       u32 htotal, new_crtc_htotal;
+
+       htotal = (mode->crtc_hdisplay - 1) | ((mode->crtc_htotal - 1) << 16);
+
+       /*
+        * 1024 x 768  new_crtc_htotal = 0x1024;
+        * 1280 x 1024 new_crtc_htotal = 0x0c34;
+        */
+       new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock;
+
+       return (mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16);
+}
+
+static void oaktrail_hdmi_find_dpll(struct drm_crtc *crtc, int target,
+                               int refclk, struct oaktrail_hdmi_clock *best_clock)
+{
+       int np_min, np_max, nr_min, nr_max;
+       int np, nr, nf;
+
+       np_min = DIV_ROUND_UP(oaktrail_hdmi_limit.vco.min, target * 10);
+       np_max = oaktrail_hdmi_limit.vco.max / (target * 10);
+       if (np_min < oaktrail_hdmi_limit.np.min)
+               np_min = oaktrail_hdmi_limit.np.min;
+       if (np_max > oaktrail_hdmi_limit.np.max)
+               np_max = oaktrail_hdmi_limit.np.max;
+
+       nr_min = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_max));
+       nr_max = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_min));
+       if (nr_min < oaktrail_hdmi_limit.nr.min)
+               nr_min = oaktrail_hdmi_limit.nr.min;
+       if (nr_max > oaktrail_hdmi_limit.nr.max)
+               nr_max = oaktrail_hdmi_limit.nr.max;
+
+       np = DIV_ROUND_UP((refclk * 1000), (target * 10 * nr_max));
+       nr = DIV_ROUND_UP((refclk * 1000), (target * 10 * np));
+       nf = DIV_ROUND_CLOSEST((target * 10 * np * nr), refclk);
+       DRM_DEBUG_KMS("np, nr, nf %d %d %d\n", np, nr, nf);
+
+       /*
+        * 1024 x 768  np = 1; nr = 0x26; nf = 0x0fd8000;
+        * 1280 x 1024 np = 1; nr = 0x17; nf = 0x1034000;
+        */
+       best_clock->np = np;
+       best_clock->nr = nr - 1;
+       best_clock->nf = (nf << 14);
+}
+
+int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc,
+                           struct drm_display_mode *mode,
+                           struct drm_display_mode *adjusted_mode,
+                           int x, int y,
+                           struct drm_framebuffer *old_fb)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
+       int pipe = 1;
+       int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+       int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+       int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+       int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+       int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+       int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+       int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
+       int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
+       int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
+       int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+       int refclk;
+       struct oaktrail_hdmi_clock clock;
+       u32 dspcntr, pipeconf, dpll, temp;
+       int dspcntr_reg = DSPBCNTR;
+
+       /* Disable the VGA plane that we never use */
+       REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+
+       /* XXX: Disable the panel fitter if it was on our pipe */
+
+       /* Disable dpll if necessary */
+       dpll = REG_READ(DPLL_CTRL);
+       if ((dpll & DPLL_PWRDN) == 0) {
+               REG_WRITE(DPLL_CTRL, dpll | (DPLL_PWRDN | DPLL_RESET));
+               REG_WRITE(DPLL_DIV_CTRL, 0x00000000);
+               REG_WRITE(DPLL_STATUS, 0x1);
+       }
+       udelay(150);
+
+       /* reset controller: FIXME - can we sort out the ioremap mess ? */
+       iounmap(hdmi_dev->regs);
+       oaktrail_hdmi_reset(dev);
+
+       /* program and enable dpll */
+       refclk = 25000;
+       oaktrail_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock);
+
+       /* Setting DPLL */
+       dpll = REG_READ(DPLL_CTRL);
+       dpll &= ~DPLL_PDIV_MASK;
+       dpll &= ~(DPLL_PWRDN | DPLL_RESET);
+       REG_WRITE(DPLL_CTRL, 0x00000008);
+       REG_WRITE(DPLL_DIV_CTRL, ((clock.nf << 6) | clock.nr));
+       REG_WRITE(DPLL_ADJUST, ((clock.nf >> 14) - 1));
+       REG_WRITE(DPLL_CTRL, (dpll | (clock.np << DPLL_PDIV_SHIFT) | DPLL_ENSTAT | DPLL_DITHEN));
+       REG_WRITE(DPLL_UPDATE, 0x80000000);
+       REG_WRITE(DPLL_CLK_ENABLE, 0x80050102);
+       udelay(150);
+
+       hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len);
+       if (hdmi_dev->regs == NULL) {
+               DRM_ERROR("failed to do hdmi mmio mapping\n");
+               return -ENOMEM;
+       }
+
+       /* configure HDMI */
+       HDMI_WRITE(0x1004, 0x1fd);
+       HDMI_WRITE(0x2000, 0x1);
+       HDMI_WRITE(0x2008, 0x0);
+       HDMI_WRITE(0x3130, 0x8);
+       HDMI_WRITE(0x101c, 0x1800810);
+
+       temp = htotal_calculate(adjusted_mode);
+       REG_WRITE(htot_reg, temp);
+       REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16));
+       REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16));
+       REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16));
+       REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16));
+       REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16));
+       REG_WRITE(pipesrc_reg,
+               ((mode->crtc_hdisplay - 1) << 16) |  (mode->crtc_vdisplay - 1));
+
+       REG_WRITE(PCH_HTOTAL_B, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16));
+       REG_WRITE(PCH_HBLANK_B, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16));
+       REG_WRITE(PCH_HSYNC_B, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16));
+       REG_WRITE(PCH_VTOTAL_B, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16));
+       REG_WRITE(PCH_VBLANK_B, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16));
+       REG_WRITE(PCH_VSYNC_B, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16));
+       REG_WRITE(PCH_PIPEBSRC,
+               ((mode->crtc_hdisplay - 1) << 16) |  (mode->crtc_vdisplay - 1));
+
+       temp = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start;
+       HDMI_WRITE(HDMI_HBLANK_A, ((adjusted_mode->crtc_hdisplay - 1) << 16) |  temp);
+
+       REG_WRITE(dspsize_reg,
+                       ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
+       REG_WRITE(dsppos_reg, 0);
+
+       /* Flush the plane changes */
+       {
+               struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+               crtc_funcs->mode_set_base(crtc, x, y, old_fb);
+       }
+
+       /* Set up the display plane register */
+       dspcntr = REG_READ(dspcntr_reg);
+       dspcntr |= DISPPLANE_GAMMA_ENABLE;
+       dspcntr |= DISPPLANE_SEL_PIPE_B;
+       dspcntr |= DISPLAY_PLANE_ENABLE;
+
+       /* setup pipeconf */
+       pipeconf = REG_READ(pipeconf_reg);
+       pipeconf |= PIPEACONF_ENABLE;
+
+       REG_WRITE(pipeconf_reg, pipeconf);
+       REG_READ(pipeconf_reg);
+
+       REG_WRITE(PCH_PIPEBCONF, pipeconf);
+       REG_READ(PCH_PIPEBCONF);
+       wait_for_vblank(dev);
+
+       REG_WRITE(dspcntr_reg, dspcntr);
+       wait_for_vblank(dev);
+
+       return 0;
+}
+
+static int oaktrail_hdmi_mode_valid(struct drm_connector *connector,
+                               struct drm_display_mode *mode)
+{
+       if (mode->clock > 165000)
+               return MODE_CLOCK_HIGH;
+       if (mode->clock < 20000)
+               return MODE_CLOCK_LOW;
+
+       if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+               return MODE_NO_DBLESCAN;
+
+       return MODE_OK;
+}
+
+static bool oaktrail_hdmi_mode_fixup(struct drm_encoder *encoder,
+                                struct drm_display_mode *mode,
+                                struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static enum drm_connector_status
+oaktrail_hdmi_detect(struct drm_connector *connector, bool force)
+{
+       enum drm_connector_status status;
+       struct drm_device *dev = connector->dev;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
+       u32 temp;
+
+       temp = HDMI_READ(HDMI_HSR);
+       DRM_DEBUG_KMS("HDMI_HSR %x\n", temp);
+
+       if ((temp & HDMI_DETECT_HDP) != 0)
+               status = connector_status_connected;
+       else
+               status = connector_status_disconnected;
+
+       return status;
+}
+
+static const unsigned char raw_edid[] = {
+       0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0xac, 0x2f, 0xa0,
+       0x53, 0x55, 0x33, 0x30, 0x16, 0x13, 0x01, 0x03, 0x0e, 0x3a, 0x24, 0x78,
+       0xea, 0xe9, 0xf5, 0xac, 0x51, 0x30, 0xb4, 0x25, 0x11, 0x50, 0x54, 0xa5,
+       0x4b, 0x00, 0x81, 0x80, 0xa9, 0x40, 0x71, 0x4f, 0xb3, 0x00, 0x01, 0x01,
+       0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0,
+       0x23, 0x40, 0x30, 0x20, 0x36, 0x00, 0x46, 0x6c, 0x21, 0x00, 0x00, 0x1a,
+       0x00, 0x00, 0x00, 0xff, 0x00, 0x47, 0x4e, 0x37, 0x32, 0x31, 0x39, 0x35,
+       0x52, 0x30, 0x33, 0x55, 0x53, 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44,
+       0x45, 0x4c, 0x4c, 0x20, 0x32, 0x37, 0x30, 0x39, 0x57, 0x0a, 0x20, 0x20,
+       0x00, 0x00, 0x00, 0xfd, 0x00, 0x38, 0x4c, 0x1e, 0x53, 0x11, 0x00, 0x0a,
+       0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x8d
+};
+
+static int oaktrail_hdmi_get_modes(struct drm_connector *connector)
+{
+       struct drm_device *dev = connector->dev;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct i2c_adapter *i2c_adap;
+       struct edid *edid;
+       struct drm_display_mode *mode, *t;
+       int i = 0, ret = 0;
+
+       i2c_adap = i2c_get_adapter(3);
+       if (i2c_adap == NULL) {
+               DRM_ERROR("No ddc adapter available!\n");
+               edid = (struct edid *)raw_edid;
+       } else {
+               edid = (struct edid *)raw_edid;
+               /* FIXME ? edid = drm_get_edid(connector, i2c_adap); */
+       }
+
+       if (edid) {
+               drm_mode_connector_update_edid_property(connector, edid);
+               ret = drm_add_edid_modes(connector, edid);
+               connector->display_info.raw_edid = NULL;
+       }
+
+       /*
+        * prune modes that require frame buffer bigger than stolen mem
+        */
+       list_for_each_entry_safe(mode, t, &connector->probed_modes, head) {
+               if ((mode->hdisplay * mode->vdisplay * 4) >= dev_priv->vram_stolen_size) {
+                       i++;
+                       drm_mode_remove(connector, mode);
+               }
+       }
+       return ret - i;
+}
+
+static void oaktrail_hdmi_mode_set(struct drm_encoder *encoder,
+                              struct drm_display_mode *mode,
+                              struct drm_display_mode *adjusted_mode)
+{
+       struct drm_device *dev = encoder->dev;
+
+       oaktrail_hdmi_audio_enable(dev);
+       return;
+}
+
+static void oaktrail_hdmi_destroy(struct drm_connector *connector)
+{
+       return;
+}
+
+static const struct drm_encoder_helper_funcs oaktrail_hdmi_helper_funcs = {
+       .dpms = oaktrail_hdmi_dpms,
+       .mode_fixup = oaktrail_hdmi_mode_fixup,
+       .prepare = psb_intel_encoder_prepare,
+       .mode_set = oaktrail_hdmi_mode_set,
+       .commit = psb_intel_encoder_commit,
+};
+
+static const struct drm_connector_helper_funcs
+                                       oaktrail_hdmi_connector_helper_funcs = {
+       .get_modes = oaktrail_hdmi_get_modes,
+       .mode_valid = oaktrail_hdmi_mode_valid,
+       .best_encoder = psb_intel_best_encoder,
+};
+
+static const struct drm_connector_funcs oaktrail_hdmi_connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .detect = oaktrail_hdmi_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = oaktrail_hdmi_destroy,
+};
+
+static void oaktrail_hdmi_enc_destroy(struct drm_encoder *encoder)
+{
+       drm_encoder_cleanup(encoder);
+}
+
+static const struct drm_encoder_funcs oaktrail_hdmi_enc_funcs = {
+       .destroy = oaktrail_hdmi_enc_destroy,
+};
+
+void oaktrail_hdmi_init(struct drm_device *dev,
+                                       struct psb_intel_mode_device *mode_dev)
+{
+       struct psb_intel_output *psb_intel_output;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+
+       psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL);
+       if (!psb_intel_output)
+               return;
+
+       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,
+                          &oaktrail_hdmi_connector_funcs,
+                          DRM_MODE_CONNECTOR_DVID);
+
+       drm_encoder_init(dev, &psb_intel_output->enc,
+                        &oaktrail_hdmi_enc_funcs,
+                        DRM_MODE_ENCODER_TMDS);
+
+       drm_mode_connector_attach_encoder(&psb_intel_output->base,
+                                         &psb_intel_output->enc);
+
+       psb_intel_output->type = INTEL_OUTPUT_HDMI;
+       drm_encoder_helper_add(encoder, &oaktrail_hdmi_helper_funcs);
+       drm_connector_helper_add(connector, &oaktrail_hdmi_connector_helper_funcs);
+
+       connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+       connector->interlace_allowed = false;
+       connector->doublescan_allowed = false;
+       drm_sysfs_connector_add(connector);
+
+       return;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = {
+       { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) },
+       {}
+};
+
+void oaktrail_hdmi_setup(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct pci_dev *pdev;
+       struct oaktrail_hdmi_dev *hdmi_dev;
+       int ret;
+
+       pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x080d, NULL);
+       if (!pdev)
+               return;
+
+       hdmi_dev = kzalloc(sizeof(struct oaktrail_hdmi_dev), GFP_KERNEL);
+       if (!hdmi_dev) {
+               dev_err(dev->dev, "failed to allocate memory\n");
+               goto out;
+       }
+
+
+       ret = pci_enable_device(pdev);
+       if (ret) {
+               dev_err(dev->dev, "failed to enable hdmi controller\n");
+               goto free;
+       }
+
+       hdmi_dev->mmio = pci_resource_start(pdev, 0);
+       hdmi_dev->mmio_len = pci_resource_len(pdev, 0);
+       hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len);
+       if (!hdmi_dev->regs) {
+               dev_err(dev->dev, "failed to map hdmi mmio\n");
+               goto free;
+       }
+
+       hdmi_dev->dev = pdev;
+       pci_set_drvdata(pdev, hdmi_dev);
+
+       /* Initialize i2c controller */
+       ret = oaktrail_hdmi_i2c_init(hdmi_dev->dev);
+       if (ret)
+               dev_err(dev->dev, "HDMI I2C initialization failed\n");
+
+       dev_priv->hdmi_priv = hdmi_dev;
+       oaktrail_hdmi_audio_disable(dev);
+       return;
+
+free:
+       kfree(hdmi_dev);
+out:
+       return;
+}
+
+void oaktrail_hdmi_teardown(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
+       struct pci_dev *pdev;
+
+       if (hdmi_dev) {
+               pdev = hdmi_dev->dev;
+               pci_set_drvdata(pdev, NULL);
+               oaktrail_hdmi_i2c_exit(pdev);
+               iounmap(hdmi_dev->regs);
+               kfree(hdmi_dev);
+               pci_dev_put(pdev);
+       }
+}
+
+/* save HDMI register state */
+void oaktrail_hdmi_save(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
+       int i;
+
+       /* dpll */
+       hdmi_dev->saveDPLL_CTRL = PSB_RVDC32(DPLL_CTRL);
+       hdmi_dev->saveDPLL_DIV_CTRL = PSB_RVDC32(DPLL_DIV_CTRL);
+       hdmi_dev->saveDPLL_ADJUST = PSB_RVDC32(DPLL_ADJUST);
+       hdmi_dev->saveDPLL_UPDATE = PSB_RVDC32(DPLL_UPDATE);
+       hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE);
+
+       /* pipe B */
+       dev_priv->savePIPEBCONF = PSB_RVDC32(PIPEBCONF);
+       dev_priv->savePIPEBSRC  = PSB_RVDC32(PIPEBSRC);
+       dev_priv->saveHTOTAL_B  = PSB_RVDC32(HTOTAL_B);
+       dev_priv->saveHBLANK_B  = PSB_RVDC32(HBLANK_B);
+       dev_priv->saveHSYNC_B   = PSB_RVDC32(HSYNC_B);
+       dev_priv->saveVTOTAL_B  = PSB_RVDC32(VTOTAL_B);
+       dev_priv->saveVBLANK_B  = PSB_RVDC32(VBLANK_B);
+       dev_priv->saveVSYNC_B   = PSB_RVDC32(VSYNC_B);
+
+       hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF);
+       hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC);
+       hdmi_dev->savePCH_HTOTAL_B = PSB_RVDC32(PCH_HTOTAL_B);
+       hdmi_dev->savePCH_HBLANK_B = PSB_RVDC32(PCH_HBLANK_B);
+       hdmi_dev->savePCH_HSYNC_B  = PSB_RVDC32(PCH_HSYNC_B);
+       hdmi_dev->savePCH_VTOTAL_B = PSB_RVDC32(PCH_VTOTAL_B);
+       hdmi_dev->savePCH_VBLANK_B = PSB_RVDC32(PCH_VBLANK_B);
+       hdmi_dev->savePCH_VSYNC_B  = PSB_RVDC32(PCH_VSYNC_B);
+
+       /* plane */
+       dev_priv->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR);
+       dev_priv->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE);
+       dev_priv->saveDSPBADDR = PSB_RVDC32(DSPBBASE);
+       dev_priv->saveDSPBSURF = PSB_RVDC32(DSPBSURF);
+       dev_priv->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF);
+       dev_priv->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF);
+
+       /* cursor B */
+       dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR);
+       dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE);
+       dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS);
+
+       /* save palette */
+       for (i = 0; i < 256; i++)
+               dev_priv->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2));
+}
+
+/* restore HDMI register state */
+void oaktrail_hdmi_restore(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
+       int i;
+
+       /* dpll */
+       PSB_WVDC32(hdmi_dev->saveDPLL_CTRL, DPLL_CTRL);
+       PSB_WVDC32(hdmi_dev->saveDPLL_DIV_CTRL, DPLL_DIV_CTRL);
+       PSB_WVDC32(hdmi_dev->saveDPLL_ADJUST, DPLL_ADJUST);
+       PSB_WVDC32(hdmi_dev->saveDPLL_UPDATE, DPLL_UPDATE);
+       PSB_WVDC32(hdmi_dev->saveDPLL_CLK_ENABLE, DPLL_CLK_ENABLE);
+       DRM_UDELAY(150);
+
+       /* pipe */
+       PSB_WVDC32(dev_priv->savePIPEBSRC, PIPEBSRC);
+       PSB_WVDC32(dev_priv->saveHTOTAL_B, HTOTAL_B);
+       PSB_WVDC32(dev_priv->saveHBLANK_B, HBLANK_B);
+       PSB_WVDC32(dev_priv->saveHSYNC_B,  HSYNC_B);
+       PSB_WVDC32(dev_priv->saveVTOTAL_B, VTOTAL_B);
+       PSB_WVDC32(dev_priv->saveVBLANK_B, VBLANK_B);
+       PSB_WVDC32(dev_priv->saveVSYNC_B,  VSYNC_B);
+
+       PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC);
+       PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B);
+       PSB_WVDC32(hdmi_dev->savePCH_HBLANK_B, PCH_HBLANK_B);
+       PSB_WVDC32(hdmi_dev->savePCH_HSYNC_B,  PCH_HSYNC_B);
+       PSB_WVDC32(hdmi_dev->savePCH_VTOTAL_B, PCH_VTOTAL_B);
+       PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B);
+       PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B,  PCH_VSYNC_B);
+
+       PSB_WVDC32(dev_priv->savePIPEBCONF, PIPEBCONF);
+       PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF);
+
+       /* plane */
+       PSB_WVDC32(dev_priv->saveDSPBLINOFF, DSPBLINOFF);
+       PSB_WVDC32(dev_priv->saveDSPBSTRIDE, DSPBSTRIDE);
+       PSB_WVDC32(dev_priv->saveDSPBTILEOFF, DSPBTILEOFF);
+       PSB_WVDC32(dev_priv->saveDSPBCNTR, DSPBCNTR);
+       PSB_WVDC32(dev_priv->saveDSPBSURF, DSPBSURF);
+
+       /* cursor B */
+       PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR);
+       PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS);
+       PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE);
+
+       /* restore palette */
+       for (i = 0; i < 256; i++)
+               PSB_WVDC32(dev_priv->save_palette_b[i], PALETTE_B + (i << 2));
+}
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
new file mode 100644 (file)
index 0000000..d454e6f
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *     Li Peng <peng.li@intel.com>
+ */
+
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include "psb_drv.h"
+
+#define HDMI_READ(reg)         readl(hdmi_dev->regs + (reg))
+#define HDMI_WRITE(reg, val)   writel(val, hdmi_dev->regs + (reg))
+
+#define HDMI_HCR       0x1000
+#define HCR_DETECT_HDP         (1 << 6)
+#define HCR_ENABLE_HDCP                (1 << 5)
+#define HCR_ENABLE_AUDIO       (1 << 2)
+#define HCR_ENABLE_PIXEL       (1 << 1)
+#define HCR_ENABLE_TMDS                (1 << 0)
+#define HDMI_HICR      0x1004
+#define HDMI_INTR_I2C_ERROR    (1 << 4)
+#define HDMI_INTR_I2C_FULL     (1 << 3)
+#define HDMI_INTR_I2C_DONE     (1 << 2)
+#define HDMI_INTR_HPD          (1 << 0)
+#define HDMI_HSR       0x1008
+#define HDMI_HISR      0x100C
+#define HDMI_HI2CRDB0  0x1200
+#define HDMI_HI2CHCR   0x1240
+#define HI2C_HDCP_WRITE                (0 << 2)
+#define HI2C_HDCP_RI_READ      (1 << 2)
+#define HI2C_HDCP_READ         (2 << 2)
+#define HI2C_EDID_READ         (3 << 2)
+#define HI2C_READ_CONTINUE     (1 << 1)
+#define HI2C_ENABLE_TRANSACTION        (1 << 0)
+
+#define HDMI_ICRH      0x1100
+#define HDMI_HI2CTDR0  0x1244
+#define HDMI_HI2CTDR1  0x1248
+
+#define I2C_STAT_INIT          0
+#define I2C_READ_DONE          1
+#define I2C_TRANSACTION_DONE   2
+
+struct hdmi_i2c_dev {
+       struct i2c_adapter *adap;
+       struct mutex i2c_lock;
+       struct completion complete;
+       int status;
+       struct i2c_msg *msg;
+       int buf_offset;
+};
+
+static void hdmi_i2c_irq_enable(struct oaktrail_hdmi_dev *hdmi_dev)
+{
+       u32 temp;
+
+       temp = HDMI_READ(HDMI_HICR);
+       temp |= (HDMI_INTR_I2C_ERROR | HDMI_INTR_I2C_FULL | HDMI_INTR_I2C_DONE);
+       HDMI_WRITE(HDMI_HICR, temp);
+       HDMI_READ(HDMI_HICR);
+}
+
+static void hdmi_i2c_irq_disable(struct oaktrail_hdmi_dev *hdmi_dev)
+{
+       HDMI_WRITE(HDMI_HICR, 0x0);
+       HDMI_READ(HDMI_HICR);
+}
+
+static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg)
+{
+       struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
+       struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
+       u32 temp;
+
+       i2c_dev->status = I2C_STAT_INIT;
+       i2c_dev->msg = pmsg;
+       i2c_dev->buf_offset = 0;
+       INIT_COMPLETION(i2c_dev->complete);
+
+       /* Enable I2C transaction */
+       temp = ((pmsg->len) << 20) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION;
+       HDMI_WRITE(HDMI_HI2CHCR, temp);
+       HDMI_READ(HDMI_HI2CHCR);
+
+       while (i2c_dev->status != I2C_TRANSACTION_DONE)
+               wait_for_completion_interruptible_timeout(&i2c_dev->complete,
+                                                               10 * HZ);
+
+       return 0;
+}
+
+static int xfer_write(struct i2c_adapter *adap, struct i2c_msg *pmsg)
+{
+       /*
+        * XXX: i2c write seems isn't useful for EDID probe, don't do anything
+        */
+       return 0;
+}
+
+static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
+                               struct i2c_msg *pmsg,
+                               int num)
+{
+       struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
+       struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
+       int i, err = 0;
+
+       mutex_lock(&i2c_dev->i2c_lock);
+
+       /* Enable i2c unit */
+       HDMI_WRITE(HDMI_ICRH, 0x00008760);
+
+       /* Enable irq */
+       hdmi_i2c_irq_enable(hdmi_dev);
+       for (i = 0; i < num; i++) {
+               if (pmsg->len && pmsg->buf) {
+                       if (pmsg->flags & I2C_M_RD)
+                               err = xfer_read(adap, pmsg);
+                       else
+                               err = xfer_write(adap, pmsg);
+               }
+               pmsg++;         /* next message */
+       }
+
+       /* Disable irq */
+       hdmi_i2c_irq_disable(hdmi_dev);
+
+       mutex_unlock(&i2c_dev->i2c_lock);
+
+       return i;
+}
+
+static u32 oaktrail_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
+}
+
+static const struct i2c_algorithm oaktrail_hdmi_i2c_algorithm = {
+       .master_xfer    = oaktrail_hdmi_i2c_access,
+       .functionality  = oaktrail_hdmi_i2c_func,
+};
+
+static struct i2c_adapter oaktrail_hdmi_i2c_adapter = {
+       .name           = "oaktrail_hdmi_i2c",
+       .nr             = 3,
+       .owner          = THIS_MODULE,
+       .class          = I2C_CLASS_DDC,
+       .algo           = &oaktrail_hdmi_i2c_algorithm,
+};
+
+static void hdmi_i2c_read(struct oaktrail_hdmi_dev *hdmi_dev)
+{
+       struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
+       struct i2c_msg *msg = i2c_dev->msg;
+       u8 *buf = msg->buf;
+       u32 temp;
+       int i, offset;
+
+       offset = i2c_dev->buf_offset;
+       for (i = 0; i < 0x10; i++) {
+               temp = HDMI_READ(HDMI_HI2CRDB0 + (i * 4));
+               memcpy(buf + (offset + i * 4), &temp, 4);
+       }
+       i2c_dev->buf_offset += (0x10 * 4);
+
+       /* clearing read buffer full intr */
+       temp = HDMI_READ(HDMI_HISR);
+       HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_FULL);
+       HDMI_READ(HDMI_HISR);
+
+       /* continue read transaction */
+       temp = HDMI_READ(HDMI_HI2CHCR);
+       HDMI_WRITE(HDMI_HI2CHCR, temp | HI2C_READ_CONTINUE);
+       HDMI_READ(HDMI_HI2CHCR);
+
+       i2c_dev->status = I2C_READ_DONE;
+       return;
+}
+
+static void hdmi_i2c_transaction_done(struct oaktrail_hdmi_dev *hdmi_dev)
+{
+       struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
+       u32 temp;
+
+       /* clear transaction done intr */
+       temp = HDMI_READ(HDMI_HISR);
+       HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_DONE);
+       HDMI_READ(HDMI_HISR);
+
+
+       temp = HDMI_READ(HDMI_HI2CHCR);
+       HDMI_WRITE(HDMI_HI2CHCR, temp & ~HI2C_ENABLE_TRANSACTION);
+       HDMI_READ(HDMI_HI2CHCR);
+
+       i2c_dev->status = I2C_TRANSACTION_DONE;
+       return;
+}
+
+static irqreturn_t oaktrail_hdmi_i2c_handler(int this_irq, void *dev)
+{
+       struct oaktrail_hdmi_dev *hdmi_dev = dev;
+       struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
+       u32 stat;
+
+       stat = HDMI_READ(HDMI_HISR);
+
+       if (stat & HDMI_INTR_HPD) {
+               HDMI_WRITE(HDMI_HISR, stat | HDMI_INTR_HPD);
+               HDMI_READ(HDMI_HISR);
+       }
+
+       if (stat & HDMI_INTR_I2C_FULL)
+               hdmi_i2c_read(hdmi_dev);
+
+       if (stat & HDMI_INTR_I2C_DONE)
+               hdmi_i2c_transaction_done(hdmi_dev);
+
+       complete(&i2c_dev->complete);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * choose alternate function 2 of GPIO pin 52, 53,
+ * which is used by HDMI I2C logic
+ */
+static void oaktrail_hdmi_i2c_gpio_fix(void)
+{
+       void *base;
+       unsigned int gpio_base = 0xff12c000;
+       int gpio_len = 0x1000;
+       u32 temp;
+
+       base = ioremap((resource_size_t)gpio_base, gpio_len);
+       if (base == NULL) {
+               DRM_ERROR("gpio ioremap fail\n");
+               return;
+       }
+
+       temp = readl(base + 0x44);
+       DRM_DEBUG_DRIVER("old gpio val %x\n", temp);
+       writel((temp | 0x00000a00), (base +  0x44));
+       temp = readl(base + 0x44);
+       DRM_DEBUG_DRIVER("new gpio val %x\n", temp);
+
+       iounmap(base);
+}
+
+int oaktrail_hdmi_i2c_init(struct pci_dev *dev)
+{
+       struct oaktrail_hdmi_dev *hdmi_dev;
+       struct hdmi_i2c_dev *i2c_dev;
+       int ret;
+
+       hdmi_dev = pci_get_drvdata(dev);
+
+       i2c_dev = kzalloc(sizeof(struct hdmi_i2c_dev), GFP_KERNEL);
+       if (i2c_dev == NULL) {
+               DRM_ERROR("Can't allocate interface\n");
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       i2c_dev->adap = &oaktrail_hdmi_i2c_adapter;
+       i2c_dev->status = I2C_STAT_INIT;
+       init_completion(&i2c_dev->complete);
+       mutex_init(&i2c_dev->i2c_lock);
+       i2c_set_adapdata(&oaktrail_hdmi_i2c_adapter, hdmi_dev);
+       hdmi_dev->i2c_dev = i2c_dev;
+
+       /* Enable HDMI I2C function on gpio */
+       oaktrail_hdmi_i2c_gpio_fix();
+
+       /* request irq */
+       ret = request_irq(dev->irq, oaktrail_hdmi_i2c_handler, IRQF_SHARED,
+                         oaktrail_hdmi_i2c_adapter.name, hdmi_dev);
+       if (ret) {
+               DRM_ERROR("Failed to request IRQ for I2C controller\n");
+               goto err;
+       }
+
+       /* Adapter registration */
+       ret = i2c_add_numbered_adapter(&oaktrail_hdmi_i2c_adapter);
+       return ret;
+
+err:
+       kfree(i2c_dev);
+exit:
+       return ret;
+}
+
+void oaktrail_hdmi_i2c_exit(struct pci_dev *dev)
+{
+       struct oaktrail_hdmi_dev *hdmi_dev;
+       struct hdmi_i2c_dev *i2c_dev;
+
+       hdmi_dev = pci_get_drvdata(dev);
+       if (i2c_del_adapter(&oaktrail_hdmi_i2c_adapter))
+               DRM_DEBUG_DRIVER("Failed to delete hdmi-i2c adapter\n");
+
+       i2c_dev = hdmi_dev->i2c_dev;
+       kfree(i2c_dev);
+       free_irq(dev->irq, hdmi_dev);
+}
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c
new file mode 100644 (file)
index 0000000..a552226
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * Copyright © 2006-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.
+ *
+ * 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 <asm/mrst.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>
+
+/* The max/min PWM frequency in BPCR[31:17] - */
+/* The smallest number is 1 (not 0) that can fit in the
+ * 15-bit field of the and then*/
+/* shifts to the left by one bit to get the actual 16-bit
+ * value that the 15-bits correspond to.*/
+#define MRST_BLC_MAX_PWM_REG_FREQ          0xFFFF
+#define BRIGHTNESS_MAX_LEVEL 100
+
+/**
+ * Sets the power state for the panel.
+ */
+static void oaktrail_lvds_set_power(struct drm_device *dev,
+                               struct psb_intel_output *output, bool on)
+{
+       u32 pp_status;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+
+       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 | PP_READY)) == PP_READY);
+               dev_priv->is_lvds_on = true;
+               if (dev_priv->ops->lvds_bl_power)
+                       dev_priv->ops->lvds_bl_power(dev, true);
+       } else {
+               if (dev_priv->ops->lvds_bl_power)
+                       dev_priv->ops->lvds_bl_power(dev, false);
+               REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
+                         ~POWER_TARGET_ON);
+               do {
+                       pp_status = REG_READ(PP_STATUS);
+               } while (pp_status & PP_ON);
+               dev_priv->is_lvds_on = false;
+               pm_request_idle(&dev->pdev->dev);
+       }
+       gma_power_end(dev);
+}
+
+static void oaktrail_lvds_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)
+               oaktrail_lvds_set_power(dev, output, true);
+       else
+               oaktrail_lvds_set_power(dev, output, false);
+
+       /* XXX: We never power down the LVDS pairs. */
+}
+
+static void oaktrail_lvds_mode_set(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 drm_psb_private *dev_priv = dev->dev_private;
+       u32 lvds_port;
+       uint64_t v = DRM_MODE_SCALE_FULLSCREEN;
+
+       if (!gma_power_begin(dev, true))
+               return;
+
+       /*
+        * 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.
+        */
+       lvds_port = (REG_READ(LVDS) &
+                   (~LVDS_PIPEB_SELECT)) |
+                   LVDS_PORT_EN |
+                   LVDS_BORDER_EN;
+
+       /* If the firmware says dither on Moorestown, or the BIOS does
+          on Oaktrail then enable dithering */
+       if (mode_dev->panel_wants_dither || dev_priv->lvds_dither)
+               lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE;
+
+       REG_WRITE(LVDS, lvds_port);
+
+       drm_connector_property_get_value(
+               &enc_to_psb_intel_output(encoder)->base,
+               dev->mode_config.scaling_mode_property,
+               &v);
+
+       if (v == DRM_MODE_SCALE_NO_SCALE)
+               REG_WRITE(PFIT_CONTROL, 0);
+       else if (v == DRM_MODE_SCALE_ASPECT) {
+               if ((mode->vdisplay != adjusted_mode->crtc_vdisplay) ||
+                   (mode->hdisplay != adjusted_mode->crtc_hdisplay)) {
+                       if ((adjusted_mode->crtc_hdisplay * mode->vdisplay) ==
+                           (mode->hdisplay * adjusted_mode->crtc_vdisplay))
+                               REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
+                       else if ((adjusted_mode->crtc_hdisplay *
+                               mode->vdisplay) > (mode->hdisplay *
+                               adjusted_mode->crtc_vdisplay))
+                               REG_WRITE(PFIT_CONTROL, PFIT_ENABLE |
+                                         PFIT_SCALING_MODE_PILLARBOX);
+                       else
+                               REG_WRITE(PFIT_CONTROL, PFIT_ENABLE |
+                                         PFIT_SCALING_MODE_LETTERBOX);
+               } else
+                       REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
+       } else /*(v == DRM_MODE_SCALE_FULLSCREEN)*/
+               REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
+
+       gma_power_end(dev);
+}
+
+static void oaktrail_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);
+       oaktrail_lvds_set_power(dev, output, false);
+       gma_power_end(dev);
+}
+
+static u32 oaktrail_lvds_get_max_backlight(struct drm_device *dev)
+{
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       u32 ret;
+
+       if (gma_power_begin(dev, false)) {
+               ret = ((REG_READ(BLC_PWM_CTL) &
+                         BACKLIGHT_MODULATION_FREQ_MASK) >>
+                         BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+
+               gma_power_end(dev);
+       } else
+               ret = ((dev_priv->saveBLC_PWM_CTL &
+                         BACKLIGHT_MODULATION_FREQ_MASK) >>
+                         BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
+
+       return ret;
+}
+
+static void oaktrail_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 =
+                                       oaktrail_lvds_get_max_backlight(dev);
+       oaktrail_lvds_set_power(dev, output, true);
+}
+
+static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = {
+       .dpms = oaktrail_lvds_dpms,
+       .mode_fixup = psb_intel_lvds_mode_fixup,
+       .prepare = oaktrail_lvds_prepare,
+       .mode_set = oaktrail_lvds_mode_set,
+       .commit = oaktrail_lvds_commit,
+};
+
+static struct drm_display_mode lvds_configuration_modes[] = {
+       /* hard coded fixed mode for TPO LTPS LPJ040K001A */
+       { DRM_MODE("800x480",  DRM_MODE_TYPE_DRIVER, 33264, 800, 836,
+                  846, 1056, 0, 480, 489, 491, 525, 0, 0) },
+       /* hard coded fixed mode for LVDS 800x480 */
+       { DRM_MODE("800x480",  DRM_MODE_TYPE_DRIVER, 30994, 800, 801,
+                  802, 1024, 0, 480, 481, 482, 525, 0, 0) },
+       /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */
+       { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072,
+                  1104, 1184, 0, 600, 603, 604, 608, 0, 0) },
+       /* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */
+       { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104,
+                  1136, 1184, 0, 600, 603, 604, 608, 0, 0) },
+       /* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */
+       { DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124,
+                  1204, 1312, 0, 600, 607, 610, 621, 0, 0) },
+       /* hard coded fixed mode for LVDS 1024x768 */
+       { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
+                  1184, 1344, 0, 768, 771, 777, 806, 0, 0) },
+       /* hard coded fixed mode for LVDS 1366x768 */
+       { DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430,
+                  1558, 1664, 0, 768, 769, 770, 776, 0, 0) },
+};
+
+/* Returns the panel fixed mode from configuration. */
+
+static struct drm_display_mode *
+oaktrail_lvds_get_configuration_mode(struct drm_device *dev)
+{
+       struct drm_display_mode *mode = NULL;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
+
+       if (dev_priv->vbt_data.size != 0x00) { /*if non-zero, then use vbt*/
+               mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+               if (!mode)
+                       return NULL;
+
+               mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo;
+               mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo;
+               mode->hsync_start = mode->hdisplay + \
+                               ((ti->hsync_offset_hi << 8) | \
+                               ti->hsync_offset_lo);
+               mode->hsync_end = mode->hsync_start + \
+                               ((ti->hsync_pulse_width_hi << 8) | \
+                               ti->hsync_pulse_width_lo);
+               mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \
+                                                       ti->hblank_lo);
+               mode->vsync_start = \
+                       mode->vdisplay + ((ti->vsync_offset_hi << 4) | \
+                                               ti->vsync_offset_lo);
+               mode->vsync_end = \
+                       mode->vsync_start + ((ti->vsync_pulse_width_hi << 4) | \
+                                               ti->vsync_pulse_width_lo);
+               mode->vtotal = mode->vdisplay + \
+                               ((ti->vblank_hi << 8) | ti->vblank_lo);
+               mode->clock = ti->pixel_clock * 10;
+#if 0
+               printk(KERN_INFO "hdisplay is %d\n", mode->hdisplay);
+               printk(KERN_INFO "vdisplay is %d\n", mode->vdisplay);
+               printk(KERN_INFO "HSS is %d\n", mode->hsync_start);
+               printk(KERN_INFO "HSE is %d\n", mode->hsync_end);
+               printk(KERN_INFO "htotal is %d\n", mode->htotal);
+               printk(KERN_INFO "VSS is %d\n", mode->vsync_start);
+               printk(KERN_INFO "VSE is %d\n", mode->vsync_end);
+               printk(KERN_INFO "vtotal is %d\n", mode->vtotal);
+               printk(KERN_INFO "clock is %d\n", mode->clock);
+#endif
+       } else
+               mode = drm_mode_duplicate(dev, &lvds_configuration_modes[2]);
+
+       drm_mode_set_name(mode);
+       drm_mode_set_crtcinfo(mode, 0);
+
+       return mode;
+}
+
+/**
+ * oaktrail_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 oaktrail_lvds_init(struct drm_device *dev,
+                   struct psb_intel_mode_device *mode_dev)
+{
+       struct psb_intel_output *psb_intel_output;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+       struct drm_psb_private *dev_priv =
+                               (struct drm_psb_private *) dev->dev_private;
+       struct edid *edid;
+       int ret = 0;
+       struct i2c_adapter *i2c_adap;
+       struct drm_display_mode *scan;  /* *modes, *bios_mode; */
+
+       psb_intel_output = kzalloc(sizeof(struct psb_intel_output), GFP_KERNEL);
+       if (!psb_intel_output)
+               return;
+
+       psb_intel_output->mode_dev = mode_dev;
+       connector = &psb_intel_output->base;
+       encoder = &psb_intel_output->enc;
+       dev_priv->is_lvds_on = true;
+       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, &oaktrail_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;
+
+       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);
+
+       mode_dev->panel_wants_dither = false;
+       if (dev_priv->vbt_data.size != 0x00)
+               mode_dev->panel_wants_dither = (dev_priv->gct_data.
+                       Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE);
+
+       /*
+        * 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
+        */
+
+       i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus);
+       if (i2c_adap == NULL)
+               dev_err(dev->dev, "No ddc adapter available!\n");
+       /*
+        * Attempt to get the fixed panel mode from DDC.  Assume that the
+        * preferred mode is the right one.
+        */
+       if (i2c_adap) {
+               edid = drm_get_edid(connector, i2c_adap);
+               if (edid) {
+                       drm_mode_connector_update_edid_property(connector,
+                                                                       edid);
+                       ret = drm_add_edid_modes(connector, edid);
+                       kfree(edid);
+               }
+
+               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 */
+                       }
+               }
+       }
+       /*
+        * If we didn't get EDID, try geting panel timing
+        * from configuration data
+        */
+       mode_dev->panel_fixed_mode = oaktrail_lvds_get_configuration_mode(dev);
+
+       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;
+       }
+
+out:
+       drm_sysfs_connector_add(connector);
+       return;
+
+failed_find:
+       dev_dbg(dev->dev, "No LVDS modes found, disabling.\n");
+       if (psb_intel_output->ddc_bus)
+               psb_intel_i2c_destroy(psb_intel_output->ddc_bus);
+
+/* failed_ddc: */
+
+       drm_encoder_cleanup(encoder);
+       drm_connector_cleanup(connector);
+       kfree(connector);
+}
+