Figuring out which pipe bpp to use is a bit painful.  It depends on both
the encoder and display configuration attached to a pipe.  For instance,
to drive a 24bpp framebuffer out to an 18bpp panel, we need to use 6bpc
on the pipe but also enable dithering.  But driving that same
framebuffer to a DisplayPort output on another pipe means using 8bpc and
no dithering.

So split out and enhance the code to handle the various cases, returning
an appropriate pipe bpp as well as whether dithering should be enabled.

Save the resulting pipe bpp in the intel_crtc struct for use by encoders
in calculating bandwidth requirements (defaults to 24bpp on pre-ILK).

Signed-off-by: Jesse Barnes <jbar...@virtuousgeek.org>
---
 drivers/gpu/drm/i915/intel_display.c |  194 ++++++++++++++++++++++++++--------
 drivers/gpu/drm/i915/intel_drv.h     |    1 +
 2 files changed, 153 insertions(+), 42 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_display.c 
b/drivers/gpu/drm/i915/intel_display.c
index af0cb2b..7f56040 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -4308,6 +4308,133 @@ static inline bool intel_panel_use_ssc(struct 
drm_i915_private *dev_priv)
        return dev_priv->lvds_use_ssc && i915_panel_use_ssc;
 }
 
+/**
+ * intel_choose_pipe_bpp_dither - figure out what color depth the pipe should 
send
+ * @crtc: CRTC structure
+ *
+ * A pipe may be connected to one or more outputs.  Based on the depth of the
+ * attached framebuffer, choose a good color depth to use on the pipe.
+ *
+ * If possible, match the pipe depth to the fb depth.  In some cases, this
+ * isn't ideal, because the connected output supports a lesser or restricted
+ * set of depths.  Resolve that here:
+ *    LVDS typically supports only 6bpc, so clamp down in that case
+ *    HDMI supports only 8bpc or 12bpc, so clamp to 8bpc with dither for 10bpc
+ *    Displays may support a restricted set as well, check EDID and clamp as
+ *      appropriate.
+ *
+ * RETURNS:
+ * Dithering requirement (i.e. false if display bpc and pipe bpc match,
+ * true if they don't match).
+ */
+static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
+                                        unsigned int *pipe_bpp)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_encoder *encoder;
+       struct drm_connector *connector;
+       unsigned int display_bpc = UINT_MAX, bpc;
+
+       /* Walk the encoders & connectors on this crtc, get min bpc */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+
+               if (encoder->crtc != crtc)
+                       continue;
+
+               if (intel_encoder->type == INTEL_OUTPUT_LVDS) {
+                       unsigned int lvds_bpc;
+
+                       if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) ==
+                           LVDS_A3_POWER_UP)
+                               lvds_bpc = 8;
+                       else
+                               lvds_bpc = 6;
+
+                       if (lvds_bpc < display_bpc) {
+                               DRM_DEBUG_DRIVER("clamping display bpc (was %d) 
to LVDS (%d)\n", display_bpc, lvds_bpc);
+                               display_bpc = lvds_bpc;
+                       }
+                       continue;
+               }
+
+               if (intel_encoder->type == INTEL_OUTPUT_EDP) {
+                       /* Use VBT settings if we have an eDP panel */
+                       unsigned int edp_bpc = dev_priv->edp.bpp / 3;
+
+                       if (edp_bpc < display_bpc) {
+                               DRM_DEBUG_DRIVER("clamping display bpc (was %d) 
to eDP (%d)\n", display_bpc, edp_bpc);
+                               display_bpc = edp_bpc;
+                       }
+                       continue;
+               }
+
+               /* Not one of the known troublemakers, check the EDID */
+               list_for_each_entry(connector, &dev->mode_config.connector_list,
+                                   head) {
+                       if (connector->encoder != encoder)
+                               continue;
+
+                       if (connector->display_info.bpc < display_bpc) {
+                               DRM_DEBUG_DRIVER("clamping display bpc (was %d) 
to EDID reported max of %d\n", display_bpc, connector->display_info.bpc);
+                               display_bpc = connector->display_info.bpc;
+                       }
+               }
+
+               /*
+                * HDMI is either 12 or 8, so if the display lets 10bpc sneak
+                * through, clamp it down.  (Note: >12bpc will be caught below.)
+                */
+               if (intel_encoder->type == INTEL_OUTPUT_HDMI) {
+                       if (display_bpc > 8 && display_bpc < 12) {
+                               DRM_DEBUG_DRIVER("forcing bpc to 12 for 
HDMI\n");
+                               display_bpc = 12;
+                       } else {
+                               DRM_DEBUG_DRIVER("forcing bpc to 8 for HDMI\n");
+                               display_bpc = 8;
+                       }
+               }
+       }
+
+       /*
+        * We could just drive the pipe at the highest bpc all the time and
+        * enable dithering as needed, but that costs bandwidth.  So choose
+        * the minimum value that expresses the full color range of the fb but
+        * also stays within the max display bpc discovered above.
+        */
+
+       switch (crtc->fb->depth) {
+       case 8:
+               bpc = 8; /* since we go through a colormap */
+               break;
+       case 15:
+       case 16:
+               bpc = 6; /* min is 18bpp */
+               break;
+       case 24:
+               bpc = min((unsigned int)8, display_bpc);
+               break;
+       case 30:
+               bpc = min((unsigned int)10, display_bpc);
+               break;
+       case 48:
+               bpc = min((unsigned int)12, display_bpc);
+               break;
+       default:
+               DRM_DEBUG("unsupported depth, assuming 24 bits\n");
+               bpc = min((unsigned int)8, display_bpc);
+               break;
+       }
+
+       DRM_DEBUG_DRIVER("setting pipe bpc to %d (max display bpc %d)\n",
+                        bpc, display_bpc);
+
+       *pipe_bpp = bpc * 3;
+
+       return display_bpc != bpc;
+}
+
 static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                              struct drm_display_mode *mode,
                              struct drm_display_mode *adjusted_mode,
@@ -4720,7 +4847,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        struct fdi_m_n m_n = {0};
        u32 temp;
        u32 lvds_sync = 0;
-       int target_clock, pixel_multiplier, lane, link_bw, bpp, factor;
+       int target_clock, pixel_multiplier, lane, link_bw, factor;
+       unsigned int pipe_bpp;
+       bool dither;
 
        list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
                if (encoder->base.crtc != crtc)
@@ -4847,56 +4976,37 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
        /* determine panel color depth */
        temp = I915_READ(PIPECONF(pipe));
        temp &= ~PIPE_BPC_MASK;
-       if (is_lvds) {
-               /* the BPC will be 6 if it is 18-bit LVDS panel */
-               if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == 
LVDS_A3_POWER_UP)
-                       temp |= PIPE_8BPC;
-               else
-                       temp |= PIPE_6BPC;
-       } else if (has_edp_encoder) {
-               switch (dev_priv->edp.bpp/3) {
-               case 8:
-                       temp |= PIPE_8BPC;
-                       break;
-               case 10:
-                       temp |= PIPE_10BPC;
-                       break;
-               case 6:
-                       temp |= PIPE_6BPC;
-                       break;
-               case 12:
-                       temp |= PIPE_12BPC;
-                       break;
-               }
-       } else
-               temp |= PIPE_8BPC;
-       I915_WRITE(PIPECONF(pipe), temp);
-
-       switch (temp & PIPE_BPC_MASK) {
-       case PIPE_8BPC:
-               bpp = 24;
+       dither = intel_choose_pipe_bpp_dither(crtc, &pipe_bpp);
+       switch (pipe_bpp) {
+       case 18:
+               temp |= PIPE_6BPC;
                break;
-       case PIPE_10BPC:
-               bpp = 30;
+       case 24:
+               temp |= PIPE_8BPC;
                break;
-       case PIPE_6BPC:
-               bpp = 18;
+       case 30:
+               temp |= PIPE_10BPC;
                break;
-       case PIPE_12BPC:
-               bpp = 36;
+       case 36:
+               temp |= PIPE_12BPC;
                break;
        default:
-               DRM_ERROR("unknown pipe bpc value\n");
-               bpp = 24;
+               WARN(1, "intel_choose_pipe_bpp returned invalid value\n");
+               temp |= PIPE_8BPC;
+               pipe_bpp = 24;
+               break;
        }
 
+       intel_crtc->bpp = pipe_bpp;
+       I915_WRITE(PIPECONF(pipe), temp);
+
        if (!lane) {
                /*
                 * Account for spread spectrum to avoid
                 * oversubscribing the link. Max center spread
                 * is 2.5%; use 5% for safety's sake.
                 */
-               u32 bps = target_clock * bpp * 21 / 20;
+               u32 bps = target_clock * intel_crtc->bpp * 21 / 20;
                lane = bps / (link_bw * 8) + 1;
        }
 
@@ -4904,7 +5014,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
 
        if (pixel_multiplier > 1)
                link_bw *= pixel_multiplier;
-       ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
+       ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw,
+                            &m_n);
 
        /* Ironlake: try to setup display ref clock before DPLL
         * enabling. This is only under driver's control after
@@ -5107,14 +5218,12 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
                I915_WRITE(PCH_LVDS, temp);
        }
 
-       /* set the dithering flag and clear for anything other than a panel. */
        pipeconf &= ~PIPECONF_DITHER_EN;
        pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
-       if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
+       if ((is_lvds && dev_priv->lvds_dither) || dither) {
                pipeconf |= PIPECONF_DITHER_EN;
                pipeconf |= PIPECONF_DITHER_TYPE_ST1;
        }
-
        if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
                intel_dp_set_m_n(crtc, mode, adjusted_mode);
        } else {
@@ -6523,6 +6632,7 @@ static void intel_crtc_init(struct drm_device *dev, int 
pipe)
 
        intel_crtc_reset(&intel_crtc->base);
        intel_crtc->active = true; /* force the pipe off on setup_init_config */
+       intel_crtc->bpp = 24; /* default for pre-Ironlake */
 
        if (HAS_PCH_SPLIT(dev)) {
                intel_helper_funcs.prepare = ironlake_crtc_prepare;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 9ffa61e..81bf49e 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -170,6 +170,7 @@ struct intel_crtc {
        int16_t cursor_x, cursor_y;
        int16_t cursor_width, cursor_height;
        bool cursor_visible;
+       unsigned int bpp;
 };
 
 #define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
-- 
1.7.4.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to