[xburst] metronomefb: Add rotation support and some minor cleanups
authorLars-Peter Clausen <lars@metafoo.de>
Thu, 11 Mar 2010 00:29:21 +0000 (00:29 +0000)
committerLars-Peter Clausen <lars@metafoo.de>
Thu, 11 Mar 2010 00:29:21 +0000 (00:29 +0000)
SVN-Revision: 20128

target/linux/xburst/files-2.6.32/drivers/video/metronomefb.c
target/linux/xburst/files-2.6.32/include/video/metronomefb.h

index 0d6a1cf..390d26e 100644 (file)
@@ -19,8 +19,6 @@
  *
  */
 
-#define DEBUG
-
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 
 #include <asm/unaligned.h>
 
-/* Display specific information */
-#define DPY_W 832
-#define DPY_H 622
-
 #define WF_MODE_INIT   0 /* Initialization */
 #define WF_MODE_MU     1 /* Monochrome update */
 #define WF_MODE_GU     2 /* Grayscale update */
@@ -63,7 +57,7 @@ struct epd_frame {
        int wfm_size;
 };
 
-static struct epd_frame epd_frame_table[] = {
+static const struct epd_frame epd_frame_table[] = {
        {
                .fw = 832,
                .fh = 622,
@@ -130,22 +124,17 @@ static struct epd_frame epd_frame_table[] = {
        },
 };
 
-static const struct fb_fix_screeninfo metronomefb_fix __devinitdata = {
+static const struct fb_fix_screeninfo metronomefb_fix __devinitconst = {
        .id =           "metronomefb",
        .type =         FB_TYPE_PACKED_PIXELS,
        .visual =       FB_VISUAL_STATIC_PSEUDOCOLOR,
        .xpanstep =     0,
        .ypanstep =     0,
        .ywrapstep =    0,
-       .line_length =  DPY_W,
        .accel =        FB_ACCEL_NONE,
 };
 
-static const struct fb_var_screeninfo metronomefb_var __devinitdata = {
-       .xres           = DPY_W,
-       .yres           = DPY_H,
-       .xres_virtual   = DPY_W,
-       .yres_virtual   = DPY_H,
+static const struct fb_var_screeninfo metronomefb_var __devinitconst = {
        .bits_per_pixel = 8,
        .grayscale      = 1,
        .nonstd         = 1,
@@ -365,7 +354,6 @@ static int metronome_display_cmd(struct metronomefb_par *par)
        int i;
        u16 cs;
        u16 opcode;
-       static u8 borderval;
        int res;
 
        res = wait_for_rdy(par);
@@ -442,8 +430,8 @@ static int __devinit metronome_config_cmd(struct metronomefb_par *par)
        will try parse the command before we've set it all up */
 
        dev_dbg(&par->pdev->dev, "%s: ENTER\n", __func__);
-       memcpy(par->metromem_cmd->args, epd_frame_table[par->dt].config,
-               sizeof(epd_frame_table[par->dt].config));
+       memcpy(par->metromem_cmd->args, par->epd_frame->config,
+               sizeof(par->epd_frame->config));
        /* the rest are 0 */
        memset((u8 *) (par->metromem_cmd->args + 4), 0, (32-4)*2);
 
@@ -520,35 +508,102 @@ static int __devinit metronome_init_regs(struct metronomefb_par *par)
        return res;
 }
 
-static void metronomefb_dpy_update(struct metronomefb_par *par, int clear_all)
+static uint16_t metronomefb_update_img_buffer_rotated(struct metronomefb_par *par)
 {
        int x, y;
-       int i;
-       u16 cksum = 0;
-       u32 *buf = (u32 __force *)par->info->screen_base;
-       u32 *img = (u32 *)(par->metromem_img);
-       u32 diff;
-       u32 tmp;
-       unsigned int fbsize = par->info->fix.smem_len;
-       int fx = par->info->fix.line_length;
-       int fy = fbsize / fx;
-       int fx_buf = fx / sizeof(*buf);
-       int m;
-       static int is_first_update = 1;
-       static int partial_updates_count = 0;
-       u32 *fxbuckets = par->fxbuckets;
-       u32 *fybuckets = par->fybuckets;
+       int xstep, ystep;
+       int i, j;
+       uint16_t cksum = 0;
+       uint8_t *buf = par->info->screen_base;
+       uint32_t *img = (uint32_t *)(par->metromem_img);
+       int fw = par->epd_frame->fw;
+       int fh = par->epd_frame->fh;
+       int fw_buf = fw / 4;
+       uint32_t *fxbuckets = par->fxbuckets;
+       uint32_t *fybuckets = par->fybuckets;
+       uint32_t diff;
+       uint32_t tmp;
+
+       switch (par->info->var.rotate) {
+       case FB_ROTATE_CW:
+               xstep = -fh;
+               ystep = fw * fh + 1;
+               j = (fw - 1) * fh;
+               break;
+       case FB_ROTATE_UD:
+               xstep = -1;
+               ystep = 0;
+               j = fw * fh - 1;
+               break;
+       case FB_ROTATE_CCW:
+               xstep = fh;
+               ystep = -fw * fh - 1;
+               j = fh - 1;
+               break;
+       default:
+               BUG();
+               break;
+       }
 
-       wait_for_rdy(par);
+       memset(fxbuckets, 0, fw_buf * sizeof(*fxbuckets));
+       memset(fybuckets, 0, fh * sizeof(*fybuckets));
+
+       i = 0;
+       for (y = 0; y < fh; y++) {
+               for(x = 0; x < fw_buf; x++, i++) {
+                       if (j < 0 || j >= fw * fh) {
+                               printk("moo: %d %d %d %d %d\n", j, x, y, fw_buf, fh);
+                               return 0;
+                       }
+                       tmp = (buf[j] << 5);
+                       j += xstep;
+                       tmp |= (buf[j] << 13);
+                       j += xstep;
+                       tmp |= (buf[j] << 21);
+                       j += xstep;
+                       tmp |= (buf[j] << 29);
+                       j += xstep;
+                       tmp &= 0xe0e0e0e0;
+
+                       img[i] &= 0xf0f0f0f0;
+                       diff = img[i] ^ tmp;
+
+                       fxbuckets[x] |= diff;
+                       fybuckets[y] |= diff;
+
+                       img[i] = (img[i] >> 4) | tmp;
+                       cksum += img[i] & 0x0000ffff;
+                       cksum += (img[i] >> 16);
 
-       memset(fxbuckets, 0, fx_buf * sizeof(*fxbuckets));
-       memset(fybuckets, 0, fy * sizeof(*fybuckets));
+               }
+               j += ystep;
+       }
+
+       return cksum;
+}
+
+static uint16_t metronomefb_update_img_buffer_normal(struct metronomefb_par *par)
+{
+       int x, y, i;
+       uint16_t cksum = 0;
+       uint32_t *buf = (uint32_t __force *)par->info->screen_base;
+       uint32_t *img = (uint32_t *)(par->metromem_img);
+       uint32_t diff;
+       uint32_t tmp;
+       int fw = par->epd_frame->fw;
+       int fh = par->epd_frame->fh;
+       int fw_buf = fw / sizeof(*buf);
+       uint32_t *fxbuckets = par->fxbuckets;
+       uint32_t *fybuckets = par->fybuckets;
+
+       memset(fxbuckets, 0, fw_buf * sizeof(*fxbuckets));
+       memset(fybuckets, 0, fh * sizeof(*fybuckets));
 
        i = 0;
-       for (y = 0; y < fy; y++) {
-               for(x = 0; x < fx_buf; x++, i++) {
-                       tmp = (buf[i] << 5) & 0xE0E0E0E0;
-                       img[i] &= 0xF0F0F0F0;
+       for (y = 0; y < fh; y++) {
+               for(x = 0; x < fw_buf; x++, i++) {
+                       tmp = (buf[i] << 5) & 0xe0e0e0e0;
+                       img[i] &= 0xf0f0f0f0;
                        diff = img[i] ^ tmp;
 
                        fxbuckets[x] |= diff;
@@ -560,73 +615,92 @@ static void metronomefb_dpy_update(struct metronomefb_par *par, int clear_all)
                }
        }
 
-       *((u16 *)(par->metromem_img) + fbsize/2) = cksum;
+       return cksum;
+}
 
-       if (clear_all || is_first_update ||
-               (partial_updates_count == par->partial_autorefresh_interval)) {
-               m = WF_MODE_GC;
-               partial_updates_count = 0;
-       } else {
-               int min_x = fx_buf;
-               int max_x = 0;
-               int min_y = fy;
-               int max_y = 0;
-               int change_count;
-
-               for (x = 0; x < fx_buf; x++)
-                       if(fxbuckets[x]) {
-                               min_x = x;
-                               break;
-                       }
+static unsigned int metronomefb_get_change_count(struct metronomefb_par *par)
+{
+       int min_x;
+       int max_x;
+       int min_y;
+       int max_y;
+       int fw = par->epd_frame->fw / 4;
+       int fh = par->epd_frame->fh;
+       unsigned int change_count;
+       uint32_t *fxbuckets = par->fxbuckets;
+       uint32_t *fybuckets = par->fybuckets;
+
+       for (min_x = 0; min_x < fw; ++min_x) {
+               if(fxbuckets[min_x])
+                       break;
+       }
 
-               for (x = fx_buf - 1; x >= 0; x--)
-                       if(fxbuckets[x]) {
-                               max_x = x;
-                               break;
-                       }
+       for (max_x = fw - 1; max_x >= 0; --max_x) {
+               if(fxbuckets[max_x])
+                       break;
+       }
 
-               for (y = 0; y < fy; y++)
-                       if(fybuckets[y]) {
-                               min_y = y;
-                               break;
-                       }
+       for (min_y = 0; min_y < fh; min_y++) {
+               if(fybuckets[min_y])
+                       break;
+       }
 
-               for (y = fy - 1; y >= 0; y--)
-                       if(fybuckets[y]) {
-                               max_y = y;
-                               break;
-                       }
+       for (max_y = fh - 1; max_y >= 0; --max_y) {
+               if(fybuckets[max_y])
+                       break;
+       }
 
-               if ((min_x > max_x) || (min_y > max_y))
-                       change_count = 0;
-               else
-                       change_count = (max_x - min_x + 1) * (max_y - min_y + 1) * sizeof(*buf);
+       if ((min_x > max_x) || (min_y > max_y))
+               change_count = 0;
+       else
+               change_count = (max_x - min_x + 1) * (max_y - min_y + 1) * 4;
 
+       dev_dbg(&par->pdev->dev, "min_x = %d, max_x = %d, min_y = %d, max_y = %d\n",
+                       min_x, max_x, min_y, max_y);
+
+       return change_count;
+}
+
+static void metronomefb_dpy_update(struct metronomefb_par *par, int clear_all)
+{
+       unsigned int fbsize = par->info->fix.smem_len;
+       uint16_t cksum;
+       int m;
+
+       wait_for_rdy(par);
+
+       if (par->info->var.rotate == 0)
+               cksum = metronomefb_update_img_buffer_normal(par);
+       else
+               cksum = metronomefb_update_img_buffer_rotated(par);
+
+       *par->metromem_img_csum = __cpu_to_le16(cksum);
+
+       if (clear_all || par->is_first_update ||
+               (par->partial_updates_count == par->partial_autorefresh_interval)) {
+               m = WF_MODE_GC;
+               par->partial_updates_count = 0;
+       } else {
+               int change_count = metronomefb_get_change_count(par);
                if (change_count < fbsize / 100 * par->manual_refresh_threshold)
                        m = WF_MODE_GU;
                else
                        m = WF_MODE_GC;
 
-               dev_dbg(&par->pdev->dev, "min_x = %d, max_x = %d, min_y = %d, max_y = %d\n",
-                               min_x, max_x, min_y, max_y);
                dev_dbg(&par->pdev->dev, "change_count = %u, treshold = %u%% (%u pixels)\n",
                                change_count, par->manual_refresh_threshold,
                                fbsize / 100 * par->manual_refresh_threshold);
-
-               partial_updates_count++;
+               ++par->partial_updates_count;
        }
 
-       if (m != par->current_wf_mode) {
+       if (m != par->current_wf_mode)
                load_waveform((u8 *) par->firmware->data, par->firmware->size,
                                m, par->current_wf_temp, par);
-       }
-
-       for(;;) {
-               if (likely(!check_err(par))) {
-                       metronome_display_cmd(par);
-                       break;
-               }
 
+again:
+       metronome_display_cmd(par);
+       wait_for_rdy(par);
+       if (unlikely(check_err(par))) {
                par->board->set_stdby(par, 0);
                printk("Resetting Metronome\n");
                par->board->set_rst(par, 0);
@@ -637,12 +711,15 @@ static void metronomefb_dpy_update(struct metronomefb_par *par, int clear_all)
                mdelay(1);
                load_waveform((u8 *) par->firmware->data, par->firmware->size,
                                WF_MODE_GC, par->current_wf_temp, par);
+
                if (par->board->power_ctl)
                        par->board->power_ctl(par, METRONOME_POWER_ON);
                metronome_bootup(par);
+
+               goto again;
        }
 
-       is_first_update = 0;
+       par->is_first_update = 0;
 }
 
 /* this is called back from the deferred io workqueue */
@@ -742,12 +819,46 @@ static ssize_t metronomefb_write(struct fb_info *info, const char __user *buf,
        return (err) ? err : count;
 }
 
+static int metronome_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       struct metronomefb_par *par = info->par;
+
+       if (par->epd_frame->fw == var->xres && par->epd_frame->fh == var->yres)
+               return 0;
+
+       return -EINVAL;
+}
+
+static int metronomefb_set_par(struct fb_info *info)
+{
+       struct metronomefb_par *par = info->par;
+
+       switch (info->var.rotate) {
+       case FB_ROTATE_CW:
+       case FB_ROTATE_CCW:
+               info->fix.line_length = info->var.yres;
+               break;
+       case FB_ROTATE_UD:
+       default:
+               info->fix.line_length = info->var.xres;
+               break;
+       }
+
+       mutex_lock(&par->lock);
+       metronomefb_dpy_update(info->par, 1);
+       mutex_unlock(&par->lock);
+
+       return 0;
+}
+
 static struct fb_ops metronomefb_ops = {
        .owner          = THIS_MODULE,
        .fb_write       = metronomefb_write,
        .fb_fillrect    = metronomefb_fillrect,
        .fb_copyarea    = metronomefb_copyarea,
        .fb_imageblit   = metronomefb_imageblit,
+       .fb_check_var   = metronome_check_var,
+       .fb_set_par     = metronomefb_set_par,
 };
 
 static struct fb_deferred_io metronomefb_defio = {
@@ -979,15 +1090,13 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
        info->var.yres = fh;
        info->var.xres_virtual = fw;
        info->var.yres_virtual = fh;
-
        info->fix = metronomefb_fix;
-       info->fix.smem_len = fw * fh; /* Real size of image area */
        info->fix.line_length = fw;
-
+       info->fix.smem_len = fw * fh; /* Real size of image area */
        par = info->par;
        par->info = info;
        par->board = board;
-       par->dt = epd_dt_index;
+       par->epd_frame = &epd_frame_table[epd_dt_index];
        par->pdev = dev;
 
        par->fxbuckets = kmalloc((fw / 4 + 1) * sizeof(*par->fxbuckets), GFP_KERNEL);
@@ -1001,6 +1110,8 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
        init_waitqueue_head(&par->waitq);
        par->manual_refresh_threshold = 60;
        par->partial_autorefresh_interval = 256;
+       par->partial_updates_count = 0;
+       par->is_first_update = 1;
        mutex_init(&par->lock);
 
        /* this table caches per page csum values. */
@@ -1025,7 +1136,7 @@ static int __devinit metronomefb_probe(struct platform_device *dev)
                goto err_csum_table;
        }
 
-       info->fix.smem_start = 0;
+       info->fix.smem_start = par->metromem_dma;
 
        /* load the waveform in. assume mode 3, temp 31 for now
                a) request the waveform file from userspace
@@ -1173,6 +1284,7 @@ static int metronomefb_suspend(struct platform_device *pdev, pm_message_t messag
        if (par->board->power_ctl)
                par->board->power_ctl(par, METRONOME_POWER_OFF);
 
+
        return 0;
 }
 
index 8c7bdbe..ea446ae 100644 (file)
@@ -17,7 +17,9 @@ struct metromem_cmd {
        u16 opcode;
        u16 args[((64-2)/2)];
        u16 csum;
-} __attribute__(packed);
+} __attribute__((packed));
+
+struct epd_frame;
 
 /* struct used by metronome. board specific stuff comes from *board */
 struct metronomefb_par {
@@ -38,9 +40,13 @@ struct metronomefb_par {
        int current_wf_temp;
        unsigned int manual_refresh_threshold;
        unsigned int partial_autorefresh_interval;
-       int dt;
+       const struct epd_frame *epd_frame;
        u32 *fxbuckets;
        u32 *fybuckets;
+
+       unsigned int partial_updates_count;
+       unsigned is_first_update:1;
+
        struct mutex lock;
 };