video: xilinxfb: Add support for little endian accesses
[cascardo/linux.git] / drivers / video / xilinxfb.c
index af0b4fd..f3d4a69 100644 (file)
@@ -44,7 +44,7 @@
 
 
 /*
- * Xilinx calls it "PLB TFT LCD Controller" though it can also be used for
+ * Xilinx calls it "TFT LCD Controller" though it can also be used for
  * the VGA port on the Xilinx ML40x board. This is a hardware display
  * controller for a 640x480 resolution TFT or VGA screen.
  *
  * don't start thinking about scrolling).  The second allows the LCD to
  * be turned on or off as well as rotated 180 degrees.
  *
- * In case of direct PLB access the second control register will be at
+ * In case of direct BUS access the second control register will be at
  * an offset of 4 as compared to the DCR access where the offset is 1
  * i.e. REG_CTRL. So this is taken care in the function
- * xilinx_fb_out_be32 where it left shifts the offset 2 times in case of
- * direct PLB access.
+ * xilinx_fb_out32 where it left shifts the offset 2 times in case of
+ * direct BUS access.
  */
 #define NUM_REGS       2
 #define REG_FB_ADDR    0
@@ -116,7 +116,8 @@ static struct fb_var_screeninfo xilinx_fb_var = {
 };
 
 
