[PATCH 2/6] drm/tinydrm: add helpers for ST7586 controllers
From: David Lechner <david@lechnology.com>
Date: 2017-07-29 19:18:19
Also in:
dri-devel, linux-arm-kernel, linux-devicetree, lkml
Subsystem:
drm drivers, drm drivers and misc gpu patches, framebuffer layer, the rest · Maintainers:
David Airlie, Simona Vetter, Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, Helge Deller, Linus Torvalds
This adds helper functions and support for ST7586 controllers. These controllers have an unusual memory layout where 3 pixels are packed into 1 byte. +-------+-----------------+ | bit | 7 6 5 4 3 2 1 0 | +-------+-----------------+ | pixel | 0 0 0 1 1 1 2 2 | +-------+-----------------+ So, there are a nuber of places in the tinydrm pipline where this format needs to be taken into consideration. Signed-off-by: David Lechner <david@lechnology.com> --- drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c | 148 +++++++++++++++++++++++++ drivers/gpu/drm/tinydrm/mipi-dbi.c | 96 ++++++++++++---- include/drm/tinydrm/tinydrm-helpers.h | 6 + include/video/mipi_display.h | 2 + 4 files changed, 232 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
index d4cda33..4a36441 100644
--- a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c@@ -181,6 +181,154 @@ void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr, EXPORT_SYMBOL(tinydrm_xrgb8888_to_rgb565); /** + * tinydrm_rgb565_to_st7586 - Convert RGB565 to ST7586 clip buffer + * @dst: ST7586 destination buffer + * @vaddr: RGB565 source buffer + * @fb: DRM framebuffer + * @clip: Clip rectangle area to copy + * @swap: Swap bytes + * + * The ST7586 controller has a non-standard pixel layout. It is 2-bit grayscale + * (or 1-bit monochrome) packed into 3 pixels per byte. + * + * Note: the @clip x values are modified! They are aligned to the 3 pixel + * boundtry and divied by 3 to get the starting and ending byte (+ 1) in the + * @dst buffer. + */ +void tinydrm_rgb565_to_st7586(u8 *dst, void *vaddr, + struct drm_framebuffer *fb, + struct drm_clip_rect *clip) +{ + size_t len; + unsigned int x, y; + u16 *src, *buf; + u8 val; + + /* 3 pixels per byte, so align to dst byte */ + clip->x1 -= clip->x1 % 3; + clip->x2 = (clip->x2 + 2) / 3 * 3; + len = (clip->x2 - clip->x1) * sizeof(u16); + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return; + + for (y = clip->y1; y < clip->y2; y++) { + src = vaddr + (y * fb->pitches[0]); + src += clip->x1; + memcpy(buf, src, len); + src = buf; + for (x = clip->x1; x < clip->x2; x += 3) { + /* + * TODO: There is probably a better algorithm to get + * a better downsampling. + * If red or green is > 50%, set the high bit, which is + * bright gray. If blue is greater than 50%, set the + * low bit, which is dark gray. + */ + val = ((*src & 0x8000) >> 8) | + ((*src & 0x0400) >> 3) | + ((*src & 0x0010) << 2); + if (val & 0xC0) + val |= 0x20; + src++; + val |= ((*src & 0x8000) >> 11) | + ((*src & 0x0400) >> 6) | + ((*src & 0x0010) >> 1); + if (val & 0x18) + val |= 0x04; + src++; + val |= ((*src & 0x8000) >> 14) | + ((*src & 0x0400) >> 9) | + ((*src & 0x0010) >> 4); + src++; + *dst++ = ~val; + } + } + + /* now adjust the clip so it applies to dst */ + clip->x1 /= 3; + clip->x2 /= 3; + + kfree(buf); +} +EXPORT_SYMBOL(tinydrm_rgb565_to_st7586); + +/** + * tinydrm_xrgb8888_to_st7586 - Convert XRGB8888 to ST7586 clip buffer + * @dst: ST7586 destination buffer + * @vaddr: XRGB8888 source buffer + * @fb: DRM framebuffer + * @clip: Clip rectangle area to copy + * @swap: Swap bytes + * + * The ST7586 controller has a non-standard pixel layout. It is 2-bit grayscale + * (or 1-bit monochrome) packed into 3 pixels per byte. + * + * Note: the @clip x values are modified! They are aligned to the 3 pixel + * boundtry and divied by 3 to get the starting and ending byte (+ 1) in the + * @dst buffer. + */ +void tinydrm_xrgb8888_to_st7586(u8 *dst, void *vaddr, + struct drm_framebuffer *fb, + struct drm_clip_rect *clip) +{ + size_t len; + unsigned int x, y; + u32 *src, *buf; + u8 val; + + /* 3 pixels per byte, so align to dst byte */ + clip->x1 -= clip->x1 % 3; + clip->x2 = (clip->x2 + 2) / 3 * 3; + len = (clip->x2 - clip->x1) * sizeof(u32); + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return; + + for (y = clip->y1; y < clip->y2; y++) { + src = vaddr + (y * fb->pitches[0]); + src += clip->x1; + memcpy(buf, src, len); + src = buf; + for (x = clip->x1; x < clip->x2; x += 3) { + /* + * TODO: There is probably a better algorithm to get + * a better downsampling. + * If red or green is > 50%, set the high bit, which is + * bright gray. If blue is greater than 50%, set the + * low bit, which is dark gray. + */ + val = ((*src & 0x00800000) >> 16) | + ((*src & 0x00008000) >> 8) | + ((*src & 0x00000080) >> 1); + if (val & 0xC0) + val |= 0x20; + src++; + val |= ((*src & 0x00800000) >> 19) | + ((*src & 0x00008000) >> 11) | + ((*src & 0x00000080) >> 4); + if (val & 0x18) + val |= 0x04; + src++; + val |= ((*src & 0x00800000) >> 22) | + ((*src & 0x00008000) >> 14) | + ((*src & 0x00000080) >> 7); + src++; + *dst++ = ~val; + } + } + + /* now adjust the clip so it applies to dst */ + clip->x1 /= 3; + clip->x2 /= 3; + + kfree(buf); +} +EXPORT_SYMBOL(tinydrm_xrgb8888_to_st7586); + +/** * tinydrm_of_find_backlight - Find backlight device in device-tree * @dev: Device *
diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
index 7d49366..c8fb622 100644
--- a/drivers/gpu/drm/tinydrm/mipi-dbi.c
+++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c@@ -154,7 +154,8 @@ int mipi_dbi_command_buf(struct mipi_dbi *mipi, u8 cmd, u8 *data, size_t len) EXPORT_SYMBOL(mipi_dbi_command_buf); static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, - struct drm_clip_rect *clip, bool swap) + struct drm_clip_rect *clip, + enum mipi_dcs_pixel_format pixel_fmt, bool swap) { struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0); struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
@@ -169,21 +170,45 @@ static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, return ret; } - switch (fb->format->format) { - case DRM_FORMAT_RGB565: - if (swap) - tinydrm_swab16(dst, src, fb, clip); - else - tinydrm_memcpy(dst, src, fb, clip); + switch (pixel_fmt) { + case MIPI_DCS_PIXEL_FMT_16BIT: + switch (fb->format->format) { + case DRM_FORMAT_RGB565: + if (swap) + tinydrm_swab16(dst, src, fb, clip); + else + tinydrm_memcpy(dst, src, fb, clip); + break; + case DRM_FORMAT_XRGB8888: + tinydrm_xrgb8888_to_rgb565(dst, src, fb, clip, swap); + break; + default: + ret = -EINVAL; + break; + } break; - case DRM_FORMAT_XRGB8888: - tinydrm_xrgb8888_to_rgb565(dst, src, fb, clip, swap); + case MIPI_DCS_PIXEL_FMT_ST7586_332: + switch (fb->format->format) { + case DRM_FORMAT_RGB565: + tinydrm_rgb565_to_st7586(dst, src, fb, clip); + break; + case DRM_FORMAT_XRGB8888: + tinydrm_xrgb8888_to_st7586(dst, src, fb, clip); + break; + default: + ret = -EINVAL; + break; + } break; default: + ret = -EINVAL; + break; + } + if (ret) { dev_err_once(fb->dev->dev, "Format is not supported: %s\n", - drm_get_format_name(fb->format->format, - &format_name)); - return -EINVAL; + drm_get_format_name(fb->format->format, + &format_name)); + return ret; } if (import_attach)
@@ -204,8 +229,9 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, bool swap = mipi->swap_bytes; struct drm_clip_rect clip; int ret = 0; - bool full; + bool full, pixel_fmt_match; void *tr; + size_t len; mutex_lock(&tdev->dirty_lock);
@@ -218,20 +244,24 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, full = tinydrm_merge_clips(&clip, clips, num_clips, flags, fb->width, fb->height); + pixel_fmt_match = !swap && fb->format->format = DRM_FORMAT_RGB565 && + mipi->pixel_fmt = MIPI_DCS_PIXEL_FMT_16BIT; DRM_DEBUG("Flushing [FB:%d] x1=%u, x2=%u, y1=%u, y2=%u\n", fb->base.id, clip.x1, clip.x2, clip.y1, clip.y2); - if (!mipi->dc || !full || swap || - fb->format->format = DRM_FORMAT_XRGB8888) { + if (!mipi->dc || !full || !pixel_fmt_match) { tr = mipi->tx_buf; - ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, swap); + ret = mipi_dbi_buf_copy(mipi->tx_buf, fb, &clip, + mipi->pixel_fmt, swap); if (ret) goto out_unlock; } else { tr = cma_obj->vaddr; } + /* NB: mipi_dbi_buf_copy() may modify clip! */ + mipi_dbi_command(mipi, MIPI_DCS_SET_COLUMN_ADDRESS, (clip.x1 >> 8) & 0xFF, clip.x1 & 0xFF, (clip.x2 >> 8) & 0xFF, (clip.x2 - 1) & 0xFF);
@@ -239,8 +269,16 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb, (clip.y1 >> 8) & 0xFF, clip.y1 & 0xFF, (clip.y2 >> 8) & 0xFF, (clip.y2 - 1) & 0xFF); - ret = mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START, tr, - (clip.x2 - clip.x1) * (clip.y2 - clip.y1) * 2); + len = (clip.x2 - clip.x1) * (clip.y2 - clip.y1); + switch (mipi->pixel_fmt) { + case MIPI_DCS_PIXEL_FMT_16BIT: + len *= sizeof(u16); + break; + default: + break; + } + + ret = mipi_dbi_command_buf(mipi, MIPI_DCS_WRITE_MEMORY_START, tr, len); out_unlock: mutex_unlock(&tdev->dirty_lock);
@@ -288,7 +326,20 @@ static void mipi_dbi_blank(struct mipi_dbi *mipi) struct drm_device *drm = mipi->tinydrm.drm; u16 height = drm->mode_config.min_height; u16 width = drm->mode_config.min_width; - size_t len = width * height * 2; + size_t len; + + switch (mipi->pixel_fmt) { + case MIPI_DCS_PIXEL_FMT_16BIT: + len = width * height * sizeof(u16); + break; + case MIPI_DCS_PIXEL_FMT_ST7586_332: + width = (width + 2) / 3; + len = width * height; + break; + default: + /* unsupported pixel format */ + return; + } memset(mipi->tx_buf, 0, len);
@@ -367,6 +418,10 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi, case MIPI_DCS_PIXEL_FMT_16BIT: bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16); break; + case MIPI_DCS_PIXEL_FMT_ST7586_332: + /* 3 pixels per byte */ + bufsize = (mode->vdisplay + 2) / 3 * mode->hdisplay; + break; default: DRM_ERROR("Pixel format is not supported\n"); return -EINVAL;
@@ -776,7 +831,8 @@ static int mipi_dbi_typec3_command(struct mipi_dbi *mipi, u8 cmd, if (ret || !num) return ret; - if (cmd = MIPI_DCS_WRITE_MEMORY_START && !mipi->swap_bytes) + if (cmd = MIPI_DCS_WRITE_MEMORY_START && !mipi->swap_bytes && + mipi->pixel_fmt != MIPI_DCS_PIXEL_FMT_ST7586_332) bpw = 16; gpiod_set_value_cansleep(mipi->dc, 1);
diff --git a/include/drm/tinydrm/tinydrm-helpers.h b/include/drm/tinydrm/tinydrm-helpers.h
index 9b9b6cf..94792fb 100644
--- a/include/drm/tinydrm/tinydrm-helpers.h
+++ b/include/drm/tinydrm/tinydrm-helpers.h@@ -43,6 +43,12 @@ void tinydrm_swab16(u16 *dst, void *vaddr, struct drm_framebuffer *fb, void tinydrm_xrgb8888_to_rgb565(u16 *dst, void *vaddr, struct drm_framebuffer *fb, struct drm_clip_rect *clip, bool swap); +void tinydrm_rgb565_to_st7586(u8 *dst, void *vaddr, + struct drm_framebuffer *fb, + struct drm_clip_rect *clip); +void tinydrm_xrgb8888_to_st7586(u8 *dst, void *vaddr, + struct drm_framebuffer *fb, + struct drm_clip_rect *clip); struct backlight_device *tinydrm_of_find_backlight(struct device *dev); int tinydrm_enable_backlight(struct backlight_device *backlight);
diff --git a/include/video/mipi_display.h b/include/video/mipi_display.h
index 84b70cd..1c134c4 100644
--- a/include/video/mipi_display.h
+++ b/include/video/mipi_display.h@@ -135,6 +135,8 @@ enum mipi_dcs_pixel_format { MIPI_DCS_PIXEL_FMT_12BIT = 3, MIPI_DCS_PIXEL_FMT_8BIT = 2, MIPI_DCS_PIXEL_FMT_3BIT = 1, + /* non-standard format packing 1 or 2bpp in 3:3:2 bits */ + MIPI_DCS_PIXEL_FMT_ST7586_332 = -1, }; #endif
--
2.7.4