drm/tilcdc: Clear bits of register we're going to set.
[firefly-linux-kernel-4.4.55.git] / drivers / gpu / drm / tilcdc / tilcdc_crtc.c
index 4de3fb4246fc8d5b4d55b99fcd22dc51c72bb3b5..7418dcd986d3f50f48f33d56b703cf8dc82c4be9 100644 (file)
@@ -42,7 +42,8 @@ struct tilcdc_crtc {
 
 static void unref_worker(struct work_struct *work)
 {
-       struct tilcdc_crtc *tilcdc_crtc = container_of(work, struct tilcdc_crtc, work);
+       struct tilcdc_crtc *tilcdc_crtc =
+               container_of(work, struct tilcdc_crtc, work);
        struct drm_device *dev = tilcdc_crtc->base.dev;
        struct drm_framebuffer *fb;
 
@@ -55,10 +56,12 @@ static void unref_worker(struct work_struct *work)
 static void set_scanout(struct drm_crtc *crtc, int n)
 {
        static const uint32_t base_reg[] = {
-                       LCDC_DMA_FB_BASE_ADDR_0_REG, LCDC_DMA_FB_BASE_ADDR_1_REG,
+                       LCDC_DMA_FB_BASE_ADDR_0_REG,
+                       LCDC_DMA_FB_BASE_ADDR_1_REG,
        };
        static const uint32_t ceil_reg[] = {
-                       LCDC_DMA_FB_CEILING_ADDR_0_REG, LCDC_DMA_FB_CEILING_ADDR_1_REG,
+                       LCDC_DMA_FB_CEILING_ADDR_0_REG,
+                       LCDC_DMA_FB_CEILING_ADDR_1_REG,
        };
        static const uint32_t stat[] = {
                        LCDC_END_OF_FRAME0, LCDC_END_OF_FRAME1,
@@ -194,7 +197,8 @@ static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode)
                tilcdc_crtc->frame_done = false;
                stop(crtc);
 
-               /* if necessary wait for framedone irq which will still come
+               /*
+                * if necessary wait for framedone irq which will still come
                 * before putting things to sleep..
                 */
                if (priv->rev == 2) {
@@ -289,17 +293,24 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
        reg = tilcdc_read(dev, LCDC_RASTER_TIMING_2_REG) & ~0x000fff00;
        reg |= LCDC_AC_BIAS_FREQUENCY(info->ac_bias) |
                LCDC_AC_BIAS_TRANSITIONS_PER_INT(info->ac_bias_intrpt);
+
+       /*
+        * subtract one from hfp, hbp, hsw because the hardware uses
+        * a value of 0 as 1
+        */
        if (priv->rev == 2) {
-               reg |= (hfp & 0x300) >> 8;
-               reg |= (hbp & 0x300) >> 4;
-               reg |= (hsw & 0x3c0) << 21;
+               /* clear bits we're going to set */
+               reg &= ~0x78000033;
+               reg |= ((hfp-1) & 0x300) >> 8;
+               reg |= ((hbp-1) & 0x300) >> 4;
+               reg |= ((hsw-1) & 0x3c0) << 21;
        }
        tilcdc_write(dev, LCDC_RASTER_TIMING_2_REG, reg);
 
        reg = (((mode->hdisplay >> 4) - 1) << 4) |
-               ((hbp & 0xff) << 24) |
-               ((hfp & 0xff) << 16) |
-               ((hsw & 0x3f) << 10);
+               (((hbp-1) & 0xff) << 24) |
+               (((hfp-1) & 0xff) << 16) |
+               (((hsw-1) & 0x3f) << 10);
        if (priv->rev == 2)
                reg |= (((mode->hdisplay >> 4) - 1) & 0x40) >> 3;
        tilcdc_write(dev, LCDC_RASTER_TIMING_0_REG, reg);
@@ -307,9 +318,24 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc,
        reg = ((mode->vdisplay - 1) & 0x3ff) |
                ((vbp & 0xff) << 24) |
                ((vfp & 0xff) << 16) |
-               ((vsw & 0x3f) << 10);
+               (((vsw-1) & 0x3f) << 10);
        tilcdc_write(dev, LCDC_RASTER_TIMING_1_REG, reg);
 
+       /*
+        * be sure to set Bit 10 for the V2 LCDC controller,
+        * otherwise limited to 1024 pixels width, stopping
+        * 1920x1080 being suppoted.
+        */
+       if (priv->rev == 2) {
+               if ((mode->vdisplay - 1) & 0x400) {
+                       tilcdc_set(dev, LCDC_RASTER_TIMING_2_REG,
+                               LCDC_LPP_B10);
+               } else {
+                       tilcdc_clear(dev, LCDC_RASTER_TIMING_2_REG,
+                               LCDC_LPP_B10);
+               }
+       }
+
        /* Configure display type: */
        reg = tilcdc_read(dev, LCDC_RASTER_CTRL_REG) &
                ~(LCDC_TFT_MODE | LCDC_MONO_8BIT_MODE | LCDC_MONOCHROME_MODE |
@@ -417,7 +443,12 @@ int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode)
 {
        struct tilcdc_drm_private *priv = crtc->dev->dev_private;
        unsigned int bandwidth;
+       uint32_t hbp, hfp, hsw, vbp, vfp, vsw;
 
+       /*
+        * check to see if the width is within the range that
+        * the LCD Controller physically supports
+        */
        if (mode->hdisplay > tilcdc_crtc_max_width(crtc))
                return MODE_VIRTUAL_X;
 
@@ -428,10 +459,70 @@ int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode)
        if (mode->vdisplay > 2048)
                return MODE_VIRTUAL_Y;
 
+       DBG("Processing mode %dx%d@%d with pixel clock %d",
+               mode->hdisplay, mode->vdisplay,
+               drm_mode_vrefresh(mode), mode->clock);
+
+       hbp = mode->htotal - mode->hsync_end;
+       hfp = mode->hsync_start - mode->hdisplay;
+       hsw = mode->hsync_end - mode->hsync_start;
+       vbp = mode->vtotal - mode->vsync_end;
+       vfp = mode->vsync_start - mode->vdisplay;
+       vsw = mode->vsync_end - mode->vsync_start;
+
+       if ((hbp-1) & ~0x3ff) {
+               DBG("Pruning mode: Horizontal Back Porch out of range");
+               return MODE_HBLANK_WIDE;
+       }
+
+       if ((hfp-1) & ~0x3ff) {
+               DBG("Pruning mode: Horizontal Front Porch out of range");
+               return MODE_HBLANK_WIDE;
+       }
+
+       if ((hsw-1) & ~0x3ff) {
+               DBG("Pruning mode: Horizontal Sync Width out of range");
+               return MODE_HSYNC_WIDE;
+       }
+
+       if (vbp & ~0xff) {
+               DBG("Pruning mode: Vertical Back Porch out of range");
+               return MODE_VBLANK_WIDE;
+       }
+
+       if (vfp & ~0xff) {
+               DBG("Pruning mode: Vertical Front Porch out of range");
+               return MODE_VBLANK_WIDE;
+       }
+
+       if ((vsw-1) & ~0x3f) {
+               DBG("Pruning mode: Vertical Sync Width out of range");
+               return MODE_VSYNC_WIDE;
+       }
+
+       /*
+        * some devices have a maximum allowed pixel clock
+        * configured from the DT
+        */
+       if (mode->clock > priv->max_pixelclock) {
+               DBG("Pruning mode: pixel clock too high");
+               return MODE_CLOCK_HIGH;
+       }
+
+       /*
+        * some devices further limit the max horizontal resolution
+        * configured from the DT
+        */
+       if (mode->hdisplay > priv->max_width)
+               return MODE_BAD_WIDTH;
+
        /* filter out modes that would require too much memory bandwidth: */
-       bandwidth = mode->hdisplay * mode->vdisplay * drm_mode_vrefresh(mode);
-       if (bandwidth > priv->max_bandwidth)
+       bandwidth = mode->hdisplay * mode->vdisplay *
+               drm_mode_vrefresh(mode);
+       if (bandwidth > priv->max_bandwidth) {
+               DBG("Pruning mode: exceeds defined bandwidth limit");
                return MODE_BAD;
+       }
 
        return MODE_OK;
 }