[ARM] 5209/1: metronomefb: changes to use platform framebuffer
authorJaya Kumar <jayakumar.lkml@gmail.com>
Tue, 19 Aug 2008 10:17:55 +0000 (11:17 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Tue, 26 Aug 2008 16:01:12 +0000 (17:01 +0100)
These changes are used in order to support the use of the framebuffer
provided by the platform device driver rather than to directly allocate one.
Other changes are cleanup to error handling and order of release.

Signed-off-by: Jaya Kumar <jayakumar.lkml@gmail.com>
Acked-by: Krzysztof Helt <krzysztof.h1@wp.pl>
Acked-by: Eric Miao <eric.miao@marvell.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
drivers/video/Kconfig
drivers/video/metronomefb.c
include/video/metronomefb.h

index 70d135e..6fd4a2f 100644 (file)
@@ -172,11 +172,6 @@ config FB_DEFERRED_IO
        bool
        depends on FB
 
-config FB_METRONOME
-       tristate
-       depends on FB
-       depends on FB_DEFERRED_IO
-
 config FB_HECUBA
        tristate
        depends on FB
@@ -2041,6 +2036,19 @@ config XEN_FBDEV_FRONTEND
          frame buffer driver.  It communicates with a back-end
          in another domain.
 
+config FB_METRONOME
+       tristate "E-Ink Metronome/8track controller support"
+       depends on FB
+       select FB_SYS_FILLRECT
+       select FB_SYS_COPYAREA
+       select FB_SYS_IMAGEBLIT
+       select FB_SYS_FOPS
+       select FB_DEFERRED_IO
+       help
+         This driver implements support for the E-Ink Metronome
+         controller. The pre-release name for this device was 8track
+         and could also have been called by some vendors as PVI-nnnn.
+
 source "drivers/video/omap/Kconfig"
 
 source "drivers/video/backlight/Kconfig"
index 7cda4f8..afeed06 100644 (file)
 #define DPY_W 832
 #define DPY_H 622
 
+static int user_wfm_size;
+
 /* frame differs from image. frame includes non-visible pixels */
 struct epd_frame {
        int fw; /* frame width */
        int fh; /* frame height */
+       u16 config[4];
+       int wfm_size;
 };
 
 static struct epd_frame epd_frame_table[] = {
        {
-       .fw = 832,
-       .fh = 622
+               .fw = 832,
+               .fh = 622,
+               .config = {
+                       15 /* sdlew */
+                       | 2 << 8 /* sdosz */
+                       | 0 << 11 /* sdor */
+                       | 0 << 12 /* sdces */
+                       | 0 << 15, /* sdcer */
+                       42 /* gdspl */
+                       | 1 << 8 /* gdr1 */
+                       | 1 << 9 /* sdshr */
+                       | 0 << 15, /* gdspp */
+                       18 /* gdspw */
+                       | 0 << 15, /* dispc */
+                       599 /* vdlc */
+                       | 0 << 11 /* dsi */
+                       | 0 << 12, /* dsic */
+               },
+               .wfm_size = 47001,
+       },
+       {
+               .fw = 1088,
+               .fh = 791,
+               .config = {
+                       0x0104,
+                       0x031f,
+                       0x0088,
+                       0x02ff,
+               },
+               .wfm_size = 46770,
+       },
+       {
+               .fw = 1200,
+               .fh = 842,
+               .config = {
+                       0x0101,
+                       0x030e,
+                       0x0012,
+                       0x0280,
+               },
+               .wfm_size = 46770,
        },
 };
 
@@ -125,7 +168,6 @@ static u16 calc_img_cksum(u16 *start, int length)
 }
 
 /* here we decode the incoming waveform file and populate metromem */
