From 7a24a9749afc6803eaae80082e9e8e68b6a7ce6f Mon Sep 17 00:00:00 2001 From: Nick Hollinghurst Date: Thu, 9 Jan 2025 16:27:35 +0000 Subject: [PATCH] drm: rp1: rp1-dpi: Add "rgb_order" property (to match VC4 DPI) As on VC4, the OF property overrides the order implied by media bus format. Only 4 of the 6 possible orders are supported. New add-on hardware designs should not rely on this "legacy" feature. Signed-off-by: Nick Hollinghurst --- drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c | 15 ++ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h | 7 + drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c | 209 +++++++++++++++-------- 3 files changed, 160 insertions(+), 71 deletions(-) diff --git a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c index 6bac7ab4cb51af..6fc0143df3f16c 100644 --- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c @@ -292,6 +292,7 @@ static int rp1dpi_platform_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct rp1_dpi *dpi; struct drm_bridge *bridge = NULL; + const char *rgb_order = NULL; struct drm_panel *panel; int i, j, ret; @@ -353,6 +354,20 @@ static int rp1dpi_platform_probe(struct platform_device *pdev) if (ret) goto done_err; + dpi->rgb_order_override = -1; + if (!of_property_read_string(dev->of_node, "rgb_order", &rgb_order)) { + if (!strcmp(rgb_order, "rgb")) + dpi->rgb_order_override = RP1DPI_ORDER_RGB; + else if (!strcmp(rgb_order, "bgr")) + dpi->rgb_order_override = RP1DPI_ORDER_BGR; + else if (!strcmp(rgb_order, "grb")) + dpi->rgb_order_override = RP1DPI_ORDER_GRB; + else if (!strcmp(rgb_order, "brg")) + dpi->rgb_order_override = RP1DPI_ORDER_BRG; + else + DRM_ERROR("Invalid dpi order %s - ignored\n", rgb_order); + } + /* Check if PIO can snoop on or override DPI's GPIO1 */ dpi->gpio1_used = false; for (i = 0; !dpi->gpio1_used; i++) { diff --git a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h index 9b8ba03c942523..39c5f89ba4ec16 100644 --- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h @@ -25,6 +25,12 @@ #define RP1DPI_CLK_PLLCORE 2 #define RP1DPI_NUM_CLOCKS 3 +/* These are not RP1-specific values but chosen for VC4 compatibility: */ +#define RP1DPI_ORDER_RGB 0 +#define RP1DPI_ORDER_BGR 1 +#define RP1DPI_ORDER_GRB 2 +#define RP1DPI_ORDER_BRG 3 + /* ---------------------------------------------------------------------- */ struct rp1_dpi { @@ -45,6 +51,7 @@ struct rp1_dpi { u32 bus_fmt; bool de_inv, clk_inv; bool dpi_running, pipe_enabled; + int rgb_order_override; struct completion finished; /* Experimental stuff for interlace follows */ diff --git a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c index 78655c8e399d33..912d51f68ab690 100644 --- a/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c +++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c @@ -223,84 +223,112 @@ int rp1dpi_hw_busy(struct rp1_dpi *dpi) return (rp1dpi_hw_read(dpi, DPI_DMA_STATUS) & 0xF8F) ? 1 : 0; } -/* Table of supported input (in-memory/DMA) pixel formats. */ +/* + * Table of supported input (in-memory/DMA) pixel formats. + * + * RP1 DPI describes RGB components in terms of their MS bit position, a 10-bit + * left-aligned bit-mask, and an optional right-shift-and-OR used for scaling. + * To make it easier to permute R, G and B components, we re-pack these fields + * into 32-bit code-words, which don't themselves correspond to any register. + */ + +#define RGB_CODE(scale, shift, mask) (((scale) << 24) | ((shift) << 16) | (mask)) +#define RGB_SCALE(c) ((c) >> 24) +#define RGB_SHIFT(c) (((c) >> 16) & 31) +#define RGB_MASK(c) ((c) & 0x3ff) + struct rp1dpi_ipixfmt { - u32 format; /* DRM format code */ - u32 mask; /* RGB masks (10 bits each, left justified) */ - u32 shift; /* RGB MSB positions in the memory word */ - u32 rgbsz; /* Shifts used for scaling; also (BPP/8-1) */ + u32 format; /* DRM format code */ + u32 rgb_code[3]; /* (width&7), MS bit position, 10-bit mask */ + u32 sz; /* Bytes per pixel minus one */ }; -#define IMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_IMASK_R_MASK, r) | \ - FIELD_PREP_CONST(DPI_DMA_IMASK_G_MASK, g) | \ - FIELD_PREP_CONST(DPI_DMA_IMASK_B_MASK, b)) -#define OMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_OMASK_R_MASK, r) | \ - FIELD_PREP_CONST(DPI_DMA_OMASK_G_MASK, g) | \ - FIELD_PREP_CONST(DPI_DMA_OMASK_B_MASK, b)) -#define ISHIFT_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_SHIFT_IR_MASK, r) | \ - FIELD_PREP_CONST(DPI_DMA_SHIFT_IG_MASK, g) | \ - FIELD_PREP_CONST(DPI_DMA_SHIFT_IB_MASK, b)) -#define OSHIFT_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_SHIFT_OR_MASK, r) | \ - FIELD_PREP_CONST(DPI_DMA_SHIFT_OG_MASK, g) | \ - FIELD_PREP_CONST(DPI_DMA_SHIFT_OB_MASK, b)) - static const struct rp1dpi_ipixfmt my_formats[] = { { - .format = DRM_FORMAT_XRGB8888, - .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), - .shift = ISHIFT_RGB(23, 15, 7), - .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), + .format = DRM_FORMAT_XRGB8888, + .rgb_code = { + RGB_CODE(0, 23, 0x3fc), + RGB_CODE(0, 15, 0x3fc), + RGB_CODE(0, 7, 0x3fc), + }, + .sz = 3, }, { - .format = DRM_FORMAT_XBGR8888, - .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), - .shift = ISHIFT_RGB(7, 15, 23), - .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), + .format = DRM_FORMAT_XBGR8888, + .rgb_code = { + RGB_CODE(0, 7, 0x3fc), + RGB_CODE(0, 15, 0x3fc), + RGB_CODE(0, 23, 0x3fc), + }, + .sz = 3, }, { - .format = DRM_FORMAT_ARGB8888, - .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), - .shift = ISHIFT_RGB(23, 15, 7), - .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), + .format = DRM_FORMAT_ARGB8888, + .rgb_code = { + RGB_CODE(0, 23, 0x3fc), + RGB_CODE(0, 15, 0x3fc), + RGB_CODE(0, 7, 0x3fc), + }, + .sz = 3, }, { - .format = DRM_FORMAT_ABGR8888, - .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), - .shift = ISHIFT_RGB(7, 15, 23), - .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 3), + .format = DRM_FORMAT_ABGR8888, + .rgb_code = { + RGB_CODE(0, 7, 0x3fc), + RGB_CODE(0, 15, 0x3fc), + RGB_CODE(0, 23, 0x3fc), + }, + .sz = 3, }, { - .format = DRM_FORMAT_RGB888, - .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), - .shift = ISHIFT_RGB(23, 15, 7), - .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 2), + .format = DRM_FORMAT_RGB888, + .rgb_code = { + RGB_CODE(0, 23, 0x3fc), + RGB_CODE(0, 15, 0x3fc), + RGB_CODE(0, 7, 0x3fc), + }, + .sz = 2, }, { - .format = DRM_FORMAT_BGR888, - .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc), - .shift = ISHIFT_RGB(7, 15, 23), - .rgbsz = FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 2), + .format = DRM_FORMAT_BGR888, + .rgb_code = { + RGB_CODE(0, 7, 0x3fc), + RGB_CODE(0, 15, 0x3fc), + RGB_CODE(0, 23, 0x3fc), + }, + .sz = 2, }, { - .format = DRM_FORMAT_RGB565, - .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0), - .shift = ISHIFT_RGB(15, 10, 4), - .rgbsz = (FIELD_PREP_CONST(DPI_DMA_RGBSZ_R_MASK, 5) | - FIELD_PREP_CONST(DPI_DMA_RGBSZ_G_MASK, 6) | - FIELD_PREP_CONST(DPI_DMA_RGBSZ_B_MASK, 5) | - FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 1)), + .format = DRM_FORMAT_RGB565, + .rgb_code = { + RGB_CODE(5, 15, 0x3e0), + RGB_CODE(6, 10, 0x3f0), + RGB_CODE(5, 4, 0x3e0), + }, + .sz = 1, }, - { - .format = DRM_FORMAT_BGR565, - .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0), - .shift = ISHIFT_RGB(4, 10, 15), - .rgbsz = (FIELD_PREP_CONST(DPI_DMA_RGBSZ_R_MASK, 5) | - FIELD_PREP_CONST(DPI_DMA_RGBSZ_G_MASK, 6) | - FIELD_PREP_CONST(DPI_DMA_RGBSZ_B_MASK, 5) | - FIELD_PREP_CONST(DPI_DMA_RGBSZ_BPP_MASK, 1)), - } }; +#define IMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_IMASK_R_MASK, r) | \ + FIELD_PREP_CONST(DPI_DMA_IMASK_G_MASK, g) | \ + FIELD_PREP_CONST(DPI_DMA_IMASK_B_MASK, b)) +#define OMASK_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_OMASK_R_MASK, r) | \ + FIELD_PREP_CONST(DPI_DMA_OMASK_G_MASK, g) | \ + FIELD_PREP_CONST(DPI_DMA_OMASK_B_MASK, b)) +#define ISHIFT_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_SHIFT_IR_MASK, r) | \ + FIELD_PREP_CONST(DPI_DMA_SHIFT_IG_MASK, g) | \ + FIELD_PREP_CONST(DPI_DMA_SHIFT_IB_MASK, b)) +#define OSHIFT_RGB(r, g, b) (FIELD_PREP_CONST(DPI_DMA_SHIFT_OR_MASK, r) | \ + FIELD_PREP_CONST(DPI_DMA_SHIFT_OG_MASK, g) | \ + FIELD_PREP_CONST(DPI_DMA_SHIFT_OB_MASK, b)) + +/* + * Function to update *shift with output positions, and return output RGB masks. + * By the time we get here, RGB order has been normalized to RGB (R most significant). + * Note that an internal bus is 30 bits wide: bits [21:20], [11:10], [1:0] are dropped. + * This makes the packed RGB5656 and RGB666 formats problematic, as colour components + * need to straddle the gaps; we mitigate this by hijacking input masks and scaling. + */ static u32 set_output_format(u32 bus_format, u32 *shift, u32 *imask, u32 *rgbsz) { switch (bus_format) { @@ -308,6 +336,7 @@ static u32 set_output_format(u32 bus_format, u32 *shift, u32 *imask, u32 *rgbsz) if (*shift == ISHIFT_RGB(15, 10, 4)) { /* When framebuffer is RGB565, we can output RGB565 */ *shift = ISHIFT_RGB(15, 7, 0) | OSHIFT_RGB(19, 9, 0); + *imask = IMASK_RGB(0x3fc, 0x3fc, 0); *rgbsz &= DPI_DMA_RGBSZ_BPP_MASK; return OMASK_RGB(0x3fc, 0x3fc, 0); } @@ -322,7 +351,7 @@ static u32 set_output_format(u32 bus_format, u32 *shift, u32 *imask, u32 *rgbsz) case MEDIA_BUS_FMT_BGR666_1X18: /* due to a HW limitation, bit-depth is effectively RGB444 */ *shift |= OSHIFT_RGB(23, 15, 7); - *imask &= IMASK_RGB(0x3c0, 0x3c0, 0x3c0); + *imask = IMASK_RGB(0x3c0, 0x3c0, 0x3c0); *rgbsz = BITS(DPI_DMA_RGBSZ_R, 2) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK); return OMASK_RGB(0x330, 0x3c0, 0x3c0); @@ -359,7 +388,8 @@ void rp1dpi_hw_setup(struct rp1_dpi *dpi, struct drm_display_mode const *mode) { u32 shift, imask, omask, rgbsz, vctrl; - int i; + u32 rgb_code[3]; + int order, i; drm_info(&dpi->drm, "in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d%s %dkHz %cH%cV%cD%cC", @@ -373,26 +403,63 @@ void rp1dpi_hw_setup(struct rp1_dpi *dpi, de_inv ? '-' : '+', dpi->clk_inv ? '-' : '+'); - /* - * Configure all DPI/DMA block registers, except base address. - * DMA will not actually start until a FB base address is specified - * using rp1dpi_hw_update(). - */ + /* Look up the input (in-memory) pixel format */ for (i = 0; i < ARRAY_SIZE(my_formats); ++i) { if (my_formats[i].format == in_format) break; } if (i >= ARRAY_SIZE(my_formats)) { pr_err("%s: bad input format\n", __func__); - i = 4; + i = ARRAY_SIZE(my_formats) - 1; + } + + /* + * Although these RGB orderings refer to the output (DPI bus) format, + * here we permute the *input* components. After this point, "Red" + * will be most significant (highest numbered GPIOs), regardless + * of rgb_order or bus_format. This simplifies later workarounds. + */ + order = dpi->rgb_order_override; + if (order < 0) + order = BUS_FMT_IS_BGR(bus_format) ? RP1DPI_ORDER_BGR : RP1DPI_ORDER_RGB; + switch (order) { + case RP1DPI_ORDER_RGB: + rgb_code[0] = my_formats[i].rgb_code[0]; + rgb_code[1] = my_formats[i].rgb_code[1]; + rgb_code[2] = my_formats[i].rgb_code[2]; + break; + case RP1DPI_ORDER_BGR: + rgb_code[0] = my_formats[i].rgb_code[2]; + rgb_code[1] = my_formats[i].rgb_code[1]; + rgb_code[2] = my_formats[i].rgb_code[0]; + break; + case RP1DPI_ORDER_GRB: + rgb_code[0] = my_formats[i].rgb_code[1]; + rgb_code[1] = my_formats[i].rgb_code[0]; + rgb_code[2] = my_formats[i].rgb_code[2]; + break; + default: /* RP1DPI_ORDER_BRG */ + rgb_code[0] = my_formats[i].rgb_code[2]; + rgb_code[1] = my_formats[i].rgb_code[0]; + rgb_code[2] = my_formats[i].rgb_code[1]; } - if (BUS_FMT_IS_BGR(bus_format)) - i ^= 1; - shift = my_formats[i].shift; - imask = my_formats[i].mask; - rgbsz = my_formats[i].rgbsz; + rgbsz = FIELD_PREP(DPI_DMA_RGBSZ_BPP_MASK, my_formats[i].sz) | + FIELD_PREP(DPI_DMA_RGBSZ_R_MASK, RGB_SCALE(rgb_code[0])) | + FIELD_PREP(DPI_DMA_RGBSZ_G_MASK, RGB_SCALE(rgb_code[1])) | + FIELD_PREP(DPI_DMA_RGBSZ_B_MASK, RGB_SCALE(rgb_code[2])); + shift = FIELD_PREP(DPI_DMA_SHIFT_IR_MASK, RGB_SHIFT(rgb_code[0])) | + FIELD_PREP(DPI_DMA_SHIFT_IG_MASK, RGB_SHIFT(rgb_code[1])) | + FIELD_PREP(DPI_DMA_SHIFT_IB_MASK, RGB_SHIFT(rgb_code[2])); + imask = FIELD_PREP(DPI_DMA_IMASK_R_MASK, RGB_MASK(rgb_code[0])) | + FIELD_PREP(DPI_DMA_IMASK_G_MASK, RGB_MASK(rgb_code[1])) | + FIELD_PREP(DPI_DMA_IMASK_B_MASK, RGB_MASK(rgb_code[2])); omask = set_output_format(bus_format, &shift, &imask, &rgbsz); + /* + * Configure all DPI/DMA block registers, except base address. + * DMA will not actually start until a FB base address is specified + * using rp1dpi_hw_update(). + */ rp1dpi_hw_write(dpi, DPI_DMA_IMASK, imask); rp1dpi_hw_write(dpi, DPI_DMA_OMASK, omask); rp1dpi_hw_write(dpi, DPI_DMA_SHIFT, shift);