-#define PLB_ACCESS_FLAG        0x1             /* 1 = PLB, 0 = DCR */
+#define BUS_ACCESS_FLAG                0x1 /* 1 = BUS, 0 = DCR */
+#define LITTLE_ENDIAN_ACCESS   0x2 /* LITTLE ENDIAN IO functions */
 
 struct xilinxfb_drvdata {
 
@@ -146,21 +147,40 @@ struct xilinxfb_drvdata {
        container_of(_info, struct xilinxfb_drvdata, info)
 
 /*
- * The XPS TFT Controller can be accessed through PLB or DCR interface.
+ * The XPS TFT Controller can be accessed through BUS or DCR interface.
  * To perform the read/write on the registers we need to check on
  * which bus its connected and call the appropriate write API.
  */
-static void xilinx_fb_out_be32(struct xilinxfb_drvdata *drvdata, u32 offset,
+static void xilinx_fb_out32(struct xilinxfb_drvdata *drvdata, u32 offset,
                                u32 val)
 {
-       if (drvdata->flags & PLB_ACCESS_FLAG)
-               out_be32(drvdata->regs + (offset << 2), val);
+       if (drvdata->flags & BUS_ACCESS_FLAG) {
+               if (drvdata->flags & LITTLE_ENDIAN_ACCESS)
+                       iowrite32(val, drvdata->regs + (offset << 2));
+               else
+                       iowrite32be(val, drvdata->regs + (offset << 2));
+       }
 #ifdef CONFIG_PPC_DCR
        else
                dcr_write(drvdata->dcr_host, offset, val);
 #endif
 }
 
+static u32 xilinx_fb_in32(struct xilinxfb_drvdata *drvdata, u32 offset)
+{
+       if (drvdata->flags & BUS_ACCESS_FLAG) {
+               if (drvdata->flags & LITTLE_ENDIAN_ACCESS)
+                       return ioread32(drvdata->regs + (offset << 2));
+               else
+                       return ioread32be(drvdata->regs + (offset << 2));
+       }
+#ifdef CONFIG_PPC_DCR
+       else
+               return dcr_read(drvdata->dcr_host, offset);
+#endif
+       return 0;
+}
+
 static int
 xilinx_fb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue,
        unsigned transp, struct fb_info *fbi)
@@ -197,7 +217,7 @@ xilinx_fb_blank(int blank_mode, struct fb_info *fbi)
        switch (blank_mode) {
        case FB_BLANK_UNBLANK:
                /* turn on panel */
-               xilinx_fb_out_be32(drvdata, REG_CTRL, drvdata->reg_ctrl_default);
+               xilinx_fb_out32(drvdata, REG_CTRL, drvdata->reg_ctrl_default);
                break;
 
        case FB_BLANK_NORMAL:
@@ -205,7 +225,7 @@ xilinx_fb_blank(int blank_mode, struct fb_info *fbi)
        case FB_BLANK_HSYNC_SUSPEND:
        case FB_BLANK_POWERDOWN:
                /* turn off panel */
-               xilinx_fb_out_be32(drvdata, REG_CTRL, 0);
+               xilinx_fb_out32(drvdata, REG_CTRL, 0);
        default:
                break;
 
@@ -227,33 +247,23 @@ static struct fb_ops xilinxfb_ops =
  * Bus independent setup/teardown
  */
 
-static int xilinxfb_assign(struct device *dev,
+static int xilinxfb_assign(struct platform_device *pdev,
                           struct xilinxfb_drvdata *drvdata,
-                          unsigned long physaddr,
                           struct xilinxfb_platform_data *pdata)
 {
        int rc;
+       struct device *dev = &pdev->dev;
        int fbsize = pdata->xvirt * pdata->yvirt * BYTES_PER_PIXEL;
 
-       if (drvdata->flags & PLB_ACCESS_FLAG) {
-               /*
-                * Map the control registers in if the controller
-                * is on direct PLB interface.
-                */
-               if (!request_mem_region(physaddr, 8, DRIVER_NAME)) {
-                       dev_err(dev, "Couldn't lock memory region at 0x%08lX\n",
-                               physaddr);
-                       rc = -ENODEV;
-                       goto err_region;
-               }
+       if (drvdata->flags & BUS_ACCESS_FLAG) {
+               struct resource *res;
 
-               drvdata->regs_phys = physaddr;
-               drvdata->regs = ioremap(physaddr, 8);
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               drvdata->regs_phys = res->start;
+               drvdata->regs = devm_request_and_ioremap(&pdev->dev, res);
                if (!drvdata->regs) {
-                       dev_err(dev, "Couldn't lock memory region at 0x%08lX\n",
-                               physaddr);
-                       rc = -ENODEV;
-                       goto err_map;
+                       rc = -EADDRNOTAVAIL;
+                       goto err_region;
                }
        }
 
@@ -270,7 +280,7 @@ static int xilinxfb_assign(struct device *dev,
        if (!drvdata->fb_virt) {
                dev_err(dev, "Could not allocate frame buffer memory\n");
                rc = -ENOMEM;
-               if (drvdata->flags & PLB_ACCESS_FLAG)
+               if (drvdata->flags & BUS_ACCESS_FLAG)
                        goto err_fbmem;
                else
                        goto err_region;
@@ -280,13 +290,19 @@ static int xilinxfb_assign(struct device *dev,
        memset_io((void __iomem *)drvdata->fb_virt, 0, fbsize);
 
        /* Tell the hardware where the frame buffer is */
-       xilinx_fb_out_be32(drvdata, REG_FB_ADDR, drvdata->fb_phys);
+       xilinx_fb_out32(drvdata, REG_FB_ADDR, drvdata->fb_phys);
+       rc = xilinx_fb_in32(drvdata, REG_FB_ADDR);
+       /* Endianess detection */
+       if (rc != drvdata->fb_phys) {
+               drvdata->flags |= LITTLE_ENDIAN_ACCESS;
+               xilinx_fb_out32(drvdata, REG_FB_ADDR, drvdata->fb_phys);
+       }
 
        /* Turn on the display */
        drvdata->reg_ctrl_default = REG_CTRL_ENABLE;
        if (pdata->rotate_screen)
                drvdata->reg_ctrl_default |= REG_CTRL_ROTATE;
-       xilinx_fb_out_be32(drvdata, REG_CTRL,
+       xilinx_fb_out32(drvdata, REG_CTRL,
                                        drvdata->reg_ctrl_default);
 
        /* Fill struct fb_info */
@@ -323,9 +339,9 @@ static int xilinxfb_assign(struct device *dev,
                goto err_regfb;
        }
 
-       if (drvdata->flags & PLB_ACCESS_FLAG) {
+       if (drvdata->flags & BUS_ACCESS_FLAG) {
                /* Put a banner in the log (for DEBUG) */
-               dev_dbg(dev, "regs: phys=%lx, virt=%p\n", physaddr,
+               dev_dbg(dev, "regs: phys=%x, virt=%p\n", drvdata->regs_phys,
                                        drvdata->regs);
        }
        /* Put a banner in the log (for DEBUG) */
@@ -345,15 +361,11 @@ err_cmap:
                iounmap(drvdata->fb_virt);
 
        /* Turn off the display */
-       xilinx_fb_out_be32(drvdata, REG_CTRL, 0);
+       xilinx_fb_out32(drvdata, REG_CTRL, 0);
 
 err_fbmem:
-       if (drvdata->flags & PLB_ACCESS_FLAG)
-               iounmap(drvdata->regs);
-
-err_map:
-       if (drvdata->flags & PLB_ACCESS_FLAG)
-               release_mem_region(physaddr, 8);
+       if (drvdata->flags & BUS_ACCESS_FLAG)
+               devm_iounmap(dev, drvdata->regs);
 
 err_region:
        kfree(drvdata);
@@ -381,13 +393,11 @@ static int xilinxfb_release(struct device *dev)
                iounmap(drvdata->fb_virt);
 
        /* Turn off the display */
-       xilinx_fb_out_be32(drvdata, REG_CTRL, 0);
+       xilinx_fb_out32(drvdata, REG_CTRL, 0);
 
        /* Release the resources, as allocated based on interface */
-       if (drvdata->flags & PLB_ACCESS_FLAG) {
-               iounmap(drvdata->regs);
-               release_mem_region(drvdata->regs_phys, 8);
-       }
+       if (drvdata->flags & BUS_ACCESS_FLAG)
+               devm_iounmap(dev, drvdata->regs);
 #ifdef CONFIG_PPC_DCR
        else
                dcr_unmap(drvdata->dcr_host, drvdata->dcr_len);
@@ -406,11 +416,9 @@ static int xilinxfb_release(struct device *dev)
 static int xilinxfb_of_probe(struct platform_device *op)
 {
        const u32 *prop;
-       u32 *p;
-       u32 tft_access;
+       u32 tft_access = 0;
        struct xilinxfb_platform_data pdata;
-       struct resource res;
-       int size, rc;
+       int size;
        struct xilinxfb_drvdata *drvdata;
 
        /* Copy with the default pdata (not a ptr reference!) */
@@ -424,34 +432,29 @@ static int xilinxfb_of_probe(struct platform_device *op)
        }
 
        /*
-        * To check whether the core is connected directly to DCR or PLB
+        * To check whether the core is connected directly to DCR or BUS
         * interface and initialize the tft_access accordingly.
         */
-       p = (u32 *)of_get_property(op->dev.of_node, "xlnx,dcr-splb-slave-if", NULL);
-       tft_access = p ? *p : 0;
+       of_property_read_u32(op->dev.of_node, "xlnx,dcr-splb-slave-if",
+                            &tft_access);
 
        /*
-        * Fill the resource structure if its direct PLB interface
+        * Fill the resource structure if its direct BUS interface
         * otherwise fill the dcr_host structure.
         */
        if (tft_access) {
-               drvdata->flags |= PLB_ACCESS_FLAG;
-               rc = of_address_to_resource(op->dev.of_node, 0, &res);
-               if (rc) {
-                       dev_err(&op->dev, "invalid address\n");
-                       goto err;
-               }
+               drvdata->flags |= BUS_ACCESS_FLAG;
        }
 #ifdef CONFIG_PPC_DCR
        else {
                int start;
-               res.start = 0;
                start = dcr_resource_start(op->dev.of_node, 0);
                drvdata->dcr_len = dcr_resource_len(op->dev.of_node, 0);
                drvdata->dcr_host = dcr_map(op->dev.of_node, start, drvdata->dcr_len);
                if (!DCR_MAP_OK(drvdata->dcr_host)) {
                        dev_err(&op->dev, "invalid DCR address\n");
-                       goto err;
+                       kfree(drvdata);
+                       return -ENODEV;
                }
        }
 #endif
@@ -478,11 +481,7 @@ static int xilinxfb_of_probe(struct platform_device *op)
                pdata.rotate_screen = 1;
 
        dev_set_drvdata(&op->dev, drvdata);
-       return xilinxfb_assign(&op->dev, drvdata, res.start, &pdata);
-
- err:
-       kfree(drvdata);
-       return -ENODEV;
+       return xilinxfb_assign(op, drvdata, &pdata);
 }
 
 static int xilinxfb_of_remove(struct platform_device *op)