-#define EXP_WFORM_SIZE 47001
 static int __devinit load_waveform(u8 *mem, size_t size, int m, int t,
                                struct metronomefb_par *par)
 {
@@ -142,9 +184,12 @@ static int __devinit load_waveform(u8 *mem, size_t size, int m, int t,
        u8 *metromem = par->metromem_wfm;
        struct device *dev = par->info->dev;
 
-       if (size != EXP_WFORM_SIZE) {
+       if (user_wfm_size)
+               epd_frame_table[par->dt].wfm_size = user_wfm_size;
+
+       if (size != epd_frame_table[par->dt].wfm_size) {
                dev_err(dev, "Error: unexpected size %d != %d\n", size,
-                                       EXP_WFORM_SIZE);
+                                       epd_frame_table[par->dt].wfm_size);
                return -EINVAL;
        }
 
@@ -267,15 +312,12 @@ static int metronome_display_cmd(struct metronomefb_par *par)
        u16 cs;
        u16 opcode;
        static u8 borderval;
-       u8 *ptr;
 
        /* setup display command
        we can't immediately set the opcode since the controller
        will try parse the command before we've set it all up
        so we just set cs here and set the opcode at the end */
 
-       ptr = par->metromem;
-
        if (par->metromem_cmd->opcode == 0xCC40)
                opcode = cs = 0xCC41;
        else
@@ -328,44 +370,17 @@ static int __devinit metronome_powerup_cmd(struct metronomefb_par *par)
 
 static int __devinit metronome_config_cmd(struct metronomefb_par *par)
 {
-       int i;
-       u16 cs;
-
        /* setup config command
        we can't immediately set the opcode since the controller
-       will try parse the command before we've set it all up
-       so we just set cs here and set the opcode at the end */
-
-       cs = 0xCC10;
-
-       /* set the 12 args ( 8 bytes ) for config. see spec for meanings */
-       i = 0;
-       par->metromem_cmd->args[i] =    15 /* sdlew */
-                                       | 2 << 8 /* sdosz */
-                                       | 0 << 11 /* sdor */
-                                       | 0 << 12 /* sdces */
-                                       | 0 << 15; /* sdcer */
-       cs += par->metromem_cmd->args[i++];
-
-       par->metromem_cmd->args[i] =    42 /* gdspl */
-                                       | 1 << 8 /* gdr1 */
-                                       | 1 << 9 /* sdshr */
-                                       | 0 << 15; /* gdspp */
-       cs += par->metromem_cmd->args[i++];
-
-       par->metromem_cmd->args[i] =    18 /* gdspw */
-                                       | 0 << 15; /* dispc */
-       cs += par->metromem_cmd->args[i++];
-
-       par->metromem_cmd->args[i] =    599 /* vdlc */
-                                       | 0 << 11 /* dsi */
-                                       | 0 << 12; /* dsic */
-       cs += par->metromem_cmd->args[i++];
+       will try parse the command before we've set it all up */
 
+       memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
+               sizeof(epd_frame_table[par->dt].config));
        /* the rest are 0 */
-       memset((u8 *) (par->metromem_cmd->args + i), 0, (32-i)*2);
+       memset((u8 *) (par->metromem_cmd->args + 4), 0, (32-4)*2);
 
-       par->metromem_cmd->csum = cs;
+       par->metromem_cmd->csum = 0xCC10;
+       par->metromem_cmd->csum += calc_img_cksum(par->metromem_cmd->args, 4);
        par->metromem_cmd->opcode = 0xCC10; /* config cmd */
 
        return par->board->met_wait_event(par);
@@ -401,12 +416,9 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par)
 {
        int res;
 
-       par->board->init_gpio_regs(par);
-
-       par->board->init_lcdc_regs(par);
-
-       /* now that lcd is setup, setup dma descriptor */
-       par->board->post_dma_setup(par);
+       res = par->board->setup_io(par);
+       if (res)
+               return res;
 
        res = metronome_powerup_cmd(par);
        if (res)
@@ -423,16 +435,16 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par)
 
 static void metronomefb_dpy_update(struct metronomefb_par *par)
 {
+       int fbsize;
        u16 cksum;
        unsigned char *buf = (unsigned char __force *)par->info->screen_base;
 
+       fbsize = par->info->fix.smem_len;
        /* copy from vm to metromem */
-       memcpy(par->metromem_img, buf, DPY_W*DPY_H);
+       memcpy(par->metromem_img, buf, fbsize);
 
-       cksum = calc_img_cksum((u16 *) par->metromem_img,
-                               (epd_frame_table[0].fw * DPY_H)/2);
-       *((u16 *)(par->metromem_img) +
-                       (epd_frame_table[0].fw * DPY_H)/2) = cksum;
+       cksum = calc_img_cksum((u16 *) par->metromem_img, fbsize/2);
+       *((u16 *)(par->metromem_img) + fbsize/2) = cksum;
        metronome_display_cmd(par);
 }
 
@@ -567,8 +579,10 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
        unsigned char *videomemory;
        struct metronomefb_par *par;
        const struct firmware *fw_entry;
-       int cmd_size, wfm_size, img_size, padding_size, totalsize;
        int i;
+       int panel_type;
+       int fw, fh;
+       int epd_dt_index;
 
        /* pick up board specific routines */
        board = dev->dev.platform_data;
@@ -579,76 +593,88 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
        if (!try_module_get(board->owner))
                return -ENODEV;
 
+       info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
+       if (!info)
+               goto err;
+
        /* we have two blocks of memory.
        info->screen_base which is vm, and is the fb used by apps.
        par->metromem which is physically contiguous memory and
        contains the display controller commands, waveform,
        processed image data and padding. this is the data pulled
-       by the device's LCD controller and pushed to Metronome */
+       by the device's LCD controller and pushed to Metronome.
+       the metromem memory is allocated by the board driver and
+       is provided to us */
+
+       panel_type = board->get_panel_type();
+       switch (panel_type) {
+       case 6:
+               epd_dt_index = 0;
+               break;
+       case 8:
+               epd_dt_index = 1;
+               break;
+       case 97:
+               epd_dt_index = 2;
+               break;
+       default:
+               dev_err(&dev->dev, "Unexpected panel type. Defaulting to 6\n");
+               epd_dt_index = 0;
+               break;
+       }
 
-       videomemorysize = (DPY_W*DPY_H);
+       fw = epd_frame_table[epd_dt_index].fw;
+       fh = epd_frame_table[epd_dt_index].fh;
+
+       /* we need to add a spare page because our csum caching scheme walks
+        * to the end of the page */
+       videomemorysize = PAGE_SIZE + (fw * fh);
        videomemory = vmalloc(videomemorysize);
        if (!videomemory)
-               return -ENOMEM;
+               goto err_fb_rel;
 
        memset(videomemory, 0, videomemorysize);
 
-       info = framebuffer_alloc(sizeof(struct metronomefb_par), &dev->dev);
-       if (!info)
-               goto err_vfree;
-
        info->screen_base = (char __force __iomem *)videomemory;
        info->fbops = &metronomefb_ops;
 
+       metronomefb_fix.line_length = fw;
+       metronomefb_var.xres = fw;
+       metronomefb_var.yres = fh;
+       metronomefb_var.xres_virtual = fw;
+       metronomefb_var.yres_virtual = fh;
        info->var = metronomefb_var;
        info->fix = metronomefb_fix;
        info->fix.smem_len = videomemorysize;
        par = info->par;
        par->info = info;
        par->board = board;
+       par->dt = epd_dt_index;
        init_waitqueue_head(&par->waitq);
 
        /* this table caches per page csum values. */
        par->csum_table = vmalloc(videomemorysize/PAGE_SIZE);
        if (!par->csum_table)
+               goto err_vfree;
+
+       /* the physical framebuffer that we use is setup by
+        * the platform device driver. It will provide us
+        * with cmd, wfm and image memory in a contiguous area. */
+       retval = board->setup_fb(par);
+       if (retval) {
+               dev_err(&dev->dev, "Failed to setup fb\n");
                goto err_csum_table;
+       }
 
-       /* the metromem buffer is divided as follows:
-       command | CRC | padding
-       16kb waveform data | CRC | padding
-       image data | CRC
-       and an extra 256 bytes for dma descriptors
-       eg: IW=832 IH=622 WS=128
-       */
-
-       cmd_size = 1 * epd_frame_table[0].fw;
-       wfm_size = ((16*1024 + 2 + epd_frame_table[0].fw - 1)
-                       / epd_frame_table[0].fw) * epd_frame_table[0].fw;
-       img_size = epd_frame_table[0].fh * epd_frame_table[0].fw;
-       padding_size = 4 * epd_frame_table[0].fw;
-       totalsize = cmd_size + wfm_size + img_size + padding_size;
-       par->metromemsize = PAGE_ALIGN(totalsize + 256);
-       DPRINTK("desired memory size = %d\n", par->metromemsize);
-       dev->dev.coherent_dma_mask = 0xffffffffull;
-       par->metromem = dma_alloc_writecombine(&dev->dev, par->metromemsize,
-                                               &par->metromem_dma, GFP_KERNEL);
-       if (!par->metromem) {
-               printk(KERN_ERR
-                       "metronomefb: unable to allocate dma buffer\n");
-               goto err_vfree;
+       /* after this point we should have a framebuffer */
+       if ((!par->metromem_wfm) ||  (!par->metromem_img) ||
+               (!par->metromem_dma)) {
+               dev_err(&dev->dev, "fb access failure\n");
+               retval = -EINVAL;
+               goto err_csum_table;
        }
 
        info->fix.smem_start = par->metromem_dma;
-       par->metromem_cmd = (struct metromem_cmd *) par->metromem;
-       par->metromem_wfm = par->metromem + cmd_size;
-       par->metromem_img = par->metromem + cmd_size + wfm_size;
-       par->metromem_img_csum = (u16 *) (par->metromem_img +
-                                       (epd_frame_table[0].fw * DPY_H));
-       DPRINTK("img offset=0x%x\n", cmd_size + wfm_size);
-       par->metromem_desc = (struct metromem_desc *) (par->metromem + cmd_size
-                                       + wfm_size + img_size + padding_size);
-       par->metromem_desc_dma = par->metromem_dma + cmd_size + wfm_size
-                                + img_size + padding_size;
 
        /* load the waveform in. assume mode 3, temp 31 for now
                a) request the waveform file from userspace
@@ -656,7 +682,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
        retval = request_firmware(&fw_entry, "metronome.wbf", &dev->dev);
        if (retval < 0) {
                dev_err(&dev->dev, "Failed to get waveform\n");
-               goto err_dma_free;
+               goto err_csum_table;
        }
 
        retval = load_waveform((u8 *) fw_entry->data, fw_entry->size, 3, 31,
@@ -664,11 +690,11 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
        release_firmware(fw_entry);
        if (retval < 0) {
                dev_err(&dev->dev, "Failed processing waveform\n");
-               goto err_dma_free;
+               goto err_csum_table;
        }
 
        if (board->setup_irq(info))
-               goto err_dma_free;
+               goto err_csum_table;
 
        retval = metronome_init_regs(par);
        if (retval < 0)
@@ -682,7 +708,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
        retval = fb_alloc_cmap(&info->cmap, 8, 0);
        if (retval < 0) {
                dev_err(&dev->dev, "Failed to allocate colormap\n");
-               goto err_fb_rel;
+               goto err_free_irq;
        }
 
        /* set cmap */
@@ -705,17 +731,15 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
 
 err_cmap:
        fb_dealloc_cmap(&info->cmap);
-err_fb_rel:
-       framebuffer_release(info);
 err_free_irq:
-       board->free_irq(info);
-err_dma_free:
-       dma_free_writecombine(&dev->dev, par->metromemsize, par->metromem,
-                               par->metromem_dma);
+       board->cleanup(par);
 err_csum_table:
        vfree(par->csum_table);
 err_vfree:
        vfree(videomemory);
+err_fb_rel:
+       framebuffer_release(info);
+err:
        module_put(board->owner);
        return retval;
 }
@@ -726,15 +750,15 @@ static int __devexit metronomefb_remove(struct platform_device *dev)
 
        if (info) {
                struct metronomefb_par *par = info->par;
+
+               unregister_framebuffer(info);
                fb_deferred_io_cleanup(info);
-               dma_free_writecombine(&dev->dev, par->metromemsize,
-                                       par->metromem, par->metromem_dma);
                fb_dealloc_cmap(&info->cmap);
+               par->board->cleanup(par);
                vfree(par->csum_table);
-               unregister_framebuffer(info);
                vfree((void __force *)info->screen_base);
-               par->board->free_irq(info);
                module_put(par->board->owner);
+               dev_dbg(&dev->dev, "calling release\n");
                framebuffer_release(info);
        }
        return 0;
@@ -759,6 +783,9 @@ static void __exit metronomefb_exit(void)
        platform_driver_unregister(&metronomefb_driver);
 }
 
