Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

video: ssd1307fb: Handle width and height that are not multiple of 8

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

authored by

Marko Kohtala and committed by
Bartlomiej Zolnierkiewicz
b0020d8a dd978283

+17 -13
+17 -13
drivers/video/fbdev/ssd1307fb.c
··· 150 150 { 151 151 struct ssd1307fb_array *array; 152 152 u8 *vmem = par->info->screen_buffer; 153 + unsigned int line_length = par->info->fix.line_length; 154 + unsigned int pages = DIV_ROUND_UP(par->height, 8); 153 155 int i, j, k; 154 156 155 - array = ssd1307fb_alloc_array(par->width * par->height / 8, 156 - SSD1307FB_DATA); 157 + array = ssd1307fb_alloc_array(par->width * pages, SSD1307FB_DATA); 157 158 if (!array) 158 159 return; 159 160 ··· 187 186 * (5) A4 B4 C4 D4 E4 F4 G4 H4 188 187 */ 189 188 190 - for (i = 0; i < (par->height / 8); i++) { 189 + for (i = 0; i < pages; i++) { 191 190 for (j = 0; j < par->width; j++) { 191 + int m = 8; 192 192 u32 array_idx = i * par->width + j; 193 193 array->data[array_idx] = 0; 194 - for (k = 0; k < 8; k++) { 195 - u32 page_length = par->width * i; 196 - u32 index = page_length + (par->width * k + j) / 8; 197 - u8 byte = *(vmem + index); 198 - u8 bit = byte & (1 << (j % 8)); 199 - bit = bit >> (j % 8); 194 + /* Last page may be partial */ 195 + if (i + 1 == pages && par->height % 8) 196 + m = par->height % 8; 197 + for (k = 0; k < m; k++) { 198 + u8 byte = vmem[(8 * i + k) * line_length + 199 + j / 8]; 200 + u8 bit = (byte >> (j % 8)) & 1; 200 201 array->data[array_idx] |= bit << k; 201 202 } 202 203 } 203 204 } 204 205 205 - ssd1307fb_write_array(par->client, array, par->width * par->height / 8); 206 + ssd1307fb_write_array(par->client, array, par->width * pages); 206 207 kfree(array); 207 208 } 208 209 ··· 440 437 return ret; 441 438 442 439 ret = ssd1307fb_write_cmd(par->client, 443 - par->page_offset + (par->height / 8) - 1); 440 + par->page_offset + 441 + DIV_ROUND_UP(par->height, 8) - 1); 444 442 if (ret < 0) 445 443 return ret; 446 444 ··· 619 615 par->dclk_div = par->device_info->default_dclk_div; 620 616 par->dclk_frq = par->device_info->default_dclk_frq; 621 617 622 - vmem_size = par->width * par->height / 8; 618 + vmem_size = DIV_ROUND_UP(par->width, 8) * par->height; 623 619 624 620 vmem = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 625 621 get_order(vmem_size)); ··· 642 638 643 639 info->fbops = &ssd1307fb_ops; 644 640 info->fix = ssd1307fb_fix; 645 - info->fix.line_length = par->width / 8; 641 + info->fix.line_length = DIV_ROUND_UP(par->width, 8); 646 642 info->fbdefio = ssd1307fb_defio; 647 643 648 644 info->var = ssd1307fb_var;