Merge branch 'for-linus' of git://oss.sgi.com/xfs/xfs
[cascardo/linux.git] / drivers / video / fsl-diu-fb.c
index a16beeb..acf292b 100644 (file)
@@ -36,8 +36,7 @@
 #include <linux/fsl-diu-fb.h>
 #include "edid.h"
 
-#define FSL_AOI_NUM    6       /* 5 AOIs and one dummy AOI */
-                               /* 1 for plane 0, 2 for plane 1&2 each */
+#define NUM_AOIS       5       /* 1 for plane 0, 2 for planes 1 & 2 each */
 
 /* HW cursor parameters */
 #define MAX_CURS               32
 #define INT_PARERR     0x08    /* Display parameters error interrupt */
 #define INT_LS_BF_VS   0x10    /* Lines before vsync. interrupt */
 
-struct diu_addr {
-       void *vaddr;            /* Virtual address */
-       dma_addr_t paddr;       /* Physical address */
-       __u32 offset;
-};
-
 /*
  * List of supported video modes
  *
@@ -330,23 +323,6 @@ static unsigned int d_cache_line_size;
 
 static DEFINE_SPINLOCK(diu_lock);
 
-struct fsl_diu_data {
-       struct fb_info *fsl_diu_info[FSL_AOI_NUM - 1];
-                               /*FSL_AOI_NUM has one dummy AOI */
-       struct device_attribute dev_attr;
-       struct diu_ad *dummy_ad;
-       void *dummy_aoi_virt;
-       unsigned int irq;
-       int fb_enabled;
-       enum fsl_diu_monitor_port monitor_port;
-       struct diu __iomem *diu_reg;
-       spinlock_t reg_lock;
-       struct diu_addr ad;
-       struct diu_addr gamma;
-       struct diu_addr pallete;
-       struct diu_addr cursor;
-};
-
 enum mfb_index {
        PLANE0 = 0,     /* Plane 0, only one AOI that fills the screen */
        PLANE1_AOI0,    /* Plane 1, first AOI */
@@ -370,6 +346,42 @@ struct mfb_info {
        u8 *edid_data;
 };
 
+/**
+ * struct fsl_diu_data - per-DIU data structure
+ * @dma_addr: DMA address of this structure
+ * @fsl_diu_info: fb_info objects, one per AOI
+ * @dev_attr: sysfs structure
+ * @irq: IRQ
+ * @monitor_port: the monitor port this DIU is connected to
+ * @diu_reg: pointer to the DIU hardware registers
+ * @reg_lock: spinlock for register access
+ * @dummy_aoi: video buffer for the 4x4 32-bit dummy AOI
+ * dummy_ad: DIU Area Descriptor for the dummy AOI
+ * @ad[]: Area Descriptors for each real AOI
+ * @gamma: gamma color table
+ * @cursor: hardware cursor data
+ *
+ * This data structure must be allocated with 32-byte alignment, so that the
+ * internal fields can be aligned properly.
+ */
+struct fsl_diu_data {
+       dma_addr_t dma_addr;
+       struct fb_info fsl_diu_info[NUM_AOIS];
+       struct mfb_info mfb[NUM_AOIS];
+       struct device_attribute dev_attr;
+       unsigned int irq;
+       enum fsl_diu_monitor_port monitor_port;
+       struct diu __iomem *diu_reg;
+       spinlock_t reg_lock;
+       u8 dummy_aoi[4 * 4 * 4];
+       struct diu_ad dummy_ad __aligned(8);
+       struct diu_ad ad[NUM_AOIS] __aligned(8);
+       u8 gamma[256 * 3] __aligned(32);
+       u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32);
+} __aligned(32);
+
+/* Determine the DMA address of a member of the fsl_diu_data structure */
+#define DMA_ADDR(p, f) ((p)->dma_addr + offsetof(struct fsl_diu_data, f))
 
 static struct mfb_info mfb_template[] = {
        {
@@ -449,37 +461,6 @@ static enum fsl_diu_monitor_port fsl_diu_name_to_port(const char *s)
        return diu_ops.valid_monitor_port(port);
 }
 
-/**
- * fsl_diu_alloc - allocate memory for the DIU
- * @size: number of bytes to allocate
- * @param: returned physical address of memory
- *
- * This function allocates a physically-contiguous block of memory.
- */
-static void *fsl_diu_alloc(size_t size, phys_addr_t *phys)
-{
-       void *virt;
-
-       virt = alloc_pages_exact(size, GFP_DMA | __GFP_ZERO);
-       if (virt)
-               *phys = virt_to_phys(virt);
-
-       return virt;
-}
-
-/**
- * fsl_diu_free - release DIU memory
- * @virt: pointer returned by fsl_diu_alloc()
- * @size: number of bytes allocated by fsl_diu_alloc()
- *
- * This function releases memory allocated by fsl_diu_alloc().
- */
-static void fsl_diu_free(void *virt, size_t size)
-{
-       if (virt && size)
-               free_pages_exact(virt, size);
-}
-
 /*
  * Workaround for failed writing desc register of planes.
  * Needed with MPC5121 DIU rev 2.0 silicon.
@@ -495,8 +476,8 @@ static void fsl_diu_enable_panel(struct fb_info *info)
 {
        struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
        struct diu_ad *ad = mfbi->ad;
-       struct fsl_diu_data *machine_data = mfbi->parent;
-       struct diu __iomem *hw = machine_data->diu_reg;
+       struct fsl_diu_data *data = mfbi->parent;
+       struct diu __iomem *hw = data->diu_reg;
 
        switch (mfbi->index) {
        case PLANE0:
@@ -504,7 +485,7 @@ static void fsl_diu_enable_panel(struct fb_info *info)
                        wr_reg_wa(&hw->desc[0], ad->paddr);
                break;
        case PLANE1_AOI0:
-               cmfbi = machine_data->fsl_diu_info[2]->par;
+               cmfbi = &data->mfb[2];
                if (hw->desc[1] != ad->paddr) { /* AOI0 closed */
                        if (cmfbi->count > 0)   /* AOI1 open */
                                ad->next_ad =
@@ -515,7 +496,7 @@ static void fsl_diu_enable_panel(struct fb_info *info)
                }
                break;
        case PLANE2_AOI0:
