gma500: Update the Cedarview clock handling
authorAlan Cox <alan@linux.intel.com>
Wed, 25 Apr 2012 13:36:48 +0000 (14:36 +0100)
committerDave Airlie <airlied@redhat.com>
Fri, 27 Apr 2012 08:23:19 +0000 (09:23 +0100)
Signed-off-by: Dave Airlie <airlied@redhat.com>
drivers/gpu/drm/gma500/cdv_intel_display.c
drivers/gpu/drm/gma500/psb_intel_drv.h
drivers/gpu/drm/gma500/psb_intel_reg.h

index be8455919b3306b41c66020d00fe6fa82b21863f..07b37f570ad9b04905c8dc28ebf25c66be218882 100644 (file)
@@ -216,7 +216,7 @@ static void cdv_sb_reset(struct drm_device *dev)
  */
 static int
 cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
-                              struct cdv_intel_clock_t *clock)
+                              struct cdv_intel_clock_t *clock, bool is_lvds)
 {
        struct psb_intel_crtc *psb_crtc =
                                to_psb_intel_crtc(crtc);
@@ -224,6 +224,7 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
        u32 m, n_vco, p;
        int ret = 0;
        int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+       int ref_sfr = (pipe == 0) ? SB_REF_DPLLA : SB_REF_DPLLB;
        u32 ref_value;
 
        cdv_sb_reset(dev);
@@ -241,6 +242,35 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
        /* We don't know what the other fields of these regs are, so
         * leave them in place.
         */
+       /* 
+        * The BIT 14:13 of 0x8010/0x8030 is used to select the ref clk
+        * for the pipe A/B. Display spec 1.06 has wrong definition.
+        * Correct definition is like below:
+        *
+        * refclka mean use clock from same PLL
+        *
+        * if DPLLA sets 01 and DPLLB sets 01, they use clock from their pll
+        *
+        * if DPLLA sets 01 and DPLLB sets 02, both use clk from DPLLA
+        *
+        */  
+       ret = cdv_sb_read(dev, ref_sfr, &ref_value);
+       if (ret)
+               return ret;
+       ref_value &= ~(REF_CLK_MASK);
+
+       /* use DPLL_A for pipeB on CRT/HDMI */
+       if (pipe == 1 && !is_lvds) {
+               DRM_DEBUG_KMS("use DPLLA for pipe B\n");
+               ref_value |= REF_CLK_DPLLA;
+       } else {
+               DRM_DEBUG_KMS("use their DPLL for pipe A/B\n");
+               ref_value |= REF_CLK_DPLL;
+       }
+       ret = cdv_sb_write(dev, ref_sfr, ref_value);
+       if (ret)
+               return ret;
+
        ret = cdv_sb_read(dev, SB_M(pipe), &m);
        if (ret)
                return ret;
@@ -308,7 +338,7 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
                return ret;
 
        /* always Program the Lane Register for the Pipe A*/
-       if (pipe == 0) {
+/*     if (pipe == 0) */ {
                /* Program the Lane0/1 for HDMI B */
                u32 lane_reg, lane_value;
 
@@ -553,6 +583,200 @@ psb_intel_pipe_set_base_exit:
        return ret;
 }
 
+#define                FIFO_PIPEA              (1 << 0)
+#define                FIFO_PIPEB              (1 << 1)
+
+static bool cdv_intel_pipe_enabled(struct drm_device *dev, int pipe)
+{
+       struct drm_crtc *crtc;
+       struct drm_psb_private *dev_priv = dev->dev_private;
+       struct psb_intel_crtc *psb_intel_crtc = NULL;
+
+       crtc = dev_priv->pipe_to_crtc_mapping[pipe];
+       psb_intel_crtc = to_psb_intel_crtc(crtc);
+
+       if (crtc->fb == NULL || !psb_intel_crtc->active)
+               return false;
+       return true;
+}
+
+static bool cdv_intel_single_pipe_active (struct drm_device *dev)
+{
+       uint32_t pipe_enabled = 0;
+
+       if (cdv_intel_pipe_enabled(dev, 0))
+               pipe_enabled |= FIFO_PIPEA;
+
+       if (cdv_intel_pipe_enabled(dev, 1))
+               pipe_enabled |= FIFO_PIPEB;
+
+
+       DRM_DEBUG_KMS("pipe enabled %x\n", pipe_enabled);
+
+       if (pipe_enabled == FIFO_PIPEA || pipe_enabled == FIFO_PIPEB)
+               return true;
+       else
+               return false;
+}
+
+static bool is_pipeb_lvds(struct drm_device *dev, struct drm_crtc *crtc)
+{
+       struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct drm_connector *connector;
+
+       if (psb_intel_crtc->pipe != 1)
+               return false;
+
+       list_for_each_entry(connector, &mode_config->connector_list, head) {
+               struct psb_intel_encoder *psb_intel_encoder =
+                                       psb_intel_attached_encoder(connector);
+
+               if (!connector->encoder
+                   || connector->encoder->crtc != crtc)
+                       continue;
+
+               if (psb_intel_encoder->type == INTEL_OUTPUT_LVDS)
+                       return true;
+       }
+
+       return false;
+}
+
+static void cdv_intel_disable_self_refresh (struct drm_device *dev)
+{
+       if (REG_READ(FW_BLC_SELF) & FW_BLC_SELF_EN) {
+
+               /* Disable self-refresh before adjust WM */
+               REG_WRITE(FW_BLC_SELF, (REG_READ(FW_BLC_SELF) & ~FW_BLC_SELF_EN));
+               REG_READ(FW_BLC_SELF);
+
+               cdv_intel_wait_for_vblank(dev);
+
+               /* Cedarview workaround to write ovelay plane, which force to leave
+                * MAX_FIFO state.
+                */
+               REG_WRITE(OV_OVADD, 0/*dev_priv->ovl_offset*/);
+               REG_READ(OV_OVADD);
+
+               cdv_intel_wait_for_vblank(dev);
+       }
+
+}
+
+static void cdv_intel_update_watermark (struct drm_device *dev, struct drm_crtc *crtc)
+{
+
+       if (cdv_intel_single_pipe_active(dev)) {
+               u32 fw;
+
+               fw = REG_READ(DSPFW1);
+               fw &= ~DSP_FIFO_SR_WM_MASK;
+               fw |= (0x7e << DSP_FIFO_SR_WM_SHIFT);
+               fw &= ~CURSOR_B_FIFO_WM_MASK;
+               fw |= (0x4 << CURSOR_B_FIFO_WM_SHIFT);
+               REG_WRITE(DSPFW1, fw);
+
+               fw = REG_READ(DSPFW2);
+               fw &= ~CURSOR_A_FIFO_WM_MASK;
+               fw |= (0x6 << CURSOR_A_FIFO_WM_SHIFT);
+               fw &= ~DSP_PLANE_C_FIFO_WM_MASK;
+               fw |= (0x8 << DSP_PLANE_C_FIFO_WM_SHIFT);
+               REG_WRITE(DSPFW2, fw);
+
+               REG_WRITE(DSPFW3, 0x36000000);
+
+               /* ignore FW4 */
+
+               if (is_pipeb_lvds(dev, crtc)) {
+                       REG_WRITE(DSPFW5, 0x00040330);
+               } else {
+                       fw = (3 << DSP_PLANE_B_FIFO_WM1_SHIFT) |
+                            (4 << DSP_PLANE_A_FIFO_WM1_SHIFT) |
+                            (3 << CURSOR_B_FIFO_WM1_SHIFT) |
+                            (4 << CURSOR_FIFO_SR_WM1_SHIFT);
+                       REG_WRITE(DSPFW5, fw);
+               }
+
+               REG_WRITE(DSPFW6, 0x10);
+
+               cdv_intel_wait_for_vblank(dev);
+
+               /* enable self-refresh for single pipe active */
+               REG_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
+               REG_READ(FW_BLC_SELF);
+               cdv_intel_wait_for_vblank(dev);
+
+       } else {
+
+               /* HW team suggested values... */
+               REG_WRITE(DSPFW1, 0x3f880808);
+               REG_WRITE(DSPFW2, 0x0b020202);
+               REG_WRITE(DSPFW3, 0x24000000);
+               REG_WRITE(DSPFW4, 0x08030202);
+               REG_WRITE(DSPFW5, 0x01010101);
+               REG_WRITE(DSPFW6, 0x1d0);
+
+               cdv_intel_wait_for_vblank(dev);
+
+               cdv_intel_disable_self_refresh(dev);
+       
+       }
+}
+
+/** Loads the palette/gamma unit for the CRTC with the prepared values */
+static void cdv_intel_crtc_load_lut(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_psb_private *dev_priv =
+                               (struct drm_psb_private *)dev->dev_private;
+       struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
+       int palreg = PALETTE_A;
+       int i;
+
+       /* The clocks have to be on to load the palette. */
+       if (!crtc->enabled)
+               return;
+
+       switch (psb_intel_crtc->pipe) {
+       case 0:
+               break;
+       case 1:
+               palreg = PALETTE_B;
+               break;
+       case 2:
+               palreg = PALETTE_C;
+               break;
+       default:
+               dev_err(dev->dev, "Illegal Pipe Number.\n");
+               return;
+       }
+
+       if (gma_power_begin(dev, false)) {
+               for (i = 0; i < 256; i++) {
+                       REG_WRITE(palreg + 4 * i,
+                                 ((psb_intel_crtc->lut_r[i] +
+                                 psb_intel_crtc->lut_adj[i]) << 16) |
+                                 ((psb_intel_crtc->lut_g[i] +
+                                 psb_intel_crtc->lut_adj[i]) << 8) |
+                                 (psb_intel_crtc->lut_b[i] +
+                                 psb_intel_crtc->lut_adj[i]));
+               }
+               gma_power_end(dev);
+       } else {
+               for (i = 0; i < 256; i++) {
+                       dev_priv->regs.psb.save_palette_a[i] =
+                                 ((psb_intel_crtc->lut_r[i] +
+                                 psb_intel_crtc->lut_adj[i]) << 16) |
+                                 ((psb_intel_crtc->lut_g[i] +
+                                 psb_intel_crtc->lut_adj[i]) << 8) |
+                                 (psb_intel_crtc->lut_b[i] +
+                                 psb_intel_crtc->lut_adj[i]);
+               }
+
+       }
+}
+
 /**
  * Sets the power management mode of the pipe and plane.
  *
@@ -568,15 +792,23 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
        int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
        int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE;
        int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+       int pipestat_reg = (pipe == 0) ? PIPEASTAT : PIPEBSTAT;
        u32 temp;
 
        /* XXX: When our outputs are all unaware of DPMS modes other than off
         * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
         */
+       cdv_intel_disable_self_refresh(dev);
+
        switch (mode) {
        case DRM_MODE_DPMS_ON:
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
+               if (psb_intel_crtc->active)
+                       return;
+
+               psb_intel_crtc->active = true;
+
                /* Enable the DPLL */
                temp = REG_READ(dpll_reg);
                if ((temp & DPLL_VCO_ENABLE) == 0) {
@@ -611,13 +843,26 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
                if ((temp & PIPEACONF_ENABLE) == 0)
                        REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
 
-               psb_intel_crtc_load_lut(crtc);
+               temp = REG_READ(pipestat_reg);
+               temp &= ~(0xFFFF);
+               temp |= PIPE_FIFO_UNDERRUN;
+               REG_WRITE(pipestat_reg, temp);
+               REG_READ(pipestat_reg);
+
+               cdv_intel_update_watermark(dev, crtc);
+               cdv_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 */
+               psb_intel_crtc->crtc_enable = true;
                break;
        case DRM_MODE_DPMS_OFF:
+               if (!psb_intel_crtc->active)
+                       return;
+
+               psb_intel_crtc->active = false;
+
                /* Give the overlay scaler a chance to disable
                 * if it's on this pipe */
                /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */
@@ -627,6 +872,7 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
 
                /* Jim Bish - changed pipe/plane here as well. */
 
+               drm_vblank_off(dev, pipe);
                /* Wait for vblank for the disable to take effect */
                cdv_intel_wait_for_vblank(dev);
 
@@ -660,6 +906,8 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
 
                /* Wait for the clocks to turn off. */
                udelay(150);
+               cdv_intel_update_watermark(dev, crtc);
+               psb_intel_crtc->crtc_enable = false;
                break;
        }
        /*Set FIFO Watermarks*/
@@ -709,6 +957,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
                               struct drm_framebuffer *old_fb)
 {
        struct drm_device *dev = crtc->dev;
+       struct drm_psb_private *dev_priv = dev->dev_private;
        struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
        int pipe = psb_intel_crtc->pipe;
        int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
@@ -757,13 +1006,18 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
                }
        }
 
-       refclk = 96000;
-
-       /* Hack selection about ref clk for CRT */
-       /* Select 27MHz as the reference clk for HDMI */
-       if (is_crt || is_hdmi)
+       if (dev_priv->dplla_96mhz)
+               /* low-end sku, 96/100 mhz */
+               refclk = 96000;
+       else
+               /* high-end sku, 27/100 mhz */
                refclk = 27000;
 
+       if (is_lvds && dev_priv->lvds_use_ssc) {
+               refclk = dev_priv->lvds_ssc_freq * 1000;
+               DRM_DEBUG_KMS("Use SSC reference clock %d Mhz\n", dev_priv->lvds_ssc_freq);
+       }
+
        drm_mode_debug_printmodeline(adjusted_mode);
 
        ok = cdv_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk,
@@ -779,14 +1033,13 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
 /*     dpll |= PLL_REF_INPUT_TVCLKINBC; */
                dpll |= 3;
        }
