Merge branch 'fbdev-next' of github.com:timur-tabi/linux-2.6 into for-linus
authorTomi Valkeinen <tomi.valkeinen@ti.com>
Tue, 27 Nov 2012 10:06:10 +0000 (12:06 +0200)
committerTomi Valkeinen <tomi.valkeinen@ti.com>
Tue, 27 Nov 2012 10:16:37 +0000 (12:16 +0200)
* 'fbdev-next' of github.com:timur-tabi/linux-2.6:
  drivers/video: fsl-diu-fb: store EDID data in the global object
  drivers/video: fsl-diu-fb: don't touch registers for unused features
  drivers/video: fsl-diu-fb: clean up reset of primary display
  drivers/video: fsl-diu-fb: remove unused 'cursor_reset' variable
  drivers/video: fsl-diu-fb: improve message displays
  drivers/video: fsl-diu-fb: streamline enabling of interrupts
  drivers/video: fsl-diu-fb: add support for set_gamma ioctls
  drivers/video: fsl-diu-fb: simplify platforms that have only one port
  MAINTAINERS: Make Timur Tabi the maintainer for the Freescale DIU driver

MAINTAINERS
drivers/video/fsl-diu-fb.c
include/linux/fsl-diu-fb.h

index 9386a63..477a20f 100644 (file)
@@ -3108,6 +3108,12 @@ F:       drivers/video/
 F:     include/video/
 F:     include/linux/fb.h
 
+FREESCALE DIU FRAMEBUFFER DRIVER
+M:     Timur Tabi <timur@freescale.com>
+L:     linux-fbdev@vger.kernel.org
+S:     Supported
+F:     drivers/video/fsl-diu-fb.*
+
 FREESCALE DMA DRIVER
 M:     Li Yang <leoli@freescale.com>
 M:     Zhang Wei <zw@zh-kernel.org>
index ede9e55..d3fc92e 100644 (file)
@@ -337,13 +337,11 @@ struct mfb_info {
        int registered;
        unsigned long pseudo_palette[16];
        struct diu_ad *ad;
-       int cursor_reset;
        unsigned char g_alpha;
        unsigned int count;
        int x_aoi_d;            /* aoi display x offset to physical screen */
        int y_aoi_d;            /* aoi display y offset to physical screen */
        struct fsl_diu_data *parent;
-       u8 *edid_data;
 };
 
 /**
@@ -378,6 +376,8 @@ struct fsl_diu_data {
        struct diu_ad ad[NUM_AOIS] __aligned(8);
        u8 gamma[256 * 3] __aligned(32);
        u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32);
+       uint8_t edid_data[EDID_LENGTH];
+       bool has_edid;
 } __aligned(32);
 
 /* Determine the DMA address of a member of the fsl_diu_data structure */
@@ -430,6 +430,22 @@ static struct mfb_info mfb_template[] = {
        },
 };
 
+#ifdef DEBUG
+static void __attribute__ ((unused)) fsl_diu_dump(struct diu __iomem *hw)
+{
+       mb();
+       pr_debug("DIU: desc=%08x,%08x,%08x, gamma=%08x pallete=%08x "
+                "cursor=%08x curs_pos=%08x diu_mode=%08x bgnd=%08x "
+                "disp_size=%08x hsyn_para=%08x vsyn_para=%08x syn_pol=%08x "
+                "thresholds=%08x int_mask=%08x plut=%08x\n",
+                hw->desc[0], hw->desc[1], hw->desc[2], hw->gamma,
+                hw->pallete, hw->cursor, hw->curs_pos, hw->diu_mode,
+                hw->bgnd, hw->disp_size, hw->hsyn_para, hw->vsyn_para,
+                hw->syn_pol, hw->thresholds, hw->int_mask, hw->plut);
+       rmb();
+}
+#endif
+
 /**
  * fsl_diu_name_to_port - convert a port name to a monitor port enum
  *
@@ -481,8 +497,7 @@ static void fsl_diu_enable_panel(struct fb_info *info)
 
        switch (mfbi->index) {
        case PLANE0:
-               if (hw->desc[0] != ad->paddr)
-                       wr_reg_wa(&hw->desc[0], ad->paddr);
+               wr_reg_wa(&hw->desc[0], ad->paddr);
                break;
        case PLANE1_AOI0:
                cmfbi = &data->mfb[2];
@@ -534,8 +549,7 @@ static void fsl_diu_disable_panel(struct fb_info *info)
 
        switch (mfbi->index) {
        case PLANE0:
-               if (hw->desc[0] != data->dummy_ad.paddr)
-                       wr_reg_wa(&hw->desc[0], data->dummy_ad.paddr);
+               wr_reg_wa(&hw->desc[0], 0);
                break;
        case PLANE1_AOI0:
                cmfbi = &data->mfb[2];
@@ -792,7 +806,8 @@ static void update_lcdc(struct fb_info *info)
 
        hw = data->diu_reg;
 
-       diu_ops.set_monitor_port(data->monitor_port);
+       if (diu_ops.set_monitor_port)
+               diu_ops.set_monitor_port(data->monitor_port);
        gamma_table_base = data->gamma;
 
        /* Prep for DIU init  - gamma table, cursor table */
