video: ssd1307fb: Handle width and height that are not multiple of 8
authorMarko Kohtala <marko.kohtala@okoko.fi>
Tue, 18 Jun 2019 07:41:09 +0000 (10:41 +0300)
committerBartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Tue, 23 Jul 2019 15:18:19 +0000 (17:18 +0200)
Some displays have dimensions that are not multiple of eight, for example
height of 36, but the driver divided the dimensions by 8. Defining display
to the next multiple of 8 is not good as then the display registers get
configured to dimensions that do not match. This contradicts intructions
by some display manufacturers.

Use DIV_ROUND_UP to multiple of 8 when needed so correct values can be
used.

The ssd1307fb_update_display bit reordering receives a simplification in
the process.

Signed-off-by: Marko Kohtala <marko.kohtala@okoko.fi>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Daniel Vetter <daniel@ffwll.ch>
Cc: David Airlie <airlied@linux.ie>
Cc: Michal Vokáč <michal.vokac@ysoft.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190618074111.9309-5-marko.kohtala@okoko.fi
drivers/video/fbdev/ssd1307fb.c

index 4f91148..8e07204 100644 (file)
@@ -150,10 +150,11 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
 {
        struct ssd1307fb_array *array;
        u8 *vmem = par->info->screen_buffer;
+       unsigned int line_length = par->info->fix.line_length;
+       unsigned int pages = DIV_ROUND_UP(par->height, 8);
        int i, j, k;
 
-       array = ssd1307fb_alloc_array(par->width * par->height / 8,
-                                     SSD1307FB_DATA);
+       array = ssd1307fb_alloc_array(par->width * pages, SSD1307FB_DATA);
        if (!array)
                return;
 
@@ -186,22 +187,24 @@ static void ssd1307fb_update_display(struct ssd1307fb_par *par)
         *  (5) A4 B4 C4 D4 E4 F4 G4 H4
         */
 
-       for (i = 0; i < (par->height / 8); i++) {
+       for (i = 0; i < pages; i++) {
                for (j = 0; j < par->width; j++) {
+                       int m = 8;
                        u32 array_idx = i * par->width + j;
                        array->data[array_idx] = 0;
-                       for (k = 0; k < 8; k++) {
-                               u32 page_length = par->width * i;
-                               u32 index = page_length + (par->width * k + j) / 8;
-                               u8 byte = *(vmem + index);
-                               u8 bit = byte & (1 << (j % 8));
-                               bit = bit >> (j % 8);
+                       /* Last page may be partial */
+                       if (i + 1 == pages && par->height % 8)
+                               m = par->height % 8;
+                       for (k = 0; k < m; k++) {
+                               u8 byte = vmem[(8 * i + k) * line_length +
+                                              j / 8];
+                               u8 bit = (byte >> (j % 8)) & 1;
                                array->data[array_idx] |= bit << k;
                        }
                }
        }
 
-       ssd1307fb_write_array(par->client, array, par->width * par->height / 8);
+       ssd1307fb_write_array(par->client, array, par->width * pages);
        kfree(array);
 }
 
@@ -437,7 +440,8 @@ static int ssd1307fb_init(struct ssd1307fb_par *par)
                return ret;
 
        ret = ssd1307fb_write_cmd(par->client,
-                                 par->page_offset + (par->height / 8) - 1);
+                                 par->page_offset +
+                                 DIV_ROUND_UP(par->height, 8) - 1);
        if (ret < 0)
                return ret;
 
@@ -615,7 +619,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
        par->dclk_div = par->device_info->default_dclk_div;
        par->dclk_frq = par->device_info->default_dclk_frq;
 
-       vmem_size = par->width * par->height / 8;
+       vmem_size = DIV_ROUND_UP(par->width, 8) * par->height;
 
        vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
                                        get_order(vmem_size));
@@ -638,7 +642,7 @@ static int ssd1307fb_probe(struct i2c_client *client,
 
        info->fbops = &ssd1307fb_ops;
        info->fix = ssd1307fb_fix;
-       info->fix.line_length = par->width / 8;
+       info->fix.line_length = DIV_ROUND_UP(par->width, 8);
        info->fbdefio = ssd1307fb_defio;
 
        info->var = ssd1307fb_var;