-               dpll |= PLL_REF_INPUT_DREFCLK;
+/*             dpll |= PLL_REF_INPUT_DREFCLK; */
 
        dpll |= DPLL_SYNCLOCK_ENABLE;
-       dpll |= DPLL_VGA_MODE_DIS;
-       if (is_lvds)
+/*     if (is_lvds)
                dpll |= DPLLB_MODE_LVDS;
        else
-               dpll |= DPLLB_MODE_DAC_SERIAL;
+               dpll |= DPLLB_MODE_DAC_SERIAL; */
        /* dpll |= (2 << 11); */
 
        /* setup pipeconf */
@@ -806,7 +1059,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
        REG_WRITE(dpll_reg, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE);
        REG_READ(dpll_reg);
 
-       cdv_dpll_set_clock_cdv(dev, crtc, &clock);
+       cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds);
 
        udelay(150);
 
@@ -903,58 +1156,6 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
        return 0;
 }
 
-/** Loads the palette/gamma unit for the CRTC with the prepared values */
-static void cdv_intel_crtc_load_lut(struct drm_crtc *crtc)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_psb_private *dev_priv =
-                               (struct drm_psb_private *)dev->dev_private;
-       struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
-       int palreg = PALETTE_A;
-       int i;
-
-       /* The clocks have to be on to load the palette. */
-       if (!crtc->enabled)
-               return;
-
-       switch (psb_intel_crtc->pipe) {
-       case 0:
-               break;
-       case 1:
-               palreg = PALETTE_B;
-               break;
-       case 2:
-               palreg = PALETTE_C;
-               break;
-       default:
-               dev_err(dev->dev, "Illegal Pipe Number.\n");
-               return;
-       }
-
-       if (gma_power_begin(dev, false)) {
-               for (i = 0; i < 256; i++) {
-                       REG_WRITE(palreg + 4 * i,
-                                 ((psb_intel_crtc->lut_r[i] +
-                                 psb_intel_crtc->lut_adj[i]) << 16) |
-                                 ((psb_intel_crtc->lut_g[i] +
-                                 psb_intel_crtc->lut_adj[i]) << 8) |
-                                 (psb_intel_crtc->lut_b[i] +
-                                 psb_intel_crtc->lut_adj[i]));
-               }
-               gma_power_end(dev);
-       } else {
-               for (i = 0; i < 256; i++) {
-                       dev_priv->regs.psb.save_palette_a[i] =
-                                 ((psb_intel_crtc->lut_r[i] +
-                                 psb_intel_crtc->lut_adj[i]) << 16) |
-                                 ((psb_intel_crtc->lut_g[i] +
-                                 psb_intel_crtc->lut_adj[i]) << 8) |
-                                 (psb_intel_crtc->lut_b[i] +
-                                 psb_intel_crtc->lut_adj[i]);
-               }
-
-       }
-}
 
 /**
  * Save HW states of giving crtc
index f40535e566898e67cd858a4a78104f0d2e64ddf6..81852b48654c3bde86f24f3e748440a3988f9ffa 100644 (file)
@@ -193,6 +193,9 @@ struct psb_intel_crtc {
        /*crtc mode setting flags*/
        u32 mode_flags;
 
