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@xxxxxxxxxxxxxx> --- 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 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html