@@ -811,12 +826,8 @@ static void update_lcdc(struct fb_info *info)
        out_be32(&hw->gamma, DMA_ADDR(data, gamma));
        out_be32(&hw->cursor, DMA_ADDR(data, cursor));
 
-       out_be32(&hw->bgnd, 0x007F7F7F);        /* BGND */
-       out_be32(&hw->bgnd_wb, 0);              /* BGND_WB */
-       out_be32(&hw->disp_size, (var->yres << 16 | var->xres));
-                                               /* DISP SIZE */
-       out_be32(&hw->wb_size, 0); /* WB SIZE */
-       out_be32(&hw->wb_mem_addr, 0); /* WB MEM ADDR */
+       out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */
+       out_be32(&hw->disp_size, (var->yres << 16) | var->xres);
 
        /* Horizontal and vertical configuration register */
        temp = var->left_margin << 22 | /* BP_H */
@@ -833,9 +844,20 @@ static void update_lcdc(struct fb_info *info)
 
        diu_ops.set_pixel_clock(var->pixclock);
 
-       out_be32(&hw->syn_pol, 0);      /* SYNC SIGNALS POLARITY */
-       out_be32(&hw->int_status, 0);   /* INTERRUPT STATUS */
+#ifndef CONFIG_PPC_MPC512x
+       /*
+        * The PLUT register is defined differently on the MPC5121 than it
+        * is on other SOCs.  Unfortunately, there's no documentation that
+        * explains how it's supposed to be programmed, so for now, we leave
+        * it at the default value on the MPC5121.
+        *
+        * For other SOCs, program it for the highest priority, which will
+        * reduce the chance of underrun. Technically, we should scale the
+        * priority to match the screen resolution, but doing that properly
+        * requires delicate fine-tuning for each use-case.
+        */
        out_be32(&hw->plut, 0x01F5F666);
+#endif
 
        /* Enable the DIU */
        enable_lcdc(info);
@@ -965,7 +987,6 @@ static int fsl_diu_set_par(struct fb_info *info)
        hw = data->diu_reg;
 
        set_fix(info);
-       mfbi->cursor_reset = 1;
 
        len = info->var.yres_virtual * info->fix.line_length;
        /* Alloc & dealloc each time resolution/bpp change */
@@ -1107,6 +1128,12 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
 
        if (!arg)
                return -EINVAL;
