mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 06:01:57 +00:00
drm/stm: ltdc: add support of ycbcr pixel formats
This patch adds the following YCbCr input pixel formats on the latest LTDC hardware version: 1 plane (co-planar) : YUYV, YVYU, UYVY, VYUY 2 planes (semi-planar): NV12, NV21 3 planes (full-planar): YU12=I420=DRM YUV420, YV12=DRM YVU420 Signed-off-by: Yannick Fertre <yannick.fertre@foss.st.com> Acked-by: Philippe Cornu <philippe.cornu@foss.st.com> Reviewed-by: Philippe Cornu <philippe.cornu@foss.st.com> Signed-off-by: Philippe Cornu <philippe.cornu@foss.st.com> Link: https://patchwork.freedesktop.org/patch/msgid/20211215214843.20703-1-yannick.fertre@foss.st.com
This commit is contained in:
parent
8f2b5f6dcb
commit
484e72d314
@ -198,6 +198,21 @@
|
||||
|
||||
#define LXCFBLNR_CFBLN GENMASK(10, 0) /* Color Frame Buffer Line Number */
|
||||
|
||||
#define LXCR_C1R_YIA BIT(0) /* Ycbcr 422 Interleaved Ability */
|
||||
#define LXCR_C1R_YSPA BIT(1) /* Ycbcr 420 Semi-Planar Ability */
|
||||
#define LXCR_C1R_YFPA BIT(2) /* Ycbcr 420 Full-Planar Ability */
|
||||
#define LXCR_C1R_SCA BIT(31) /* SCaling Ability*/
|
||||
|
||||
#define LxPCR_YREN BIT(9) /* Y Rescale Enable for the color dynamic range */
|
||||
#define LxPCR_OF BIT(8) /* Odd pixel First */
|
||||
#define LxPCR_CBF BIT(7) /* CB component First */
|
||||
#define LxPCR_YF BIT(6) /* Y component First */
|
||||
#define LxPCR_YCM GENMASK(5, 4) /* Ycbcr Conversion Mode */
|
||||
#define YCM_I 0x0 /* Interleaved 422 */
|
||||
#define YCM_SP 0x1 /* Semi-Planar 420 */
|
||||
#define YCM_FP 0x2 /* Full-Planar 420 */
|
||||
#define LxPCR_YCEN BIT(3) /* YCbCr-to-RGB Conversion Enable */
|
||||
|
||||
#define LXRCR_IMR BIT(0) /* IMmediate Reload */
|
||||
#define LXRCR_VBR BIT(1) /* Vertical Blanking Reload */
|
||||
#define LXRCR_GRMSK BIT(2) /* Global (centralized) Reload MaSKed */
|
||||
@ -311,6 +326,23 @@ static const u32 ltdc_drm_fmt_a2[] = {
|
||||
DRM_FORMAT_C8
|
||||
};
|
||||
|
||||
static const u32 ltdc_drm_fmt_ycbcr_cp[] = {
|
||||
DRM_FORMAT_YUYV,
|
||||
DRM_FORMAT_YVYU,
|
||||
DRM_FORMAT_UYVY,
|
||||
DRM_FORMAT_VYUY
|
||||
};
|
||||
|
||||
static const u32 ltdc_drm_fmt_ycbcr_sp[] = {
|
||||
DRM_FORMAT_NV12,
|
||||
DRM_FORMAT_NV21
|
||||
};
|
||||
|
||||
static const u32 ltdc_drm_fmt_ycbcr_fp[] = {
|
||||
DRM_FORMAT_YUV420,
|
||||
DRM_FORMAT_YVU420
|
||||
};
|
||||
|
||||
/* Layer register offsets */
|
||||
static const u32 ltdc_layer_regs_a0[] = {
|
||||
0x80, /* L1 configuration 0 */
|
||||
@ -410,6 +442,26 @@ static const struct regmap_config stm32_ltdc_regmap_cfg = {
|
||||
.cache_type = REGCACHE_NONE,
|
||||
};
|
||||
|
||||
static const u32 ltdc_ycbcr2rgb_coeffs[DRM_COLOR_ENCODING_MAX][DRM_COLOR_RANGE_MAX][2] = {
|
||||
[DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
|
||||
0x02040199, /* (b_cb = 516 / r_cr = 409) */
|
||||
0x006400D0 /* (g_cb = 100 / g_cr = 208) */
|
||||
},
|
||||
[DRM_COLOR_YCBCR_BT601][DRM_COLOR_YCBCR_FULL_RANGE] = {
|
||||
0x01C60167, /* (b_cb = 454 / r_cr = 359) */
|
||||
0x005800B7 /* (g_cb = 88 / g_cr = 183) */
|
||||
},
|
||||
[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_LIMITED_RANGE] = {
|
||||
0x021D01CB, /* (b_cb = 541 / r_cr = 459) */
|
||||
0x00370089 /* (g_cb = 55 / g_cr = 137) */
|
||||
},
|
||||
[DRM_COLOR_YCBCR_BT709][DRM_COLOR_YCBCR_FULL_RANGE] = {
|
||||
0x01DB0193, /* (b_cb = 475 / r_cr = 403) */
|
||||
0x00300078 /* (g_cb = 48 / g_cr = 120) */
|
||||
}
|
||||
/* BT2020 not supported */
|
||||
};
|
||||
|
||||
static inline struct ltdc_device *crtc_to_ltdc(struct drm_crtc *crtc)
|
||||
{
|
||||
return (struct ltdc_device *)crtc->dev->dev_private;
|
||||
@ -540,6 +592,78 @@ static inline u32 is_xrgb(u32 drm)
|
||||
return ((drm & 0xFF) == 'X' || ((drm >> 8) & 0xFF) == 'X');
|
||||
}
|
||||
|
||||
static inline void ltdc_set_ycbcr_config(struct drm_plane *plane, u32 drm_pix_fmt)
|
||||
{
|
||||
struct ltdc_device *ldev = plane_to_ltdc(plane);
|
||||
struct drm_plane_state *state = plane->state;
|
||||
u32 lofs = plane->index * LAY_OFS;
|
||||
u32 val;
|
||||
|
||||
switch (drm_pix_fmt) {
|
||||
case DRM_FORMAT_YUYV:
|
||||
val = (YCM_I << 4) | LxPCR_YF | LxPCR_CBF;
|
||||
break;
|
||||
case DRM_FORMAT_YVYU:
|
||||
val = (YCM_I << 4) | LxPCR_YF;
|
||||
break;
|
||||
case DRM_FORMAT_UYVY:
|
||||
val = (YCM_I << 4) | LxPCR_CBF;
|
||||
break;
|
||||
case DRM_FORMAT_VYUY:
|
||||
val = (YCM_I << 4);
|
||||
break;
|
||||
case DRM_FORMAT_NV12:
|
||||
val = (YCM_SP << 4) | LxPCR_CBF;
|
||||
break;
|
||||
case DRM_FORMAT_NV21:
|
||||
val = (YCM_SP << 4);
|
||||
break;
|
||||
case DRM_FORMAT_YUV420:
|
||||
case DRM_FORMAT_YVU420:
|
||||
val = (YCM_FP << 4);
|
||||
break;
|
||||
default:
|
||||
/* RGB or not a YCbCr supported format */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Enable limited range */
|
||||
if (state->color_range == DRM_COLOR_YCBCR_LIMITED_RANGE)
|
||||
val |= LxPCR_YREN;
|
||||
|
||||
/* enable ycbcr conversion */
|
||||
val |= LxPCR_YCEN;
|
||||
|
||||
regmap_write(ldev->regmap, LTDC_L1PCR + lofs, val);
|
||||
}
|
||||
|
||||
static inline void ltdc_set_ycbcr_coeffs(struct drm_plane *plane)
|
||||
{
|
||||
struct ltdc_device *ldev = plane_to_ltdc(plane);
|
||||
struct drm_plane_state *state = plane->state;
|
||||
enum drm_color_encoding enc = state->color_encoding;
|
||||
enum drm_color_range ran = state->color_range;
|
||||
u32 lofs = plane->index * LAY_OFS;
|
||||
|
||||
if (enc != DRM_COLOR_YCBCR_BT601 && enc != DRM_COLOR_YCBCR_BT709) {
|
||||
DRM_ERROR("color encoding %d not supported, use bt601 by default\n", enc);
|
||||
/* set by default color encoding to DRM_COLOR_YCBCR_BT601 */
|
||||
enc = DRM_COLOR_YCBCR_BT601;
|
||||
}
|
||||
|
||||
if (ran != DRM_COLOR_YCBCR_LIMITED_RANGE && ran != DRM_COLOR_YCBCR_FULL_RANGE) {
|
||||
DRM_ERROR("color range %d not supported, use limited range by default\n", ran);
|
||||
/* set by default color range to DRM_COLOR_YCBCR_LIMITED_RANGE */
|
||||
ran = DRM_COLOR_YCBCR_LIMITED_RANGE;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("Color encoding=%d, range=%d\n", enc, ran);
|
||||
regmap_write(ldev->regmap, LTDC_L1CYR0R + lofs,
|
||||
ltdc_ycbcr2rgb_coeffs[enc][ran][0]);
|
||||
regmap_write(ldev->regmap, LTDC_L1CYR1R + lofs,
|
||||
ltdc_ycbcr2rgb_coeffs[enc][ran][1]);
|
||||
}
|
||||
|
||||
static irqreturn_t ltdc_irq_thread(int irq, void *arg)
|
||||
{
|
||||
struct drm_device *ddev = arg;
|
||||
@ -1010,7 +1134,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
|
||||
u32 y0 = newstate->crtc_y;
|
||||
u32 y1 = newstate->crtc_y + newstate->crtc_h - 1;
|
||||
u32 src_x, src_y, src_w, src_h;
|
||||
u32 val, pitch_in_bytes, line_length, paddr, ahbp, avbp, bpcr;
|
||||
u32 val, pitch_in_bytes, line_length, line_number, paddr, ahbp, avbp, bpcr;
|
||||
enum ltdc_pix_fmt pf;
|
||||
|
||||
if (!newstate->crtc || !fb) {
|
||||
@ -1086,8 +1210,8 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
|
||||
regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs, LXBFCR_BF2 | LXBFCR_BF1, val);
|
||||
|
||||
/* Configures the frame buffer line number */
|
||||
val = y1 - y0 + 1;
|
||||
regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, val);
|
||||
line_number = y1 - y0 + 1;
|
||||
regmap_write_bits(ldev->regmap, LTDC_L1CFBLNR + lofs, LXCFBLNR_CFBLN, line_number);
|
||||
|
||||
/* Sets the FB address */
|
||||
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 0);
|
||||
@ -1095,6 +1219,77 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
|
||||
DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
|
||||
regmap_write(ldev->regmap, LTDC_L1CFBAR + lofs, paddr);
|
||||
|
||||
if (ldev->caps.ycbcr_input) {
|
||||
if (fb->format->is_yuv) {
|
||||
switch (fb->format->format) {
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_NV21:
|
||||
/* Configure the auxiliary frame buffer address 0 & 1 */
|
||||
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr + 1);
|
||||
|
||||
/* Configure the buffer length */
|
||||
val = ((pitch_in_bytes << 16) | line_length);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
|
||||
|
||||
/* Configure the frame buffer line number */
|
||||
val = (line_number >> 1);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
|
||||
break;
|
||||
case DRM_FORMAT_YUV420:
|
||||
/* Configure the auxiliary frame buffer address 0 */
|
||||
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
|
||||
|
||||
/* Configure the auxiliary frame buffer address 1 */
|
||||
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
|
||||
|
||||
line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
|
||||
(ldev->caps.bus_width >> 3) - 1;
|
||||
|
||||
/* Configure the buffer length */
|
||||
val = (((pitch_in_bytes >> 1) << 16) | line_length);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
|
||||
|
||||
/* Configure the frame buffer line number */
|
||||
val = (line_number >> 1);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
|
||||
break;
|
||||
case DRM_FORMAT_YVU420:
|
||||
/* Configure the auxiliary frame buffer address 0 */
|
||||
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 2);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBA0R + lofs, paddr);
|
||||
|
||||
/* Configure the auxiliary frame buffer address 1 */
|
||||
paddr = (u32)drm_fb_cma_get_gem_addr(fb, newstate, 1);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBA1R + lofs, paddr);
|
||||
|
||||
line_length = ((fb->format->cpp[0] * (x1 - x0 + 1)) >> 1) +
|
||||
(ldev->caps.bus_width >> 3) - 1;
|
||||
|
||||
/* Configure the buffer length */
|
||||
val = (((pitch_in_bytes >> 1) << 16) | line_length);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBLR + lofs, val);
|
||||
|
||||
/* Configure the frame buffer line number */
|
||||
val = (line_number >> 1);
|
||||
regmap_write(ldev->regmap, LTDC_L1AFBLNR + lofs, val);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Configure YCbC conversion coefficient */
|
||||
ltdc_set_ycbcr_coeffs(plane);
|
||||
|
||||
/* Configure YCbCr format and enable/disable conversion */
|
||||
ltdc_set_ycbcr_config(plane, fb->format->format);
|
||||
} else {
|
||||
/* disable ycbcr conversion */
|
||||
regmap_write(ldev->regmap, LTDC_L1PCR + lofs, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable layer and CLUT if needed */
|
||||
val = fb->format->format == DRM_FORMAT_C8 ? LXCR_CLUTEN : 0;
|
||||
val |= LXCR_LEN;
|
||||
@ -1186,7 +1381,8 @@ static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
|
||||
};
|
||||
|
||||
static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
|
||||
enum drm_plane_type type)
|
||||
enum drm_plane_type type,
|
||||
int index)
|
||||
{
|
||||
unsigned long possible_crtcs = CRTC_MASK;
|
||||
struct ltdc_device *ldev = ddev->dev_private;
|
||||
@ -1196,9 +1392,16 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
|
||||
u32 *formats;
|
||||
u32 drm_fmt;
|
||||
const u64 *modifiers = ltdc_format_modifiers;
|
||||
u32 lofs = index * LAY_OFS;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
formats = devm_kzalloc(dev, ldev->caps.pix_fmt_nb * sizeof(*formats), GFP_KERNEL);
|
||||
/* Allocate the biggest size according to supported color formats */
|
||||
formats = devm_kzalloc(dev, (ldev->caps.pix_fmt_nb +
|
||||
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) +
|
||||
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) +
|
||||
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp)) *
|
||||
sizeof(*formats), GFP_KERNEL);
|
||||
|
||||
for (i = 0; i < ldev->caps.pix_fmt_nb; i++) {
|
||||
drm_fmt = ldev->caps.pix_fmt_drm[i];
|
||||
@ -1212,6 +1415,26 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
|
||||
formats[nb_fmt++] = drm_fmt;
|
||||
}
|
||||
|
||||
/* Add YCbCr supported pixel formats */
|
||||
if (ldev->caps.ycbcr_input) {
|
||||
regmap_read(ldev->regmap, LTDC_L1C1R + lofs, &val);
|
||||
if (val & LXCR_C1R_YIA) {
|
||||
memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_cp,
|
||||
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) * sizeof(*formats));
|
||||
nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp);
|
||||
}
|
||||
if (val & LXCR_C1R_YSPA) {
|
||||
memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_sp,
|
||||
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) * sizeof(*formats));
|
||||
nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp);
|
||||
}
|
||||
if (val & LXCR_C1R_YFPA) {
|
||||
memcpy(&formats[nb_fmt], ltdc_drm_fmt_ycbcr_fp,
|
||||
ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp) * sizeof(*formats));
|
||||
nb_fmt += ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp);
|
||||
}
|
||||
}
|
||||
|
||||
plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL);
|
||||
if (!plane)
|
||||
return NULL;
|
||||
@ -1222,6 +1445,17 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev,
|
||||
if (ret < 0)
|
||||
return NULL;
|
||||
|
||||
if (ldev->caps.ycbcr_input) {
|
||||
if (val & (LXCR_C1R_YIA | LXCR_C1R_YSPA | LXCR_C1R_YFPA))
|
||||
drm_plane_create_color_properties(plane,
|
||||
BIT(DRM_COLOR_YCBCR_BT601) |
|
||||
BIT(DRM_COLOR_YCBCR_BT709),
|
||||
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
|
||||
BIT(DRM_COLOR_YCBCR_FULL_RANGE),
|
||||
DRM_COLOR_YCBCR_BT601,
|
||||
DRM_COLOR_YCBCR_LIMITED_RANGE);
|
||||
}
|
||||
|
||||
drm_plane_helper_add(plane, <dc_plane_helper_funcs);
|
||||
|
||||
drm_plane_create_alpha_property(plane);
|
||||
@ -1247,7 +1481,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY);
|
||||
primary = ltdc_plane_create(ddev, DRM_PLANE_TYPE_PRIMARY, 0);
|
||||
if (!primary) {
|
||||
DRM_ERROR("Can not create primary plane\n");
|
||||
return -EINVAL;
|
||||
@ -1271,7 +1505,7 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
|
||||
|
||||
/* Add planes. Note : the first layer is used by primary plane */
|
||||
for (i = 1; i < ldev->caps.nb_layers; i++) {
|
||||
overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY);
|
||||
overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY, i);
|
||||
if (!overlay) {
|
||||
ret = -ENOMEM;
|
||||
DRM_ERROR("Can not create overlay plane %d\n", i);
|
||||
@ -1403,6 +1637,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
|
||||
if (ldev->caps.hw_version == HWVER_10200)
|
||||
ldev->caps.pad_max_freq_hz = 65000000;
|
||||
ldev->caps.nb_irq = 2;
|
||||
ldev->caps.ycbcr_input = false;
|
||||
ldev->caps.ycbcr_output = false;
|
||||
ldev->caps.plane_reg_shadow = false;
|
||||
break;
|
||||
@ -1416,6 +1651,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
|
||||
ldev->caps.non_alpha_only_l1 = false;
|
||||
ldev->caps.pad_max_freq_hz = 150000000;
|
||||
ldev->caps.nb_irq = 4;
|
||||
ldev->caps.ycbcr_input = false;
|
||||
ldev->caps.ycbcr_output = false;
|
||||
ldev->caps.plane_reg_shadow = false;
|
||||
break;
|
||||
@ -1429,6 +1665,7 @@ static int ltdc_get_caps(struct drm_device *ddev)
|
||||
ldev->caps.non_alpha_only_l1 = false;
|
||||
ldev->caps.pad_max_freq_hz = 90000000;
|
||||
ldev->caps.nb_irq = 2;
|
||||
ldev->caps.ycbcr_input = true;
|
||||
ldev->caps.ycbcr_output = true;
|
||||
ldev->caps.plane_reg_shadow = true;
|
||||
break;
|
||||
|
@ -24,6 +24,7 @@ struct ltdc_caps {
|
||||
bool non_alpha_only_l1; /* non-native no-alpha formats on layer 1 */
|
||||
int pad_max_freq_hz; /* max frequency supported by pad */
|
||||
int nb_irq; /* number of hardware interrupts */
|
||||
bool ycbcr_input; /* ycbcr input converter supported */
|
||||
bool ycbcr_output; /* ycbcr output converter supported */
|
||||
bool plane_reg_shadow; /* plane shadow registers ability */
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user