drm/sun4i: tcon: Add dithering support for RGB565/RGB666 LCD panels
[sfrench/cifs-2.6.git] / drivers / gpu / drm / sun4i / sun4i_tcon.c
index d6f9d5f3b15b05266d768601200b303971ce7c9b..4834c90b49127d3e004cf47fd95a00f971c3943a 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_encoder.h>
@@ -277,6 +278,57 @@ static void sun4i_tcon0_mode_set_common(struct sun4i_tcon *tcon,
                     SUN4I_TCON0_BASIC0_Y(mode->crtc_vdisplay));
 }
 
+static void sun4i_tcon0_mode_set_dithering(struct sun4i_tcon *tcon,
+                                          const struct drm_connector *connector)
+{
+       u32 bus_format = 0;
+       u32 val = 0;
+
+       /* XXX Would this ever happen? */
+       if (!connector)
+               return;
+
+       /*
+        * FIXME: Undocumented bits
+        *
+        * The whole dithering process and these parameters are not
+        * explained in the vendor documents or BSP kernel code.
+        */
+       regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_PR_REG, 0x11111111);
+       regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_PG_REG, 0x11111111);
+       regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_PB_REG, 0x11111111);
+       regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_LR_REG, 0x11111111);
+       regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_LG_REG, 0x11111111);
+       regmap_write(tcon->regs, SUN4I_TCON0_FRM_SEED_LB_REG, 0x11111111);
+       regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL0_REG, 0x01010000);
+       regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL1_REG, 0x15151111);
+       regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL2_REG, 0x57575555);
+       regmap_write(tcon->regs, SUN4I_TCON0_FRM_TBL3_REG, 0x7f7f7777);
+
+       /* Do dithering if panel only supports 6 bits per color */
+       if (connector->display_info.bpc == 6)
+               val |= SUN4I_TCON0_FRM_CTL_EN;
+
+       if (connector->display_info.num_bus_formats == 1)
+               bus_format = connector->display_info.bus_formats[0];
+
+       /* Check the connection format */
+       switch (bus_format) {
+       case MEDIA_BUS_FMT_RGB565_1X16:
+               /* R and B components are only 5 bits deep */
+               val |= SUN4I_TCON0_FRM_CTL_MODE_R;
+               val |= SUN4I_TCON0_FRM_CTL_MODE_B;
+       case MEDIA_BUS_FMT_RGB666_1X18:
+       case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+               /* Fall through: enable dithering */
+               val |= SUN4I_TCON0_FRM_CTL_EN;
+               break;
+       }
+
+       /* Write dithering settings */
+       regmap_write(tcon->regs, SUN4I_TCON_FRM_CTL_REG, val);
+}
+
 static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon,
                                     const struct drm_encoder *encoder,
                                     const struct drm_display_mode *mode)
@@ -294,6 +346,9 @@ static void sun4i_tcon0_mode_set_cpu(struct sun4i_tcon *tcon,
 
        sun4i_tcon0_mode_set_common(tcon, mode);
 
+       /* Set dithering if needed */
+       sun4i_tcon0_mode_set_dithering(tcon, sun4i_tcon_get_connector(encoder));
+
        regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
                           SUN4I_TCON0_CTL_IF_MASK,
                           SUN4I_TCON0_CTL_IF_8080);
@@ -359,6 +414,9 @@ static void sun4i_tcon0_mode_set_lvds(struct sun4i_tcon *tcon,
        tcon->dclk_max_div = 7;
        sun4i_tcon0_mode_set_common(tcon, mode);
 
+       /* Set dithering if needed */
+       sun4i_tcon0_mode_set_dithering(tcon, sun4i_tcon_get_connector(encoder));
+
        /* Adjust clock delay */
        clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
        regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
@@ -432,6 +490,9 @@ static void sun4i_tcon0_mode_set_rgb(struct sun4i_tcon *tcon,
        tcon->dclk_max_div = 127;
        sun4i_tcon0_mode_set_common(tcon, mode);
 
+       /* Set dithering if needed */
+       sun4i_tcon0_mode_set_dithering(tcon, tcon->panel->connector);
+
        /* Adjust clock delay */
        clk_delay = sun4i_tcon_get_clk_delay(mode, 0);
        regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,