+
+       dev_dbg(info->dev, "ioctl %08x (dir=%s%s type=%u nr=%u size=%u)\n", cmd,
+               _IOC_DIR(cmd) & _IOC_READ ? "R" : "",
+               _IOC_DIR(cmd) & _IOC_WRITE ? "W" : "",
+               _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));
+
        switch (cmd) {
        case MFB_SET_PIXFMT_OLD:
                dev_warn(info->dev,
@@ -1180,6 +1207,23 @@ static int fsl_diu_ioctl(struct fb_info *info, unsigned int cmd,
                        ad->ckmin_b = ck.blue_min;
                }
                break;
+#ifdef CONFIG_PPC_MPC512x
+       case MFB_SET_GAMMA: {
+               struct fsl_diu_data *data = mfbi->parent;
+
+               if (copy_from_user(data->gamma, buf, sizeof(data->gamma)))
+                       return -EFAULT;
+               setbits32(&data->diu_reg->gamma, 0); /* Force table reload */
+               break;
+       }
+       case MFB_GET_GAMMA: {
+               struct fsl_diu_data *data = mfbi->parent;
+
+               if (copy_to_user(buf, data->gamma, sizeof(data->gamma)))
+                       return -EFAULT;
+               break;
+       }
+#endif
        default:
                dev_err(info->dev, "unknown ioctl command (0x%08X)\n", cmd);
                return -ENOIOCTLCMD;
@@ -1206,8 +1250,22 @@ static int fsl_diu_open(struct fb_info *info, int user)
                res = fsl_diu_set_par(info);
                if (res < 0)
                        mfbi->count--;
-               else
+               else {
+                       struct fsl_diu_data *data = mfbi->parent;
+
+#ifdef CONFIG_NOT_COHERENT_CACHE
+                       /*
+                        * Enable underrun detection and vertical sync
+                        * interrupts.
+                        */
+                       clrbits32(&data->diu_reg->int_mask,
+                                 INT_UNDRUN | INT_VSYNC);
+#else
+                       /* Enable underrun detection */
+                       clrbits32(&data->diu_reg->int_mask, INT_UNDRUN);
+#endif
                        fsl_diu_enable_panel(info);
+               }
        }
 
        spin_unlock(&diu_lock);
@@ -1223,8 +1281,13 @@ static int fsl_diu_release(struct fb_info *info, int user)
 
        spin_lock(&diu_lock);
        mfbi->count--;
-       if (mfbi->count == 0)
+       if (mfbi->count == 0) {
+               struct fsl_diu_data *data = mfbi->parent;
+
+               /* Disable interrupts */
+               out_be32(&data->diu_reg->int_mask, 0xffffffff);
                fsl_diu_disable_panel(info);
+       }
 
        spin_unlock(&diu_lock);
        return res;
@@ -1248,6 +1311,7 @@ static int __devinit install_fb(struct fb_info *info)
 {
        int rc;
        struct mfb_info *mfbi = info->par;
+       struct fsl_diu_data *data = mfbi->parent;
        const char *aoi_mode, *init_aoi_mode = "320x240";
        struct fb_videomode *db = fsl_diu_mode_db;
        unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db);
@@ -1264,9 +1328,9 @@ static int __devinit install_fb(struct fb_info *info)
                return rc;
 
        if (mfbi->index == PLANE0) {
-               if (mfbi->edid_data) {
+               if (data->has_edid) {
                        /* Now build modedb from EDID */
-                       fb_edid_to_monspecs(mfbi->edid_data, &info->monspecs);
+                       fb_edid_to_monspecs(data->edid_data, &info->monspecs);
                        fb_videomode_to_modelist(info->monspecs.modedb,
                                                 info->monspecs.modedb_len,
                                                 &info->modelist);
@@ -1284,7 +1348,7 @@ static int __devinit install_fb(struct fb_info *info)
                 * For plane 0 we continue and look into
                 * driver's internal modedb.
                 */
-               if ((mfbi->index == PLANE0) && mfbi->edid_data)
+               if ((mfbi->index == PLANE0) && data->has_edid)
                        has_default_mode = 0;
                else
                        return -EINVAL;
@@ -1348,9 +1412,6 @@ static void uninstall_fb(struct fb_info *info)
        if (!mfbi->registered)
                return;
 
-       if (mfbi->index == PLANE0)
-               kfree(mfbi->edid_data);
-
        unregister_framebuffer(info);
        unmap_video_memory(info);
        if (&info->cmap)
@@ -1362,7 +1423,7 @@ static void uninstall_fb(struct fb_info *info)
 static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
 {
        struct diu __iomem *hw = dev_id;
-       unsigned int status = in_be32(&hw->int_status);
+       uint32_t status = in_be32(&hw->int_status);
 
        if (status) {
                /* This is the workaround for underrun */
@@ -1387,40 +1448,6 @@ static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
        return IRQ_NONE;
 }
 
-static int request_irq_local(struct fsl_diu_data *data)
-{
-       struct diu __iomem *hw = data->diu_reg;
-       u32 ints;
-       int ret;
-
-       /* Read to clear the status */
-       in_be32(&hw->int_status);
-
-       ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb", hw);
-       if (!ret) {
-               ints = INT_PARERR | INT_LS_BF_VS;
-#if !defined(CONFIG_NOT_COHERENT_CACHE)
-               ints |= INT_VSYNC;
-#endif
-
-               /* Read to clear the status */
-               in_be32(&hw->int_status);
-               out_be32(&hw->int_mask, ints);
-       }
-
-       return ret;
-}
-
-static void free_irq_local(struct fsl_diu_data *data)
-{
-       struct diu __iomem *hw = data->diu_reg;
-
-       /* Disable all LCDC interrupt */
-       out_be32(&hw->int_mask, 0x1f);
-
-       free_irq(data->irq, NULL);
-}
-
 #ifdef CONFIG_PM
 /*
  * Power management hooks. Note that we won't be called from IRQ context,
@@ -1496,8 +1523,8 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
        struct device_node *np = pdev->dev.of_node;
        struct mfb_info *mfbi;
        struct fsl_diu_data *data;
-       int diu_mode;
        dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */
+       const void *prop;
        unsigned int i;
        int ret;
 
@@ -1541,17 +1568,13 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
                memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
                mfbi->parent = data;
                mfbi->ad = &data->ad[i];
+       }
 
-               if (mfbi->index == PLANE0) {
-                       const u8 *prop;
-                       int len;
-
-                       /* Get EDID */
-                       prop = of_get_property(np, "edid", &len);
-                       if (prop && len == EDID_LENGTH)
-                               mfbi->edid_data = kmemdup(prop, EDID_LENGTH,
-                                                         GFP_KERNEL);
-               }
+       /* Get the EDID data from the device tree, if present */
+       prop = of_get_property(np, "edid", &ret);
+       if (prop && ret == EDID_LENGTH) {
+               memcpy(data->edid_data, prop, EDID_LENGTH);
+               data->has_edid = true;
        }
 
        data->diu_reg = of_iomap(np, 0);
@@ -1561,10 +1584,6 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
                goto error;
        }
 
-       diu_mode = in_be32(&data->diu_reg->diu_mode);
-       if (diu_mode == MFB_MODE0)
-               out_be32(&data->diu_reg->diu_mode, 0); /* disable DIU */
-
        /* Get the IRQ of the DIU */
        data->irq = irq_of_parse_and_map(np, 0);
 
@@ -1586,11 +1605,11 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
        data->dummy_ad.paddr = DMA_ADDR(data, dummy_ad);
 
        /*
-        * Let DIU display splash screen if it was pre-initialized
-        * by the bootloader, set dummy area descriptor otherwise.
+        * Let DIU continue to display splash screen if it was pre-initialized
+        * by the bootloader; otherwise, clear the display.
         */
-       if (diu_mode == MFB_MODE0)
-               out_be32(&data->diu_reg->desc[0], data->dummy_ad.paddr);
+       if (in_be32(&data->diu_reg->diu_mode) == MFB_MODE0)
+               out_be32(&data->diu_reg->desc[0], 0);
 
        out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr);
        out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr);
@@ -1603,7 +1622,16 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
                }
        }
 