+module_param(user_wfm_size, uint, 0);
+MODULE_PARM_DESC(user_wfm_size, "Set custom waveform size");
+
 module_init(metronomefb_init);
 module_exit(metronomefb_exit);
 
index dab04b4..9863f4b 100644 (file)
 #ifndef _LINUX_METRONOMEFB_H_
 #define _LINUX_METRONOMEFB_H_
 
-/* address and control descriptors used by metronome controller */
-struct metromem_desc {
-       u32 mFDADR0;
-       u32 mFSADR0;
-       u32 mFIDR0;
-       u32 mLDCMD0;
-};
-
 /* command structure used by metronome controller */
 struct metromem_cmd {
        u16 opcode;
@@ -29,34 +21,37 @@ struct metromem_cmd {
 
 /* struct used by metronome. board specific stuff comes from *board */
 struct metronomefb_par {
-       unsigned char *metromem;
-       struct metromem_desc *metromem_desc;
        struct metromem_cmd *metromem_cmd;
        unsigned char *metromem_wfm;
        unsigned char *metromem_img;
        u16 *metromem_img_csum;
        u16 *csum_table;
-       int metromemsize;
        dma_addr_t metromem_dma;
-       dma_addr_t metromem_desc_dma;
        struct fb_info *info;
        struct metronome_board *board;
        wait_queue_head_t waitq;
        u8 frame_count;
+       int extra_size;
+       int dt;
 };
 
-/* board specific routines */
+/* board specific routines and data */
 struct metronome_board {
-       struct module *owner;
-       void (*free_irq)(struct fb_info *);
-       void (*init_gpio_regs)(struct metronomefb_par *);
-       void (*init_lcdc_regs)(struct metronomefb_par *);
-       void (*post_dma_setup)(struct metronomefb_par *);
+       struct module *owner; /* the platform device */
        void (*set_rst)(struct metronomefb_par *, int);
        void (*set_stdby)(struct metronomefb_par *, int);
+       void (*cleanup)(struct metronomefb_par *);
        int (*met_wait_event)(struct metronomefb_par *);
        int (*met_wait_event_intr)(struct metronomefb_par *);
        int (*setup_irq)(struct fb_info *);
+       int (*setup_fb)(struct metronomefb_par *);
+       int (*setup_io)(struct metronomefb_par *);
+       int (*get_panel_type)(void);
+       unsigned char *metromem;
+       int fw;
+       int fh;
+       int wfm_size;
+       struct fb_info *host_fbinfo; /* the host LCD controller's fbi */
 };
 
 #endif