diff --git a/arch/sim/src/sim/up_lcd.c b/arch/sim/src/sim/up_lcd.c index 98de2ab532..6420930688 100644 --- a/arch/sim/src/sim/up_lcd.c +++ b/arch/sim/src/sim/up_lcd.c @@ -104,9 +104,10 @@ struct sim_dev_s static int sim_putrun(struct lcd_dev_s *dev, fb_coord_t row, fb_coord_t col, const uint8_t *buffer, size_t npixels); -static int sim_putarea(struct lcd_dev_s *dev, fb_coord_t row_start, - fb_coord_t row_end, fb_coord_t col_start, - fb_coord_t col_end, const uint8_t *buffer); +static int sim_putarea(struct lcd_dev_s *dev, + fb_coord_t row_start, fb_coord_t row_end, + fb_coord_t col_start, fb_coord_t col_end, + const uint8_t *buffer, fb_coord_t stride); static int sim_getrun(struct lcd_dev_s *dev, fb_coord_t row, fb_coord_t col, uint8_t *buffer, size_t npixels); @@ -252,12 +253,17 @@ static int sim_putrun(struct lcd_dev_s *dev, fb_coord_t row, fb_coord_t col, * col_end - Ending column to write to * (range: col_start <= col_end < xres) * buffer - The buffer containing the area to be written to the LCD + * stride - Length of a line in bytes. This parameter may be necessary + * to allow the LCD driver to calculate the offset for partial + * writes when the buffer needs to be splited for row-by-row + * writing. * ****************************************************************************/ static int sim_putarea(struct lcd_dev_s *dev, fb_coord_t row_start, fb_coord_t row_end, fb_coord_t col_start, - fb_coord_t col_end, const uint8_t *buffer) + fb_coord_t col_end, const uint8_t *buffer, + fb_coord_t stride) { fb_coord_t row; size_t rows; diff --git a/drivers/lcd/apa102.c b/drivers/lcd/apa102.c index 1c787f31a2..981ec954ac 100644 --- a/drivers/lcd/apa102.c +++ b/drivers/lcd/apa102.c @@ -162,7 +162,7 @@ static int apa102_putrun(FAR struct lcd_dev_s *dev, fb_coord_t row, static int apa102_putarea(FAR struct lcd_dev_s *dev, fb_coord_t row_start, fb_coord_t row_end, fb_coord_t col_start, fb_coord_t col_end, - FAR const uint8_t *buffer); + FAR const uint8_t *buffer, fb_coord_t stride); static int apa102_getrun(FAR struct lcd_dev_s *dev, fb_coord_t row, fb_coord_t col, FAR uint8_t *buffer, size_t npixels); @@ -474,13 +474,17 @@ static int apa102_putrun(FAR struct lcd_dev_s *dev, fb_coord_t row, * col_end - Ending column to write to * (range: col_start <= col_end < xres) * buffer - The buffer containing the area to be written to the LCD + * stride - Length of a line in bytes. This parameter may be necessary + * to allow the LCD driver to calculate the offset for partial + * writes when the buffer needs to be splited for row-by-row + * writing. * ****************************************************************************/ static int apa102_putarea(FAR struct lcd_dev_s *dev, fb_coord_t row_start, fb_coord_t row_end, fb_coord_t col_start, fb_coord_t col_end, - FAR const uint8_t *buffer) + FAR const uint8_t *buffer, fb_coord_t stride) { FAR struct apa102_dev_s *priv = (FAR struct apa102_dev_s *)dev; FAR uint16_t *src = (FAR uint16_t *)buffer; diff --git a/drivers/lcd/gc9a01.c b/drivers/lcd/gc9a01.c index 903fb9149e..229f7be761 100644 --- a/drivers/lcd/gc9a01.c +++ b/drivers/lcd/gc9a01.c @@ -189,7 +189,8 @@ static void gc9a01_setarea(FAR struct gc9a01_dev_s *dev, uint16_t x1, uint16_t y1); static void gc9a01_bpp(FAR struct gc9a01_dev_s *dev, int bpp); static void gc9a01_wrram(FAR struct gc9a01_dev_s *dev, - FAR const uint16_t *buff, size_t size); + FAR const uint8_t *buff, size_t size , size_t skip, + size_t count); #ifndef CONFIG_LCD_NOGETRUN static void gc9a01_rdram(FAR struct gc9a01_dev_s *dev, FAR uint16_t *buff, size_t size); @@ -204,7 +205,7 @@ static int gc9a01_putrun(FAR struct lcd_dev_s *dev, static int gc9a01_putarea(FAR struct lcd_dev_s *dev, fb_coord_t row_start, fb_coord_t row_end, fb_coord_t col_start, fb_coord_t col_end, - FAR const uint8_t *buffer); + FAR const uint8_t *buffer, fb_coord_t stride); #ifndef CONFIG_LCD_NOGETRUN static int gc9a01_getrun(FAR struct lcd_dev_s *dev, fb_coord_t row, fb_coord_t col, @@ -535,17 +536,26 @@ static void gc9a01_bpp(FAR struct gc9a01_dev_s *dev, int bpp) * Name: gc9a01_wrram * * Description: - * Write to the driver's RAM. + * Write to the driver's RAM. It is possible to write multiples of size + * while skipping some values. * ****************************************************************************/ static void gc9a01_wrram(FAR struct gc9a01_dev_s *dev, - FAR const uint16_t *buff, size_t size) + FAR const uint8_t *buff, size_t size, size_t skip, + size_t count) { + size_t i; + gc9a01_sendcmd(dev, GC9A01_RAMWR); - gc9a01_select(dev->spi, GC9A01_BYTESPP * 8); - SPI_SNDBLOCK(dev->spi, buff, size); + gc9a01_select(dev->spi, 8); + + for (i = 0; i < count; i++) + { + SPI_SNDBLOCK(dev->spi, buff + (i * (size + skip)), size); + } + gc9a01_deselect(dev->spi); } @@ -614,13 +624,12 @@ static int gc9a01_putrun(FAR struct lcd_dev_s *dev, FAR const uint8_t *buffer, size_t npixels) { FAR struct gc9a01_dev_s *priv = (FAR struct gc9a01_dev_s *)dev; - FAR const uint16_t *src = (FAR const uint16_t *)buffer; ginfo("row: %d col: %d npixels: %d\n", row, col, npixels); DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0); gc9a01_setarea(priv, col, row, col + npixels - 1, row); - gc9a01_wrram(priv, src, npixels); + gc9a01_wrram(priv, buffer, npixels, 0, 1); return OK; } @@ -638,16 +647,22 @@ static int gc9a01_putrun(FAR struct lcd_dev_s *dev, * col_end - Ending column to write to * (range: col_start <= col_end < xres) * buffer - The buffer containing the area to be written to the LCD + * stride - Length of a line in bytes. This parameter may be necessary + * to allow the LCD driver to calculate the offset for partial + * writes when the buffer needs to be splited for row-by-row + * writing. * ****************************************************************************/ static int gc9a01_putarea(FAR struct lcd_dev_s *dev, fb_coord_t row_start, fb_coord_t row_end, fb_coord_t col_start, fb_coord_t col_end, - FAR const uint8_t *buffer) + FAR const uint8_t *buffer, fb_coord_t stride) { FAR struct gc9a01_dev_s *priv = (FAR struct gc9a01_dev_s *)dev; - FAR const uint16_t *src = (FAR const uint16_t *)buffer; + size_t cols = col_end - col_start + 1; + size_t rows = row_end - row_start + 1; + size_t row_size = cols * (priv->bpp >> 3); ginfo("row_start: %d row_end: %d col_start: %d col_end: %d\n", row_start, row_end, col_start, col_end); @@ -655,8 +670,26 @@ static int gc9a01_putarea(FAR struct lcd_dev_s *dev, DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0); gc9a01_setarea(priv, col_start, row_start, col_end, row_end); - gc9a01_wrram(priv, src, - (row_end - row_start + 1) * (col_end - col_start + 1)); + + /* If the stride is the same of the row, a single SPI transfer is enough. + * That is always true for lcddev. For framebuffer, that indicates a full + * screen or full row update. + */ + + if (stride == row_size) + { + /* simpler case, we can just send the whole buffer */ + + ginfo("Using full screen/full row mode\n"); + gc9a01_wrram(priv, buffer, rows * row_size, 0, 1); + } + else + { + /* We have to go row by row */ + + ginfo("Falling-back to row by row mode\n"); + gc9a01_wrram(priv, buffer, row_size, stride - row_size, rows); + } return OK; } diff --git a/drivers/lcd/lcd_dev.c b/drivers/lcd/lcd_dev.c index 7da6a06625..7573619eeb 100644 --- a/drivers/lcd/lcd_dev.c +++ b/drivers/lcd/lcd_dev.c @@ -121,6 +121,8 @@ static int lcddev_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR struct lcddev_area_s *lcd_area = (FAR struct lcddev_area_s *)arg; + size_t cols = lcd_area->col_end - lcd_area->col_start + 1; + size_t row_size = cols * (priv->planeinfo.bpp >> 3); if (priv->planeinfo.getarea) { @@ -129,27 +131,27 @@ static int lcddev_ioctl(FAR struct file *filep, int cmd, unsigned long arg) lcd_area->row_end, lcd_area->col_start, lcd_area->col_end, - lcd_area->data); + lcd_area->data, + row_size); } else { /* Emulate getarea() using getrun() */ uint8_t *buf = lcd_area->data; - size_t npixels = (lcd_area->col_end - lcd_area->col_start + 1); int row; for (row = lcd_area->row_start; row <= lcd_area->row_end; row++) { ret = priv->planeinfo.getrun(priv->lcd_ptr, row, lcd_area->col_start, buf, - npixels); + row_size); if (ret < 0) { break; } - buf += npixels * (priv->planeinfo.bpp >> 3); + buf += row_size; } } } @@ -158,6 +160,8 @@ static int lcddev_ioctl(FAR struct file *filep, int cmd, unsigned long arg) { FAR const struct lcddev_area_s *lcd_area = (FAR const struct lcddev_area_s *)arg; + size_t cols = lcd_area->col_end - lcd_area->col_start + 1; + size_t row_size = cols * (priv->planeinfo.bpp >> 3); if (priv->planeinfo.putarea) { @@ -166,27 +170,27 @@ static int lcddev_ioctl(FAR struct file *filep, int cmd, unsigned long arg) lcd_area->row_end, lcd_area->col_start, lcd_area->col_end, - lcd_area->data); + lcd_area->data, + row_size); } else { /* Emulate putarea() using putrun() */ uint8_t *buf = lcd_area->data; - size_t npixels = (lcd_area->col_end - lcd_area->col_start + 1); int row; for (row = lcd_area->row_start; row <= lcd_area->row_end; row++) { ret = priv->planeinfo.putrun(priv->lcd_ptr, row, lcd_area->col_start, buf, - npixels); + row_size); if (ret < 0) { break; } - buf += npixels * (priv->planeinfo.bpp >> 3); + buf += row_size; } } } diff --git a/drivers/lcd/lcd_framebuffer.c b/drivers/lcd/lcd_framebuffer.c index 884fe0f40d..7645aadfd6 100644 --- a/drivers/lcd/lcd_framebuffer.c +++ b/drivers/lcd/lcd_framebuffer.c @@ -211,6 +211,11 @@ static int lcdfb_updateearea(FAR struct fb_vtable_s *vtable, unsigned int pixperbyte = 8 / pinfo->bpp; startx &= ~(pixperbyte - 1); } + + /* Get the starting position in the framebuffer */ + + run = priv->fbmem + starty * priv->stride; + run += (startx * pinfo->bpp + 7) >> 3; } if (pinfo->putarea != NULL) @@ -225,7 +230,8 @@ static int lcdfb_updateearea(FAR struct fb_vtable_s *vtable, * - apply DMA channel to transfer data to driver memory. */ - ret = pinfo->putarea(pinfo->dev, starty, endy, startx, endx, run); + ret = pinfo->putarea(pinfo->dev, starty, endy, startx, endx, + run, priv->stride); if (ret < 0) { lcderr("Failed to update area"); @@ -236,11 +242,6 @@ static int lcdfb_updateearea(FAR struct fb_vtable_s *vtable, { width = endx - startx + 1; - /* Get the starting position in the framebuffer */ - - run = priv->fbmem + starty * priv->stride; - run += (startx * pinfo->bpp + 7) >> 3; - for (row = starty; row <= endy; row++) { ret = pinfo->putrun(pinfo->dev, row, startx, run, width); diff --git a/drivers/lcd/st7789.c b/drivers/lcd/st7789.c index 92f79bfcb1..469ddbce93 100644 --- a/drivers/lcd/st7789.c +++ b/drivers/lcd/st7789.c @@ -186,7 +186,7 @@ static void st7789_setarea(FAR struct st7789_dev_s *dev, uint16_t x1, uint16_t y1); static void st7789_bpp(FAR struct st7789_dev_s *dev, int bpp); static void st7789_wrram(FAR struct st7789_dev_s *dev, - FAR const uint16_t *buff, size_t size, size_t skip, + FAR const uint8_t *buff, size_t size, size_t skip, size_t count); #ifndef CONFIG_LCD_NOGETRUN static void st7789_rdram(FAR struct st7789_dev_s *dev, @@ -202,7 +202,7 @@ static int st7789_putrun(FAR struct lcd_dev_s *dev, static int st7789_putarea(FAR struct lcd_dev_s *dev, fb_coord_t row_start, fb_coord_t row_end, fb_coord_t col_start, fb_coord_t col_end, - FAR const uint8_t *buffer); + FAR const uint8_t *buffer, fb_coord_t stride); #ifndef CONFIG_LCD_NOGETRUN static int st7789_getrun(FAR struct lcd_dev_s *dev, fb_coord_t row, fb_coord_t col, @@ -448,14 +448,14 @@ static void st7789_bpp(FAR struct st7789_dev_s *dev, int bpp) ****************************************************************************/ static void st7789_wrram(FAR struct st7789_dev_s *dev, - FAR const uint16_t *buff, size_t size, size_t skip, + FAR const uint8_t *buff, size_t size, size_t skip, size_t count) { size_t i; st7789_sendcmd(dev, ST7789_RAMWR); - st7789_select(dev->spi, ST7789_BYTESPP * 8); + st7789_select(dev->spi, 8); for (i = 0; i < count; i++) { @@ -530,13 +530,12 @@ static int st7789_putrun(FAR struct lcd_dev_s *dev, FAR const uint8_t *buffer, size_t npixels) { FAR struct st7789_dev_s *priv = (FAR struct st7789_dev_s *)dev; - FAR const uint16_t *src = (FAR const uint16_t *)buffer; ginfo("row: %d col: %d npixels: %d\n", row, col, npixels); DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0); st7789_setarea(priv, col, row, col + npixels - 1, row); - st7789_wrram(priv, src, npixels, 0, 1); + st7789_wrram(priv, buffer, npixels, 0, 1); return OK; } @@ -554,17 +553,22 @@ static int st7789_putrun(FAR struct lcd_dev_s *dev, * col_end - Ending column to write to * (range: col_start <= col_end < xres) * buffer - The buffer containing the area to be written to the LCD + * stride - Length of a line in bytes. This parameter may be necessary + * to allow the LCD driver to calculate the offset for partial + * writes when the buffer needs to be splited for row-by-row + * writing. * ****************************************************************************/ static int st7789_putarea(FAR struct lcd_dev_s *dev, fb_coord_t row_start, fb_coord_t row_end, fb_coord_t col_start, fb_coord_t col_end, - FAR const uint8_t *buffer) + FAR const uint8_t *buffer, fb_coord_t stride) { FAR struct st7789_dev_s *priv = (FAR struct st7789_dev_s *)dev; - FAR const uint16_t *src = (FAR const uint16_t *)buffer; - fb_coord_t bsiz = col_end - col_start + 1; + size_t cols = col_end - col_start + 1; + size_t rows = row_end - row_start + 1; + size_t row_size = cols * (priv->bpp >> 3); ginfo("row_start: %d row_end: %d col_start: %d col_end: %d\n", row_start, row_end, col_start, col_end); @@ -572,8 +576,26 @@ static int st7789_putarea(FAR struct lcd_dev_s *dev, DEBUGASSERT(buffer && ((uintptr_t)buffer & 1) == 0); st7789_setarea(priv, col_start, row_start, col_end, row_end); - st7789_wrram(priv, src + (ST7789_XRES * row_start) + col_start, - bsiz, ST7789_XRES - bsiz, row_end - row_start + 1); + + /* If the stride is the same of the row, a single SPI transfer is enough. + * That is always true for lcddev. For framebuffer, that indicates a full + * screen or full row update. + */ + + if (stride == row_size) + { + /* simpler case, we can just send the whole buffer */ + + ginfo("Using full screen/full row mode\n"); + st7789_wrram(priv, buffer, rows * row_size, 0, 1); + } + else + { + /* We have to go row by row */ + + ginfo("Falling-back to row by row mode\n"); + st7789_wrram(priv, buffer, row_size, stride - row_size, rows); + } return OK; } diff --git a/include/nuttx/lcd/lcd.h b/include/nuttx/lcd/lcd.h index 7879dc3f1e..b76d680278 100644 --- a/include/nuttx/lcd/lcd.h +++ b/include/nuttx/lcd/lcd.h @@ -79,9 +79,11 @@ struct lcd_planeinfo_s * col_start - Starting column to write to (range: 0 <= col <= xres) * col_end - Ending column to write to * (range: col_start <= col_end < xres) - * buffer - The buffer containing the complete frame to be written to - * the display (the correct rows and columns have to be - * selected from it) + * buffer - The buffer containing the area to be written to the LCD + * stride - Length of a line in bytes. This parameter may be necessary + * to allow the LCD driver to calculate the offset for partial + * writes when the buffer needs to be splited for row-by-row + * writing. * * NOTE: this operation may not be supported by the device, in which case * the callback pointer will be NULL. In that case, putrun() should be @@ -90,7 +92,8 @@ struct lcd_planeinfo_s int (*putarea)(FAR struct lcd_dev_s *dev, fb_coord_t row_start, fb_coord_t row_end, fb_coord_t col_start, - fb_coord_t col_end, FAR const uint8_t *buffer); + fb_coord_t col_end, FAR const uint8_t *buffer, + fb_coord_t stride); /* This method can be used to read a partial raster line from the LCD: * @@ -115,6 +118,7 @@ struct lcd_planeinfo_s * col_end - Ending column to read from * (range: col_start <= col_end < xres) * buffer - The buffer where the data will be written + * stride - Length of a line in bytes. * * NOTE: this operation may not be supported by the device, in which case * the callback pointer will be NULL. In that case, getrun() should be @@ -123,7 +127,8 @@ struct lcd_planeinfo_s int (*getarea)(FAR struct lcd_dev_s *dev, fb_coord_t row_start, fb_coord_t row_end, fb_coord_t col_start, - fb_coord_t col_end, FAR uint8_t *buffer); + fb_coord_t col_end, FAR uint8_t *buffer, + fb_coord_t stride); /* This method can be used to redraw display's content. *