-       if (request_irq_local(data)) {
+       /*
+        * Older versions of U-Boot leave interrupts enabled, so disable
+        * all of them and clear the status register.
+        */
+       out_be32(&data->diu_reg->int_mask, 0xffffffff);
+       in_be32(&data->diu_reg->int_status);
+
+       ret = request_irq(data->irq, fsl_diu_isr, 0, "fsl-diu-fb",
+                         &data->diu_reg);
+       if (ret) {
                dev_err(&pdev->dev, "could not claim irq\n");
                goto error;
        }
@@ -1638,7 +1666,8 @@ static int fsl_diu_remove(struct platform_device *pdev)
 
        data = dev_get_drvdata(&pdev->dev);
        disable_lcdc(&data->fsl_diu_info[0]);
-       free_irq_local(data);
+
+       free_irq(data->irq, &data->diu_reg);
 
        for (i = 0; i < NUM_AOIS; i++)
                uninstall_fb(&data->fsl_diu_info[i]);
@@ -1741,6 +1770,9 @@ static int __init fsl_diu_init(void)
        coherence_data_size = be32_to_cpup(prop) * 13;
        coherence_data_size /= 8;
 
+       pr_debug("fsl-diu-fb: coherence data size is %zu bytes\n",
+                coherence_data_size);
+
        prop = of_get_property(np, "d-cache-line-size", NULL);
        if (prop == NULL) {
                pr_err("fsl-diu-fb: missing 'd-cache-line-size' property' "
@@ -1750,10 +1782,17 @@ static int __init fsl_diu_init(void)
        }
        d_cache_line_size = be32_to_cpup(prop);
 
+       pr_debug("fsl-diu-fb: cache lines size is %u bytes\n",
+                d_cache_line_size);
+
        of_node_put(np);
        coherence_data = vmalloc(coherence_data_size);
-       if (!coherence_data)
+       if (!coherence_data) {
+               pr_err("fsl-diu-fb: could not allocate coherence data "
+                      "(size=%zu)\n", coherence_data_size);
                return -ENOMEM;
+       }
+
 #endif
 
        ret = platform_driver_register(&fsl_diu_driver);
index 11c16a1..a1e8277 100644 (file)
@@ -47,6 +47,15 @@ struct aoi_display_offset {
 #define MFB_GET_PIXFMT         _IOR('M', 8, __u32)
 
 /*
+ * The MPC5121 BSP comes with a gamma_set utility that initializes the
+ * gamma table.  Unfortunately, it uses bad values for the IOCTL commands,
+ * but there's nothing we can do about it now.  These ioctls are only
+ * supported on the MPC5121.
+ */
+#define MFB_SET_GAMMA          _IOW('M', 1, __u8)
+#define MFB_GET_GAMMA          _IOR('M', 1, __u8)
+
+/*
  * The original definitions of MFB_SET_PIXFMT and MFB_GET_PIXFMT used the
  * wrong value for 'size' field of the ioctl.  The current macros above use the
  * right size, but we still need to provide backwards compatibility, at least