+       bool active;
+       bool crtc_enable;
+
        /* Saved Crtc HW states */
        struct psb_intel_crtc_state *crtc_state;
 };
index e89d3a2e8fdcad01f43e04eb7119dc911e106115..46792fc7d0d031743614c49f2846586c068b2761 100644 (file)
 #define PIPE_VSYNC_ENABL                       (1UL << 25)
 #define PIPE_HDMI_AUDIO_UNDERRUN               (1UL << 26)
 #define PIPE_HDMI_AUDIO_BUFFER_DONE            (1UL << 27)
+#define PIPE_FIFO_UNDERRUN                     (1UL << 31)
 #define PIPE_HDMI_AUDIO_INT_MASK               (PIPE_HDMI_AUDIO_UNDERRUN | \
                                                PIPE_HDMI_AUDIO_BUFFER_DONE)
 #define PIPE_EVENT_MASK ((1 << 29)|(1 << 28)|(1 << 27)|(1 << 26)|(1 << 24)|(1 << 23)|(1 << 22)|(1 << 21)|(1 << 20)|(1 << 16))
@@ -569,12 +570,27 @@ struct dpst_guardband {
 #define PIPE_PIXEL_MASK                0x00ffffff
 #define PIPE_PIXEL_SHIFT       0
 
+#define FW_BLC_SELF            0x20e0 
+#define FW_BLC_SELF_EN          (1<<15)
+
 #define DSPARB                 0x70030
 #define DSPFW1                 0x70034
+#define DSP_FIFO_SR_WM_MASK            0xFF800000
+#define DSP_FIFO_SR_WM_SHIFT           23
+#define CURSOR_B_FIFO_WM_MASK          0x003F0000
+#define CURSOR_B_FIFO_WM_SHIFT         16
 #define DSPFW2                 0x70038
+#define CURSOR_A_FIFO_WM_MASK          0x3F00
+#define CURSOR_A_FIFO_WM_SHIFT         8
+#define DSP_PLANE_C_FIFO_WM_MASK       0x7F
+#define DSP_PLANE_C_FIFO_WM_SHIFT      0
 #define DSPFW3                 0x7003c
 #define DSPFW4                 0x70050
 #define DSPFW5                 0x70054
+#define DSP_PLANE_B_FIFO_WM1_SHIFT     24
+#define DSP_PLANE_A_FIFO_WM1_SHIFT     16
+#define CURSOR_B_FIFO_WM1_SHIFT                8
+#define CURSOR_FIFO_SR_WM1_SHIFT       0
 #define DSPFW6                 0x70058
 #define DSPCHICKENBIT          0x70400
 #define DSPACNTR               0x70180
@@ -1290,6 +1306,15 @@ No status bits are changed.
 #define SB_N_CB_TUNE_MASK                      PSB_MASK(25, 24)
 #define SB_N_CB_TUNE_SHIFT                     24
 
+/* the bit 14:13 is used to select between the different reference clock for Pipe A/B */
+#define SB_REF_DPLLA           0x8010
+#define SB_REF_DPLLB           0x8030
+#define        REF_CLK_MASK            (0x3 << 13)
+#define REF_CLK_CORE           (0 << 13)
+#define REF_CLK_DPLL           (1 << 13)
+#define REF_CLK_DPLLA          (2 << 13)
+/* For the DPLL B, it will use the reference clk from DPLL A when using (2 << 13) */
+
 #define _SB_REF_A              0x8018
 #define _SB_REF_B              0x8038
 #define SB_REF_SFR(pipe)       _PIPE(pipe, _SB_REF_A, _SB_REF_B)