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

video: fbdev: ssd1307fb: Optimize screen updates

Currently, each screen update triggers an I2C transfer of all screen
data, up to 1 KiB of data for a 128x64 display, which takes at least 20
ms in Fast mode.

Reduce the amount of transferred data by only updating the rectangle
that changed. Remove the calls to ssd1307fb_set_col_range() and
ssd1307fb_set_page_range() during initialization, as
ssd1307fb_update_rect() now takes care of that.

Note that for now the optimized operation is only used for fillrect,
copyarea, and imageblit, which are used by fbcon.

Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20210727134730.3765898-5-geert@linux-m68k.org

authored by

Geert Uytterhoeven and committed by
Sam Ravnborg
251e48a1 8a15af3b

+29 -21
+29 -21
drivers/video/fbdev/ssd1307fb.c
··· 186 186 return ssd1307fb_write_cmd(par->client, page_end); 187 187 } 188 188 189 - static int ssd1307fb_update_display(struct ssd1307fb_par *par) 189 + static int ssd1307fb_update_rect(struct ssd1307fb_par *par, unsigned int x, 190 + unsigned int y, unsigned int width, 191 + unsigned int height) 190 192 { 191 193 struct ssd1307fb_array *array; 192 194 u8 *vmem = par->info->screen_buffer; 193 195 unsigned int line_length = par->info->fix.line_length; 194 - unsigned int pages = DIV_ROUND_UP(par->height, 8); 196 + unsigned int pages = DIV_ROUND_UP(y % 8 + height, 8); 195 197 u32 array_idx = 0; 196 198 int ret, i, j, k; 197 199 198 - array = ssd1307fb_alloc_array(par->width * pages, SSD1307FB_DATA); 200 + array = ssd1307fb_alloc_array(width * pages, SSD1307FB_DATA); 199 201 if (!array) 200 202 return -ENOMEM; 201 203 ··· 230 228 * (5) A4 B4 C4 D4 E4 F4 G4 H4 231 229 */ 232 230 233 - for (i = 0; i < pages; i++) { 231 + ret = ssd1307fb_set_col_range(par, par->col_offset + x, width); 232 + if (ret < 0) 233 + goto out_free; 234 + 235 + ret = ssd1307fb_set_page_range(par, par->page_offset + y / 8, pages); 236 + if (ret < 0) 237 + goto out_free; 238 + 239 + for (i = y / 8; i < y / 8 + pages; i++) { 234 240 int m = 8; 235 241 236 242 /* Last page may be partial */ 237 - if (i + 1 == pages && par->height % 8) 243 + if (8 * (i + 1) > par->height) 238 244 m = par->height % 8; 239 - for (j = 0; j < par->width; j++) { 245 + for (j = x; j < x + width; j++) { 240 246 u8 data = 0; 241 247 242 248 for (k = 0; k < m; k++) { ··· 257 247 } 258 248 } 259 249 260 - ret = ssd1307fb_write_array(par->client, array, par->width * pages); 250 + ret = ssd1307fb_write_array(par->client, array, width * pages); 251 + 252 + out_free: 261 253 kfree(array); 262 254 return ret; 263 255 } 264 256 257 + static int ssd1307fb_update_display(struct ssd1307fb_par *par) 258 + { 259 + return ssd1307fb_update_rect(par, 0, 0, par->width, par->height); 260 + } 265 261 266 262 static ssize_t ssd1307fb_write(struct fb_info *info, const char __user *buf, 267 263 size_t count, loff_t *ppos) ··· 317 301 { 318 302 struct ssd1307fb_par *par = info->par; 319 303 sys_fillrect(info, rect); 320 - ssd1307fb_update_display(par); 304 + ssd1307fb_update_rect(par, rect->dx, rect->dy, rect->width, 305 + rect->height); 321 306 } 322 307 323 308 static void ssd1307fb_copyarea(struct fb_info *info, const struct fb_copyarea *area) 324 309 { 325 310 struct ssd1307fb_par *par = info->par; 326 311 sys_copyarea(info, area); 327 - ssd1307fb_update_display(par); 312 + ssd1307fb_update_rect(par, area->dx, area->dy, area->width, 313 + area->height); 328 314 } 329 315 330 316 static void ssd1307fb_imageblit(struct fb_info *info, const struct fb_image *image) 331 317 { 332 318 struct ssd1307fb_par *par = info->par; 333 319 sys_imageblit(info, image); 334 - ssd1307fb_update_display(par); 320 + ssd1307fb_update_rect(par, image->dx, image->dy, image->width, 321 + image->height); 335 322 } 336 323 337 324 static const struct fb_ops ssd1307fb_ops = { ··· 511 492 512 493 ret = ssd1307fb_write_cmd(par->client, 513 494 SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); 514 - if (ret < 0) 515 - return ret; 516 - 517 - /* Set column range */ 518 - ret = ssd1307fb_set_col_range(par, par->col_offset, par->width); 519 - if (ret < 0) 520 - return ret; 521 - 522 - /* Set page range */ 523 - ret = ssd1307fb_set_page_range(par, par->page_offset, 524 - DIV_ROUND_UP(par->height, 8)); 525 495 if (ret < 0) 526 496 return ret; 527 497