-               cmfbi = machine_data->fsl_diu_info[4]->par;
+               cmfbi = &data->mfb[4];
                if (hw->desc[2] != ad->paddr) { /* AOI0 closed */
                        if (cmfbi->count > 0)   /* AOI1 open */
                                ad->next_ad =
@@ -526,17 +507,17 @@ static void fsl_diu_enable_panel(struct fb_info *info)
                }
                break;
        case PLANE1_AOI1:
-               pmfbi = machine_data->fsl_diu_info[1]->par;
+               pmfbi = &data->mfb[1];
                ad->next_ad = 0;
-               if (hw->desc[1] == machine_data->dummy_ad->paddr)
+               if (hw->desc[1] == data->dummy_ad.paddr)
                        wr_reg_wa(&hw->desc[1], ad->paddr);
                else                                    /* AOI0 open */
                        pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
                break;
        case PLANE2_AOI1:
-               pmfbi = machine_data->fsl_diu_info[3]->par;
+               pmfbi = &data->mfb[3];
                ad->next_ad = 0;
-               if (hw->desc[2] == machine_data->dummy_ad->paddr)
+               if (hw->desc[2] == data->dummy_ad.paddr)
                        wr_reg_wa(&hw->desc[2], ad->paddr);
                else                            /* AOI0 was open */
                        pmfbi->ad->next_ad = cpu_to_le32(ad->paddr);
@@ -548,52 +529,52 @@ static void fsl_diu_disable_panel(struct fb_info *info)
 {
        struct mfb_info *pmfbi, *cmfbi, *mfbi = info->par;
        struct diu_ad *ad = mfbi->ad;
-       struct fsl_diu_data *machine_data = mfbi->parent;
-       struct diu __iomem *hw = machine_data->diu_reg;
+       struct fsl_diu_data *data = mfbi->parent;
+       struct diu __iomem *hw = data->diu_reg;
 
        switch (mfbi->index) {
        case PLANE0:
-               if (hw->desc[0] != machine_data->dummy_ad->paddr)
-                       wr_reg_wa(&hw->desc[0], machine_data->dummy_ad->paddr);
+               if (hw->desc[0] != data->dummy_ad.paddr)
+                       wr_reg_wa(&hw->desc[0], data->dummy_ad.paddr);
                break;
        case PLANE1_AOI0:
-               cmfbi = machine_data->fsl_diu_info[2]->par;
+               cmfbi = &data->mfb[2];
                if (cmfbi->count > 0)   /* AOI1 is open */
                        wr_reg_wa(&hw->desc[1], cmfbi->ad->paddr);
                                        /* move AOI1 to the first */
                else                    /* AOI1 was closed */
-                       wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr);
+                       wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);
                                        /* close AOI 0 */
                break;
        case PLANE2_AOI0:
-               cmfbi = machine_data->fsl_diu_info[4]->par;
+               cmfbi = &data->mfb[4];
                if (cmfbi->count > 0)   /* AOI1 is open */
                        wr_reg_wa(&hw->desc[2], cmfbi->ad->paddr);
                                        /* move AOI1 to the first */
                else                    /* AOI1 was closed */
-                       wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr);
+                       wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);
                                        /* close AOI 0 */
                break;
        case PLANE1_AOI1:
-               pmfbi = machine_data->fsl_diu_info[1]->par;
+               pmfbi = &data->mfb[1];
                if (hw->desc[1] != ad->paddr) {
                                /* AOI1 is not the first in the chain */
                        if (pmfbi->count > 0)
                                        /* AOI0 is open, must be the first */
                                pmfbi->ad->next_ad = 0;
                } else                  /* AOI1 is the first in the chain */
-                       wr_reg_wa(&hw->desc[1], machine_data->dummy_ad->paddr);
+                       wr_reg_wa(&hw->desc[1], data->dummy_ad.paddr);
                                        /* close AOI 1 */
                break;
        case PLANE2_AOI1:
-               pmfbi = machine_data->fsl_diu_info[3]->par;
+               pmfbi = &data->mfb[3];
                if (hw->desc[2] != ad->paddr) {
                                /* AOI1 is not the first in the chain */
                        if (pmfbi->count > 0)
                                /* AOI0 is open, must be the first */
                                pmfbi->ad->next_ad = 0;
                } else          /* AOI1 is the first in the chain */
-                       wr_reg_wa(&hw->desc[2], machine_data->dummy_ad->paddr);
+                       wr_reg_wa(&hw->desc[2], data->dummy_ad.paddr);
                                /* close AOI 1 */
                break;
        }
@@ -602,39 +583,33 @@ static void fsl_diu_disable_panel(struct fb_info *info)
 static void enable_lcdc(struct fb_info *info)
 {
        struct mfb_info *mfbi = info->par;
-       struct fsl_diu_data *machine_data = mfbi->parent;
-       struct diu __iomem *hw = machine_data->diu_reg;
+       struct fsl_diu_data *data = mfbi->parent;
+       struct diu __iomem *hw = data->diu_reg;
 
-       if (!machine_data->fb_enabled) {
-               out_be32(&hw->diu_mode, MFB_MODE1);
-               machine_data->fb_enabled++;
-       }
+       out_be32(&hw->diu_mode, MFB_MODE1);
 }
 
 static void disable_lcdc(struct fb_info *info)
 {
        struct mfb_info *mfbi = info->par;
-       struct fsl_diu_data *machine_data = mfbi->parent;
-       struct diu __iomem *hw = machine_data->diu_reg;
+       struct fsl_diu_data *data = mfbi->parent;
+       struct diu __iomem *hw = data->diu_reg;
 
-       if (machine_data->fb_enabled) {
-               out_be32(&hw->diu_mode, 0);
-               machine_data->fb_enabled = 0;
-       }
+       out_be32(&hw->diu_mode, 0);
 }
 
 static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
                                struct fb_info *info)
 {
        struct mfb_info *lower_aoi_mfbi, *upper_aoi_mfbi, *mfbi = info->par;
-       struct fsl_diu_data *machine_data = mfbi->parent;
+       struct fsl_diu_data *data = mfbi->parent;
        int available_height, upper_aoi_bottom;
        enum mfb_index index = mfbi->index;
        int lower_aoi_is_open, upper_aoi_is_open;
        __u32 base_plane_width, base_plane_height, upper_aoi_height;
 
-       base_plane_width = machine_data->fsl_diu_info[0]->var.xres;
-       base_plane_height = machine_data->fsl_diu_info[0]->var.yres;
+       base_plane_width = data->fsl_diu_info[0].var.xres;
+       base_plane_height = data->fsl_diu_info[0].var.yres;
 
        if (mfbi->x_aoi_d < 0)
                mfbi->x_aoi_d = 0;
@@ -649,7 +624,7 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
                break;
        case PLANE1_AOI0:
        case PLANE2_AOI0:
-               lower_aoi_mfbi = machine_data->fsl_diu_info[index+1]->par;
+               lower_aoi_mfbi = data->fsl_diu_info[index+1].par;
                lower_aoi_is_open = lower_aoi_mfbi->count > 0 ? 1 : 0;
                if (var->xres > base_plane_width)
                        var->xres = base_plane_width;
@@ -667,9 +642,8 @@ static void adjust_aoi_size_position(struct fb_var_screeninfo *var,
                break;
        case PLANE1_AOI1:
        case PLANE2_AOI1:
-               upper_aoi_mfbi = machine_data->fsl_diu_info[index-1]->par;
-               upper_aoi_height =
-                               machine_data->fsl_diu_info[index-1]->var.yres;
+               upper_aoi_mfbi = data->fsl_diu_info[index-1].par;
+               upper_aoi_height = data->fsl_diu_info[index-1].var.yres;
                upper_aoi_bottom = upper_aoi_mfbi->y_aoi_d + upper_aoi_height;
                upper_aoi_is_open = upper_aoi_mfbi->count > 0 ? 1 : 0;
                if (var->xres > base_plane_width)
@@ -809,33 +783,33 @@ static void update_lcdc(struct fb_info *info)
 {
        struct fb_var_screeninfo *var = &info->var;
        struct mfb_info *mfbi = info->par;
-       struct fsl_diu_data *machine_data = mfbi->parent;
+       struct fsl_diu_data *data = mfbi->parent;
        struct diu __iomem *hw;
        int i, j;
-       char __iomem *cursor_base, *gamma_table_base;
+       u8 *gamma_table_base;
 
        u32 temp;
 
-       hw = machine_data->diu_reg;
+       hw = data->diu_reg;
+
+       diu_ops.set_monitor_port(data->monitor_port);
+       gamma_table_base = data->gamma;
 
-       diu_ops.set_monitor_port(machine_data->monitor_port);
-       gamma_table_base = machine_data->gamma.vaddr;
-       cursor_base = machine_data->cursor.vaddr;
        /* Prep for DIU init  - gamma table, cursor table */
 
        for (i = 0; i <= 2; i++)
                for (j = 0; j <= 255; j++)
                        *gamma_table_base++ = j;
 
-       diu_ops.set_gamma_table(machine_data->monitor_port,
-                               machine_data->gamma.vaddr);
+       if (diu_ops.set_gamma_table)
+               diu_ops.set_gamma_table(data->monitor_port, data->gamma);
 
        disable_lcdc(info);
 
        /* Program DIU registers */
 
-       out_be32(&hw->gamma, machine_data->gamma.paddr);
-       out_be32(&hw->cursor, machine_data->cursor.paddr);
+       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 */
@@ -870,16 +844,17 @@ static void update_lcdc(struct fb_info *info)
 
 static int map_video_memory(struct fb_info *info)
 {
-       phys_addr_t phys;
        u32 smem_len = info->fix.line_length * info->var.yres_virtual;
+       void *p;
 
-       info->screen_base = fsl_diu_alloc(smem_len, &phys);
-       if (info->screen_base == NULL) {
+       p = alloc_pages_exact(smem_len, GFP_DMA | __GFP_ZERO);
+       if (!p) {
                dev_err(info->dev, "unable to allocate fb memory\n");
                return -ENOMEM;
        }
        mutex_lock(&info->mm_lock);
-       info->fix.smem_start = (unsigned long) phys;
+       info->screen_base = p;
+       info->fix.smem_start = virt_to_phys(info->screen_base);
        info->fix.smem_len = smem_len;
        mutex_unlock(&info->mm_lock);
        info->screen_size = info->fix.smem_len;
@@ -889,12 +864,17 @@ static int map_video_memory(struct fb_info *info)
 
 static void unmap_video_memory(struct fb_info *info)
 {
-       fsl_diu_free(info->screen_base, info->fix.smem_len);
+       void *p = info->screen_base;
+       size_t l = info->fix.smem_len;
+
        mutex_lock(&info->mm_lock);
        info->screen_base = NULL;
        info->fix.smem_start = 0;
        info->fix.smem_len = 0;
        mutex_unlock(&info->mm_lock);
+
+       if (p)
+               free_pages_exact(p, l);
 }
 
 /*
@@ -913,6 +893,59 @@ static int fsl_diu_set_aoi(struct fb_info *info)
        return 0;
 }
 
+/**
+ * fsl_diu_get_pixel_format: return the pixel format for a given color depth
+ *
+ * The pixel format is a 32-bit value that determine which bits in each
+ * pixel are to be used for each color.  This is the default function used
+ * if the platform does not define its own version.
+ */
+static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel)
+{
+#define PF_BYTE_F              0x10000000
+#define PF_ALPHA_C_MASK                0x0E000000
+#define PF_ALPHA_C_SHIFT       25
+#define PF_BLUE_C_MASK         0x01800000
+#define PF_BLUE_C_SHIFT                23
+#define PF_GREEN_C_MASK                0x00600000
+#define PF_GREEN_C_SHIFT       21
+#define PF_RED_C_MASK          0x00180000
+#define PF_RED_C_SHIFT         19
+#define PF_PALETTE             0x00040000
+#define PF_PIXEL_S_MASK                0x00030000
+#define PF_PIXEL_S_SHIFT       16
+#define PF_COMP_3_MASK         0x0000F000
+#define PF_COMP_3_SHIFT                12
+#define PF_COMP_2_MASK         0x00000F00
+#define PF_COMP_2_SHIFT                8
+#define PF_COMP_1_MASK         0x000000F0
+#define PF_COMP_1_SHIFT                4
+#define PF_COMP_0_MASK         0x0000000F
+#define PF_COMP_0_SHIFT                0
+
+#define MAKE_PF(alpha, red, blue, green, size, c0, c1, c2, c3) \
+       cpu_to_le32(PF_BYTE_F | (alpha << PF_ALPHA_C_SHIFT) | \
+       (blue << PF_BLUE_C_SHIFT) | (green << PF_GREEN_C_SHIFT) | \
+       (red << PF_RED_C_SHIFT) | (c3 << PF_COMP_3_SHIFT) | \
+       (c2 << PF_COMP_2_SHIFT) | (c1 << PF_COMP_1_SHIFT) | \
+       (c0 << PF_COMP_0_SHIFT) | (size << PF_PIXEL_S_SHIFT))
+
+       switch (bits_per_pixel) {
+       case 32:
+               /* 0x88883316 */
+               return MAKE_PF(3, 2, 0, 1, 3, 8, 8, 8, 8);
+       case 24:
+               /* 0x88082219 */
+               return MAKE_PF(4, 0, 1, 2, 2, 0, 8, 8, 8);
+       case 16:
+               /* 0x65053118 */
+               return MAKE_PF(4, 2, 1, 0, 1, 5, 6, 5, 0);
+       default:
+               pr_err("fsl-diu: unsupported color depth %u\n", bits_per_pixel);
+               return 0;
+       }
+}
+
 /*
  * Using the fb_var_screeninfo in fb_info we set the resolution of this
  * particular framebuffer. This function alters the fb_fix_screeninfo stored
@@ -926,11 +959,11 @@ static int fsl_diu_set_par(struct fb_info *info)
        unsigned long len;
        struct fb_var_screeninfo *var = &info->var;
        struct mfb_info *mfbi = info->par;
-       struct fsl_diu_data *machine_data = mfbi->parent;
+       struct fsl_diu_data *data = mfbi->parent;
        struct diu_ad *ad = mfbi->ad;
        struct diu __iomem *hw;
 
-       hw = machine_data->diu_reg;
+       hw = data->diu_reg;
 
        set_fix(info);
        mfbi->cursor_reset = 1;
@@ -948,8 +981,12 @@ static int fsl_diu_set_par(struct fb_info *info)
                }
        }
 
-       ad->pix_fmt = diu_ops.get_pixel_format(machine_data->monitor_port,
-                                              var->bits_per_pixel);
+       if (diu_ops.get_pixel_format)
+               ad->pix_fmt = diu_ops.get_pixel_format(data->monitor_port,
+                                                      var->bits_per_pixel);
+       else
+               ad->pix_fmt = fsl_diu_get_pixel_format(var->bits_per_pixel);
+
        ad->addr    = cpu_to_le32(info->fix.smem_start);
        ad->src_size_g_alpha = cpu_to_le32((var->yres_virtual << 12) |
                                var->xres_virtual) | mfbi->g_alpha;
@@ -1208,21 +1245,6 @@ static struct fb_ops fsl_diu_ops = {
        .fb_release = fsl_diu_release,
 };
 
-static int init_fbinfo(struct fb_info *info)
-{
-       struct mfb_info *mfbi = info->par;
-
-       info->device = NULL;
-       info->var.activate = FB_ACTIVATE_NOW;
-       info->fbops = &fsl_diu_ops;
-       info->flags = FBINFO_FLAG_DEFAULT;
-       info->pseudo_palette = &mfbi->pseudo_palette;
-
-       /* Allocate colormap */
-       fb_alloc_cmap(&info->cmap, 16, 0);
-       return 0;
-}
-
 static int __devinit install_fb(struct fb_info *info)
 {
        int rc;
@@ -1232,8 +1254,15 @@ static int __devinit install_fb(struct fb_info *info)
        unsigned int dbsize = ARRAY_SIZE(fsl_diu_mode_db);
        int has_default_mode = 1;
 
-       if (init_fbinfo(info))
-               return -EINVAL;
+       info->var.activate = FB_ACTIVATE_NOW;
+       info->fbops = &fsl_diu_ops;
+       info->flags = FBINFO_DEFAULT | FBINFO_VIRTFB | FBINFO_PARTIAL_PAN_OK |
+               FBINFO_READS_FAST;
+       info->pseudo_palette = mfbi->pseudo_palette;
+
+       rc = fb_alloc_cmap(&info->cmap, 16, 0);
+       if (rc)
+               return rc;
 
        if (mfbi->index == PLANE0) {
                if (mfbi->edid_data) {
@@ -1359,16 +1388,16 @@ static irqreturn_t fsl_diu_isr(int irq, void *dev_id)
        return IRQ_NONE;
 }
 
-static int request_irq_local(struct fsl_diu_data *machine_data)
+static int request_irq_local(struct fsl_diu_data *data)
 {
-       struct diu __iomem *hw = machine_data->diu_reg;
+       struct diu __iomem *hw = data->diu_reg;
        u32 ints;
        int ret;
 
        /* Read to clear the status */
        in_be32(&hw->int_status);
 
-       ret = request_irq(machine_data->irq, fsl_diu_isr, 0, "fsl-diu-fb", hw);
+       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)
@@ -1383,14 +1412,14 @@ static int request_irq_local(struct fsl_diu_data *machine_data)
        return ret;
 }
 
-static void free_irq_local(struct fsl_diu_data *machine_data)
+static void free_irq_local(struct fsl_diu_data *data)
 {
-       struct diu __iomem *hw = machine_data->diu_reg;
+       struct diu __iomem *hw = data->diu_reg;
 
        /* Disable all LCDC interrupt */
        out_be32(&hw->int_mask, 0x1f);
 
-       free_irq(machine_data->irq, NULL);
+       free_irq(data->irq, NULL);
 }
 
 #ifdef CONFIG_PM
@@ -1400,20 +1429,20 @@ static void free_irq_local(struct fsl_diu_data *machine_data)
  */
 static int fsl_diu_suspend(struct platform_device *ofdev, pm_message_t state)
 {
-       struct fsl_diu_data *machine_data;
+       struct fsl_diu_data *data;
 
-       machine_data = dev_get_drvdata(&ofdev->dev);
-       disable_lcdc(machine_data->fsl_diu_info[0]);
+       data = dev_get_drvdata(&ofdev->dev);
+       disable_lcdc(data->fsl_diu_info[0]);
 
        return 0;
 }
 
 static int fsl_diu_resume(struct platform_device *ofdev)
 {
-       struct fsl_diu_data *machine_data;
+       struct fsl_diu_data *data;
 
-       machine_data = dev_get_drvdata(&ofdev->dev);
-       enable_lcdc(machine_data->fsl_diu_info[0]);
+       data = dev_get_drvdata(&ofdev->dev);
+       enable_lcdc(data->fsl_diu_info[0]);
 
        return 0;
 }
@@ -1423,56 +1452,24 @@ static int fsl_diu_resume(struct platform_device *ofdev)
 #define fsl_diu_resume NULL
 #endif                         /* CONFIG_PM */
 
-/* Align to 64-bit(8-byte), 32-byte, etc. */
-static int allocate_buf(struct device *dev, struct diu_addr *buf, u32 size,
-                       u32 bytes_align)
-{
-       u32 offset;
-       dma_addr_t mask;
-
-       buf->vaddr =
-               dma_alloc_coherent(dev, size + bytes_align, &buf->paddr,
-                                  GFP_DMA | __GFP_ZERO);
-       if (!buf->vaddr)
-               return -ENOMEM;
-
-       mask = bytes_align - 1;
-       offset = buf->paddr & mask;
-       if (offset) {
-               buf->offset = bytes_align - offset;
-               buf->paddr = buf->paddr + offset;
-       } else
-               buf->offset = 0;
-
-       return 0;
-}
-
-static void free_buf(struct device *dev, struct diu_addr *buf, u32 size,
-                    u32 bytes_align)
-{
-       dma_free_coherent(dev, size + bytes_align, buf->vaddr,
-                         buf->paddr - buf->offset);
-}
-
 static ssize_t store_monitor(struct device *device,
        struct device_attribute *attr, const char *buf, size_t count)
 {
        enum fsl_diu_monitor_port old_monitor_port;
-       struct fsl_diu_data *machine_data =
+       struct fsl_diu_data *data =
                container_of(attr, struct fsl_diu_data, dev_attr);
 
-       old_monitor_port = machine_data->monitor_port;
-       machine_data->monitor_port = fsl_diu_name_to_port(buf);
+       old_monitor_port = data->monitor_port;
+       data->monitor_port = fsl_diu_name_to_port(buf);
 
-       if (old_monitor_port != machine_data->monitor_port) {
+       if (old_monitor_port != data->monitor_port) {
                /* All AOIs need adjust pixel format
                 * fsl_diu_set_par only change the pixsel format here
                 * unlikely to fail. */
-               fsl_diu_set_par(machine_data->fsl_diu_info[0]);
-               fsl_diu_set_par(machine_data->fsl_diu_info[1]);
-               fsl_diu_set_par(machine_data->fsl_diu_info[2]);
-               fsl_diu_set_par(machine_data->fsl_diu_info[3]);
-               fsl_diu_set_par(machine_data->fsl_diu_info[4]);
+               unsigned int i;
+
+               for (i=0; i < NUM_AOIS; i++)
+                       fsl_diu_set_par(&data->fsl_diu_info[i]);
        }
        return count;
 }
@@ -1480,10 +1477,10 @@ static ssize_t store_monitor(struct device *device,
 static ssize_t show_monitor(struct device *device,
        struct device_attribute *attr, char *buf)
 {
-       struct fsl_diu_data *machine_data =
+       struct fsl_diu_data *data =
                container_of(attr, struct fsl_diu_data, dev_attr);
 
-       switch (machine_data->monitor_port) {
+       switch (data->monitor_port) {
        case FSL_DIU_PORT_DVI:
                return sprintf(buf, "DVI\n");
        case FSL_DIU_PORT_LVDS:
@@ -1499,28 +1496,52 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
        struct mfb_info *mfbi;
-       phys_addr_t dummy_ad_addr = 0;
-       int ret, i, error = 0;
-       struct fsl_diu_data *machine_data;
+       struct fsl_diu_data *data;
        int diu_mode;
+       dma_addr_t dma_addr; /* DMA addr of fsl_diu_data struct */
+       unsigned int i;
+       int ret;
 
-       machine_data = kzalloc(sizeof(struct fsl_diu_data), GFP_KERNEL);
-       if (!machine_data)
+       data = dma_alloc_coherent(&pdev->dev, sizeof(struct fsl_diu_data),
+                                 &dma_addr, GFP_DMA | __GFP_ZERO);
+       if (!data)
                return -ENOMEM;
+       data->dma_addr = dma_addr;
+
+       /*
+        * dma_alloc_coherent() uses a page allocator, so the address is
+        * always page-aligned.  We need the memory to be 32-byte aligned,
+        * so that's good.  However, if one day the allocator changes, we
+        * need to catch that.  It's not worth the effort to handle unaligned
+        * alloctions now because it's highly unlikely to ever be a problem.
+        */
+       if ((unsigned long)data & 31) {
+               dev_err(&pdev->dev, "misaligned allocation");
+               ret = -ENOMEM;
+               goto error;
+       }
 
-       spin_lock_init(&machine_data->reg_lock);
+       spin_lock_init(&data->reg_lock);
 
-       for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
-               machine_data->fsl_diu_info[i] =
-                       framebuffer_alloc(sizeof(struct mfb_info), &pdev->dev);
-               if (!machine_data->fsl_diu_info[i]) {
-                       dev_err(&pdev->dev, "cannot allocate memory\n");
-                       ret = -ENOMEM;
-                       goto error2;
-               }
-               mfbi = machine_data->fsl_diu_info[i]->par;
+       for (i = 0; i < NUM_AOIS; i++) {
+               struct fb_info *info = &data->fsl_diu_info[i];
+
+               info->device = &pdev->dev;
+               info->par = &data->mfb[i];
+
+               /*
+                * We store the physical address of the AD in the reserved
+                * 'paddr' field of the AD itself.
+                */
+               data->ad[i].paddr = DMA_ADDR(data, ad[i]);
+
+               info->fix.smem_start = 0;
+
+               /* Initialize the AOI data structure */
+               mfbi = info->par;
                memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
-               mfbi->parent = machine_data;
+               mfbi->parent = data;
+               mfbi->ad = &data->ad[i];
 
                if (mfbi->index == PLANE0) {
                        const u8 *prop;
@@ -1534,158 +1555,102 @@ static int __devinit fsl_diu_probe(struct platform_device *pdev)
                }
        }
 
-       machine_data->diu_reg = of_iomap(np, 0);
-       if (!machine_data->diu_reg) {
+       data->diu_reg = of_iomap(np, 0);
+       if (!data->diu_reg) {
                dev_err(&pdev->dev, "cannot map DIU registers\n");
                ret = -EFAULT;
-               goto error2;
+               goto error;
        }
 
-       diu_mode = in_be32(&machine_data->diu_reg->diu_mode);
+       diu_mode = in_be32(&data->diu_reg->diu_mode);
        if (diu_mode == MFB_MODE0)
-               out_be32(&machine_data->diu_reg->diu_mode, 0); /* disable DIU */
+               out_be32(&data->diu_reg->diu_mode, 0); /* disable DIU */
 
        /* Get the IRQ of the DIU */
-       machine_data->irq = irq_of_parse_and_map(np, 0);
+       data->irq = irq_of_parse_and_map(np, 0);
 
-       if (!machine_data->irq) {
+       if (!data->irq) {
                dev_err(&pdev->dev, "could not get DIU IRQ\n");
                ret = -EINVAL;
                goto error;
        }
-       machine_data->monitor_port = monitor_port;
-
-       /* Area descriptor memory pool aligns to 64-bit boundary */
-       if (allocate_buf(&pdev->dev, &machine_data->ad,
-                        sizeof(struct diu_ad) * FSL_AOI_NUM, 8))
-               return -ENOMEM;
-
-       /* Get memory for Gamma Table  - 32-byte aligned memory */
-       if (allocate_buf(&pdev->dev, &machine_data->gamma, 768, 32)) {
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       /* For performance, cursor bitmap buffer aligns to 32-byte boundary */
-       if (allocate_buf(&pdev->dev, &machine_data->cursor,
-                        MAX_CURS * MAX_CURS * 2, 32)) {
-               ret = -ENOMEM;
-               goto error;
-       }
-
-       i = ARRAY_SIZE(machine_data->fsl_diu_info);
-       machine_data->dummy_ad = (struct diu_ad *)((u32)machine_data->ad.vaddr +
-                       machine_data->ad.offset) + i;
-       machine_data->dummy_ad->paddr = machine_data->ad.paddr +
-                       i * sizeof(struct diu_ad);
-       machine_data->dummy_aoi_virt = fsl_diu_alloc(64, &dummy_ad_addr);
-       if (!machine_data->dummy_aoi_virt) {
-               ret = -ENOMEM;
-               goto error;
-       }
-       machine_data->dummy_ad->addr = cpu_to_le32(dummy_ad_addr);
-       machine_data->dummy_ad->pix_fmt = 0x88882317;
-       machine_data->dummy_ad->src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
-       machine_data->dummy_ad->aoi_size = cpu_to_le32((4 << 16) |  2);
-       machine_data->dummy_ad->offset_xyi = 0;
-       machine_data->dummy_ad->offset_xyd = 0;
-       machine_data->dummy_ad->next_ad = 0;
+       data->monitor_port = monitor_port;
+
+       /* Initialize the dummy Area Descriptor */
+       data->dummy_ad.addr = cpu_to_le32(DMA_ADDR(data, dummy_aoi));
+       data->dummy_ad.pix_fmt = 0x88882317;
+       data->dummy_ad.src_size_g_alpha = cpu_to_le32((4 << 12) | 4);
+       data->dummy_ad.aoi_size = cpu_to_le32((4 << 16) |  2);
+       data->dummy_ad.offset_xyi = 0;
+       data->dummy_ad.offset_xyd = 0;
+       data->dummy_ad.next_ad = 0;
+       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.
         */
        if (diu_mode == MFB_MODE0)
-               out_be32(&machine_data->diu_reg->desc[0],
-                        machine_data->dummy_ad->paddr);
-
-       out_be32(&machine_data->diu_reg->desc[1], machine_data->dummy_ad->paddr);
-       out_be32(&machine_data->diu_reg->desc[2], machine_data->dummy_ad->paddr);
-
-       for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++) {
-               machine_data->fsl_diu_info[i]->fix.smem_start = 0;
-               mfbi = machine_data->fsl_diu_info[i]->par;
-               mfbi->ad = (struct diu_ad *)((u32)machine_data->ad.vaddr
-                                       + machine_data->ad.offset) + i;
-               mfbi->ad->paddr =
-                       machine_data->ad.paddr + i * sizeof(struct diu_ad);
-               ret = install_fb(machine_data->fsl_diu_info[i]);
+               out_be32(&data->diu_reg->desc[0], data->dummy_ad.paddr);
+
+       out_be32(&data->diu_reg->desc[1], data->dummy_ad.paddr);
+       out_be32(&data->diu_reg->desc[2], data->dummy_ad.paddr);
+
+       for (i = 0; i < NUM_AOIS; i++) {
+               ret = install_fb(&data->fsl_diu_info[i]);
                if (ret) {
                        dev_err(&pdev->dev, "could not register fb %d\n", i);
                        goto error;
                }
        }
 
-       if (request_irq_local(machine_data)) {
+       if (request_irq_local(data)) {
                dev_err(&pdev->dev, "could not claim irq\n");
                goto error;
        }
 
-       sysfs_attr_init(&machine_data->dev_attr.attr);
-       machine_data->dev_attr.attr.name = "monitor";
-       machine_data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;
-       machine_data->dev_attr.show = show_monitor;
-       machine_data->dev_attr.store = store_monitor;
-       error = device_create_file(machine_data->fsl_diu_info[0]->dev,
-                                 &machine_data->dev_attr);
-       if (error) {
+       sysfs_attr_init(&data->dev_attr.attr);
+       data->dev_attr.attr.name = "monitor";
+       data->dev_attr.attr.mode = S_IRUGO|S_IWUSR;
+       data->dev_attr.show = show_monitor;
+       data->dev_attr.store = store_monitor;
+       ret = device_create_file(&pdev->dev, &data->dev_attr);
+       if (ret) {
                dev_err(&pdev->dev, "could not create sysfs file %s\n",
-                       machine_data->dev_attr.attr.name);
+                       data->dev_attr.attr.name);
        }
 
-       dev_set_drvdata(&pdev->dev, machine_data);
+       dev_set_drvdata(&pdev->dev, data);
        return 0;
 
 error:
-       for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
-               uninstall_fb(machine_data->fsl_diu_info[i]);
-
-       if (machine_data->ad.vaddr)
-               free_buf(&pdev->dev, &machine_data->ad,
-                        sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
-       if (machine_data->gamma.vaddr)
-               free_buf(&pdev->dev, &machine_data->gamma, 768, 32);
-       if (machine_data->cursor.vaddr)
-               free_buf(&pdev->dev, &machine_data->cursor,
-                        MAX_CURS * MAX_CURS * 2, 32);
-       if (machine_data->dummy_aoi_virt)
-               fsl_diu_free(machine_data->dummy_aoi_virt, 64);
-       iounmap(machine_data->diu_reg);
-
-error2:
-       for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
-               if (machine_data->fsl_diu_info[i])
-                       framebuffer_release(machine_data->fsl_diu_info[i]);
-       kfree(machine_data);
+       for (i = 0; i < NUM_AOIS; i++)
+               uninstall_fb(&data->fsl_diu_info[i]);
+
+       iounmap(data->diu_reg);
+
+       dma_free_coherent(&pdev->dev, sizeof(struct fsl_diu_data), data,
+                         data->dma_addr);
 
        return ret;
 }
 
 static int fsl_diu_remove(struct platform_device *pdev)
 {
-       struct fsl_diu_data *machine_data;
+       struct fsl_diu_data *data;
        int i;
 
-       machine_data = dev_get_drvdata(&pdev->dev);
-       disable_lcdc(machine_data->fsl_diu_info[0]);
-       free_irq_local(machine_data);
-       for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
-               uninstall_fb(machine_data->fsl_diu_info[i]);
-       if (machine_data->ad.vaddr)
-               free_buf(&pdev->dev, &machine_data->ad,
-                        sizeof(struct diu_ad) * FSL_AOI_NUM, 8);
-       if (machine_data->gamma.vaddr)
-               free_buf(&pdev->dev, &machine_data->gamma, 768, 32);
-       if (machine_data->cursor.vaddr)
-               free_buf(&pdev->dev, &machine_data->cursor,
-                        MAX_CURS * MAX_CURS * 2, 32);
-       if (machine_data->dummy_aoi_virt)
-               fsl_diu_free(machine_data->dummy_aoi_virt, 64);
-       iounmap(machine_data->diu_reg);
-       for (i = 0; i < ARRAY_SIZE(machine_data->fsl_diu_info); i++)
-               if (machine_data->fsl_diu_info[i])
-                       framebuffer_release(machine_data->fsl_diu_info[i]);
-       kfree(machine_data);
+       data = dev_get_drvdata(&pdev->dev);
+       disable_lcdc(&data->fsl_diu_info[0]);
+       free_irq_local(data);
+
+       for (i = 0; i < NUM_AOIS; i++)
+               uninstall_fb(&data->fsl_diu_info[i]);
+
+       iounmap(data->diu_reg);
+
+       dma_free_coherent(&pdev->dev, sizeof(struct fsl_diu_data), data,
+                         data->dma_addr);
 
        return 0;
 }