summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/stm/ltdc.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2022-04-19 17:26:01 +0200
committerTakashi Iwai <tiwai@suse.de>2022-04-19 17:26:01 +0200
commit0aea30a07ec6b50de0fc5f5b2ec34a68ead86b61 (patch)
treeee7d7d116570f39e47399c8f691a5a7565077eeb /drivers/gpu/drm/stm/ltdc.c
parent4ddef9c4d70aae0c9029bdec7c3f7f1c1c51ff8c (diff)
parent5b933c7262c5b0ea11ea3c3b3ea81add04895954 (diff)
Merge tag 'asoc-fix-v5.18-rc3' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: Fixes for v5.18 A collection of fixes that came in since the merge window, plus one new device ID for an x86 laptop. Nothing that really stands out with particularly big impact outside of the affected device.
Diffstat (limited to 'drivers/gpu/drm/stm/ltdc.c')
-rw-r--r--drivers/gpu/drm/stm/ltdc.c915
1 files changed, 749 insertions, 166 deletions
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index dbdee954692a..17cc050207f4 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -18,6 +18,7 @@
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/reset.h>
#include <drm/drm_atomic.h>
@@ -46,15 +47,15 @@
#define HWVER_10200 0x010200
#define HWVER_10300 0x010300
#define HWVER_20101 0x020101
+#define HWVER_40100 0x040100
/*
* The address of some registers depends on the HW version: such registers have
- * an extra offset specified with reg_ofs.
+ * an extra offset specified with layer_ofs.
*/
-#define REG_OFS_NONE 0
-#define REG_OFS_4 4 /* Insertion of "Layer Conf. 2" reg */
-#define REG_OFS (ldev->caps.reg_ofs)
-#define LAY_OFS 0x80 /* Register Offset between 2 layers */
+#define LAY_OFS_0 0x80
+#define LAY_OFS_1 0x100
+#define LAY_OFS (ldev->caps.layer_ofs)
/* Global register offsets */
#define LTDC_IDR 0x0000 /* IDentification */
@@ -75,29 +76,36 @@
#define LTDC_LIPCR 0x0040 /* Line Interrupt Position Conf. */
#define LTDC_CPSR 0x0044 /* Current Position Status */
#define LTDC_CDSR 0x0048 /* Current Display Status */
+#define LTDC_EDCR 0x0060 /* External Display Control */
+#define LTDC_CCRCR 0x007C /* Computed CRC value */
+#define LTDC_FUT 0x0090 /* Fifo underrun Threshold */
/* Layer register offsets */
-#define LTDC_L1LC1R (0x80) /* L1 Layer Configuration 1 */
-#define LTDC_L1LC2R (0x84) /* L1 Layer Configuration 2 */
-#define LTDC_L1CR (0x84 + REG_OFS)/* L1 Control */
-#define LTDC_L1WHPCR (0x88 + REG_OFS)/* L1 Window Hor Position Config */
-#define LTDC_L1WVPCR (0x8C + REG_OFS)/* L1 Window Vert Position Config */
-#define LTDC_L1CKCR (0x90 + REG_OFS)/* L1 Color Keying Configuration */
-#define LTDC_L1PFCR (0x94 + REG_OFS)/* L1 Pixel Format Configuration */
-#define LTDC_L1CACR (0x98 + REG_OFS)/* L1 Constant Alpha Config */
-#define LTDC_L1DCCR (0x9C + REG_OFS)/* L1 Default Color Configuration */
-#define LTDC_L1BFCR (0xA0 + REG_OFS)/* L1 Blend Factors Configuration */
-#define LTDC_L1FBBCR (0xA4 + REG_OFS)/* L1 FrameBuffer Bus Control */
-#define LTDC_L1AFBCR (0xA8 + REG_OFS)/* L1 AuxFB Control */
-#define LTDC_L1CFBAR (0xAC + REG_OFS)/* L1 Color FrameBuffer Address */
-#define LTDC_L1CFBLR (0xB0 + REG_OFS)/* L1 Color FrameBuffer Length */
-#define LTDC_L1CFBLNR (0xB4 + REG_OFS)/* L1 Color FrameBuffer Line Nb */
-#define LTDC_L1AFBAR (0xB8 + REG_OFS)/* L1 AuxFB Address */
-#define LTDC_L1AFBLR (0xBC + REG_OFS)/* L1 AuxFB Length */
-#define LTDC_L1AFBLNR (0xC0 + REG_OFS)/* L1 AuxFB Line Number */
-#define LTDC_L1CLUTWR (0xC4 + REG_OFS)/* L1 CLUT Write */
-#define LTDC_L1YS1R (0xE0 + REG_OFS)/* L1 YCbCr Scale 1 */
-#define LTDC_L1YS2R (0xE4 + REG_OFS)/* L1 YCbCr Scale 2 */
+#define LTDC_L1C0R (ldev->caps.layer_regs[0]) /* L1 configuration 0 */
+#define LTDC_L1C1R (ldev->caps.layer_regs[1]) /* L1 configuration 1 */
+#define LTDC_L1RCR (ldev->caps.layer_regs[2]) /* L1 reload control */
+#define LTDC_L1CR (ldev->caps.layer_regs[3]) /* L1 control register */
+#define LTDC_L1WHPCR (ldev->caps.layer_regs[4]) /* L1 window horizontal position configuration */
+#define LTDC_L1WVPCR (ldev->caps.layer_regs[5]) /* L1 window vertical position configuration */
+#define LTDC_L1CKCR (ldev->caps.layer_regs[6]) /* L1 color keying configuration */
+#define LTDC_L1PFCR (ldev->caps.layer_regs[7]) /* L1 pixel format configuration */
+#define LTDC_L1CACR (ldev->caps.layer_regs[8]) /* L1 constant alpha configuration */
+#define LTDC_L1DCCR (ldev->caps.layer_regs[9]) /* L1 default color configuration */
+#define LTDC_L1BFCR (ldev->caps.layer_regs[10]) /* L1 blending factors configuration */
+#define LTDC_L1BLCR (ldev->caps.layer_regs[11]) /* L1 burst length configuration */
+#define LTDC_L1PCR (ldev->caps.layer_regs[12]) /* L1 planar configuration */
+#define LTDC_L1CFBAR (ldev->caps.layer_regs[13]) /* L1 color frame buffer address */
+#define LTDC_L1CFBLR (ldev->caps.layer_regs[14]) /* L1 color frame buffer length */
+#define LTDC_L1CFBLNR (ldev->caps.layer_regs[15]) /* L1 color frame buffer line number */
+#define LTDC_L1AFBA0R (ldev->caps.layer_regs[16]) /* L1 auxiliary frame buffer address 0 */
+#define LTDC_L1AFBA1R (ldev->caps.layer_regs[17]) /* L1 auxiliary frame buffer address 1 */
+#define LTDC_L1AFBLR (ldev->caps.layer_regs[18]) /* L1 auxiliary frame buffer length */
+#define LTDC_L1AFBLNR (ldev->caps.layer_regs[19]) /* L1 auxiliary frame buffer line number */
+#define LTDC_L1CLUTWR (ldev->caps.layer_regs[20]) /* L1 CLUT write */
+#define LTDC_L1CYR0R (ldev->caps.layer_regs[21]) /* L1 Conversion YCbCr RGB 0 */
+#define LTDC_L1CYR1R (ldev->caps.layer_regs[22]) /* L1 Conversion YCbCr RGB 1 */
+#define LTDC_L1FPF0R (ldev->caps.layer_regs[23]) /* L1 Flexible Pixel Format 0 */
+#define LTDC_L1FPF1R (ldev->caps.layer_regs[24]) /* L1 Flexible Pixel Format 1 */
/* Bit definitions */
#define SSCR_VSH GENMASK(10, 0) /* Vertical Synchronization Height */
@@ -114,6 +122,7 @@
#define GCR_LTDCEN BIT(0) /* LTDC ENable */
#define GCR_DEN BIT(16) /* Dither ENable */
+#define GCR_CRCEN BIT(19) /* CRC ENable */
#define GCR_PCPOL BIT(28) /* Pixel Clock POLarity-Inverted */
#define GCR_DEPOL BIT(29) /* Data Enable POLarity-High */
#define GCR_VSPOL BIT(30) /* Vertical Synchro POLarity-High */
@@ -164,6 +173,10 @@
#define ISR_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */
#define ISR_RRIF BIT(3) /* Register Reload Interrupt Flag */
+#define EDCR_OCYEN BIT(25) /* Output Conversion to YCbCr 422: ENable */
+#define EDCR_OCYSEL BIT(26) /* Output Conversion to YCbCr 422: SELection of the CCIR */
+#define EDCR_OCYCO BIT(27) /* Output Conversion to YCbCr 422: Chrominance Order */
+
#define LXCR_LEN BIT(0) /* Layer ENable */
#define LXCR_COLKEN BIT(1) /* Color Keying Enable */
#define LXCR_CLUTEN BIT(4) /* Color Look-Up Table ENable */
@@ -175,6 +188,7 @@
#define LXWVPCR_WVSPPOS GENMASK(26, 16) /* Window Vertical StoP POSition */
#define LXPFCR_PF GENMASK(2, 0) /* Pixel Format */
+#define PF_FLEXIBLE 0x7 /* Flexible Pixel Format selected */
#define LXCACR_CONSTA GENMASK(7, 0) /* CONSTant Alpha */
@@ -186,6 +200,25 @@
#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 */
+
#define CLUT_SIZE 256
#define CONSTA_MAX 0xFF /* CONSTant Alpha MAX= 1.0 */
@@ -196,13 +229,24 @@
#define NB_PF 8 /* Max nb of HW pixel format */
+/*
+ * Skip the first value and the second in case CRC was enabled during
+ * the thread irq. This is to be sure CRC value is relevant for the
+ * frame.
+ */
+#define CRC_SKIP_FRAMES 2
+
enum ltdc_pix_fmt {
PF_NONE,
/* RGB formats */
PF_ARGB8888, /* ARGB [32 bits] */
PF_RGBA8888, /* RGBA [32 bits] */
+ PF_ABGR8888, /* ABGR [32 bits] */
+ PF_BGRA8888, /* BGRA [32 bits] */
PF_RGB888, /* RGB [24 bits] */
+ PF_BGR888, /* BGR [24 bits] */
PF_RGB565, /* RGB [16 bits] */
+ PF_BGR565, /* BGR [16 bits] */
PF_ARGB1555, /* ARGB A:1 bit RGB:15 bits [16 bits] */
PF_ARGB4444, /* ARGB A:4 bits R/G/B: 4 bits each [16 bits] */
/* Indexed formats */
@@ -234,36 +278,198 @@ static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = {
PF_ARGB4444 /* 0x07 */
};
-static const u64 ltdc_format_modifiers[] = {
- DRM_FORMAT_MOD_LINEAR,
- DRM_FORMAT_MOD_INVALID
+static const enum ltdc_pix_fmt ltdc_pix_fmt_a2[NB_PF] = {
+ PF_ARGB8888, /* 0x00 */
+ PF_ABGR8888, /* 0x01 */
+ PF_RGBA8888, /* 0x02 */
+ PF_BGRA8888, /* 0x03 */
+ PF_RGB565, /* 0x04 */
+ PF_BGR565, /* 0x05 */
+ PF_RGB888, /* 0x06 */
+ PF_NONE /* 0x07 */
};
-static inline u32 reg_read(void __iomem *base, u32 reg)
-{
- return readl_relaxed(base + reg);
-}
+static const u32 ltdc_drm_fmt_a0[] = {
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_ARGB4444,
+ DRM_FORMAT_XRGB4444,
+ DRM_FORMAT_C8
+};
-static inline void reg_write(void __iomem *base, u32 reg, u32 val)
-{
- writel_relaxed(val, base + reg);
-}
+static const u32 ltdc_drm_fmt_a1[] = {
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_ARGB4444,
+ DRM_FORMAT_XRGB4444,
+ DRM_FORMAT_C8
+};
-static inline void reg_set(void __iomem *base, u32 reg, u32 mask)
-{
- reg_write(base, reg, reg_read(base, reg) | mask);
-}
+static const u32 ltdc_drm_fmt_a2[] = {
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_ABGR8888,
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_RGBA8888,
+ DRM_FORMAT_RGBX8888,
+ DRM_FORMAT_BGRA8888,
+ DRM_FORMAT_BGRX8888,
+ DRM_FORMAT_RGB565,
+ DRM_FORMAT_BGR565,
+ DRM_FORMAT_RGB888,
+ DRM_FORMAT_BGR888,
+ DRM_FORMAT_ARGB1555,
+ DRM_FORMAT_XRGB1555,
+ DRM_FORMAT_ARGB4444,
+ DRM_FORMAT_XRGB4444,
+ DRM_FORMAT_C8
+};
-static inline void reg_clear(void __iomem *base, u32 reg, u32 mask)
-{
- reg_write(base, reg, reg_read(base, reg) & ~mask);
-}
+static const u32 ltdc_drm_fmt_ycbcr_cp[] = {
+ DRM_FORMAT_YUYV,
+ DRM_FORMAT_YVYU,
+ DRM_FORMAT_UYVY,
+ DRM_FORMAT_VYUY
+};
-static inline void reg_update_bits(void __iomem *base, u32 reg, u32 mask,
- u32 val)
-{
- reg_write(base, reg, (reg_read(base, reg) & ~mask) | val);
-}
+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 */
+ 0x00, /* not available */
+ 0x00, /* not available */
+ 0x84, /* L1 control register */
+ 0x88, /* L1 window horizontal position configuration */
+ 0x8c, /* L1 window vertical position configuration */
+ 0x90, /* L1 color keying configuration */
+ 0x94, /* L1 pixel format configuration */
+ 0x98, /* L1 constant alpha configuration */
+ 0x9c, /* L1 default color configuration */
+ 0xa0, /* L1 blending factors configuration */
+ 0x00, /* not available */
+ 0x00, /* not available */
+ 0xac, /* L1 color frame buffer address */
+ 0xb0, /* L1 color frame buffer length */
+ 0xb4, /* L1 color frame buffer line number */
+ 0x00, /* not available */
+ 0x00, /* not available */
+ 0x00, /* not available */
+ 0x00, /* not available */
+ 0xc4, /* L1 CLUT write */
+ 0x00, /* not available */
+ 0x00, /* not available */
+ 0x00, /* not available */
+ 0x00 /* not available */
+};
+
+static const u32 ltdc_layer_regs_a1[] = {
+ 0x80, /* L1 configuration 0 */
+ 0x84, /* L1 configuration 1 */
+ 0x00, /* L1 reload control */
+ 0x88, /* L1 control register */
+ 0x8c, /* L1 window horizontal position configuration */
+ 0x90, /* L1 window vertical position configuration */
+ 0x94, /* L1 color keying configuration */
+ 0x98, /* L1 pixel format configuration */
+ 0x9c, /* L1 constant alpha configuration */
+ 0xa0, /* L1 default color configuration */
+ 0xa4, /* L1 blending factors configuration */
+ 0xa8, /* L1 burst length configuration */
+ 0x00, /* not available */
+ 0xac, /* L1 color frame buffer address */
+ 0xb0, /* L1 color frame buffer length */
+ 0xb4, /* L1 color frame buffer line number */
+ 0xb8, /* L1 auxiliary frame buffer address 0 */
+ 0xbc, /* L1 auxiliary frame buffer address 1 */
+ 0xc0, /* L1 auxiliary frame buffer length */
+ 0xc4, /* L1 auxiliary frame buffer line number */
+ 0xc8, /* L1 CLUT write */
+ 0x00, /* not available */
+ 0x00, /* not available */
+ 0x00, /* not available */
+ 0x00 /* not available */
+};
+
+static const u32 ltdc_layer_regs_a2[] = {
+ 0x100, /* L1 configuration 0 */
+ 0x104, /* L1 configuration 1 */
+ 0x108, /* L1 reload control */
+ 0x10c, /* L1 control register */
+ 0x110, /* L1 window horizontal position configuration */
+ 0x114, /* L1 window vertical position configuration */
+ 0x118, /* L1 color keying configuration */
+ 0x11c, /* L1 pixel format configuration */
+ 0x120, /* L1 constant alpha configuration */
+ 0x124, /* L1 default color configuration */
+ 0x128, /* L1 blending factors configuration */
+ 0x12c, /* L1 burst length configuration */
+ 0x130, /* L1 planar configuration */
+ 0x134, /* L1 color frame buffer address */
+ 0x138, /* L1 color frame buffer length */
+ 0x13c, /* L1 color frame buffer line number */
+ 0x140, /* L1 auxiliary frame buffer address 0 */
+ 0x144, /* L1 auxiliary frame buffer address 1 */
+ 0x148, /* L1 auxiliary frame buffer length */
+ 0x14c, /* L1 auxiliary frame buffer line number */
+ 0x150, /* L1 CLUT write */
+ 0x16c, /* L1 Conversion YCbCr RGB 0 */
+ 0x170, /* L1 Conversion YCbCr RGB 1 */
+ 0x174, /* L1 Flexible Pixel Format 0 */
+ 0x178 /* L1 Flexible Pixel Format 1 */
+};
+
+static const u64 ltdc_format_modifiers[] = {
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID
+};
+
+static const struct regmap_config stm32_ltdc_regmap_cfg = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = sizeof(u32),
+ .max_register = 0x400,
+ .use_relaxed_mmio = true,
+ .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)
{
@@ -289,16 +495,30 @@ static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
case DRM_FORMAT_XRGB8888:
pf = PF_ARGB8888;
break;
+ case DRM_FORMAT_ABGR8888:
+ case DRM_FORMAT_XBGR8888:
+ pf = PF_ABGR8888;
+ break;
case DRM_FORMAT_RGBA8888:
case DRM_FORMAT_RGBX8888:
pf = PF_RGBA8888;
break;
+ case DRM_FORMAT_BGRA8888:
+ case DRM_FORMAT_BGRX8888:
+ pf = PF_BGRA8888;
+ break;
case DRM_FORMAT_RGB888:
pf = PF_RGB888;
break;
+ case DRM_FORMAT_BGR888:
+ pf = PF_BGR888;
+ break;
case DRM_FORMAT_RGB565:
pf = PF_RGB565;
break;
+ case DRM_FORMAT_BGR565:
+ pf = PF_BGR565;
+ break;
case DRM_FORMAT_ARGB1555:
case DRM_FORMAT_XRGB1555:
pf = PF_ARGB1555;
@@ -319,49 +539,159 @@ static inline enum ltdc_pix_fmt to_ltdc_pixelformat(u32 drm_fmt)
return pf;
}
-static inline u32 to_drm_pixelformat(enum ltdc_pix_fmt pf)
+static inline u32 ltdc_set_flexible_pixel_format(struct drm_plane *plane, enum ltdc_pix_fmt pix_fmt)
{
- switch (pf) {
- case PF_ARGB8888:
- return DRM_FORMAT_ARGB8888;
- case PF_RGBA8888:
- return DRM_FORMAT_RGBA8888;
- case PF_RGB888:
- return DRM_FORMAT_RGB888;
- case PF_RGB565:
- return DRM_FORMAT_RGB565;
+ struct ltdc_device *ldev = plane_to_ltdc(plane);
+ u32 lofs = plane->index * LAY_OFS, ret = PF_FLEXIBLE;
+ int psize, alen, apos, rlen, rpos, glen, gpos, blen, bpos;
+
+ switch (pix_fmt) {
+ case PF_BGR888:
+ psize = 3;
+ alen = 0; apos = 0; rlen = 8; rpos = 0;
+ glen = 8; gpos = 8; blen = 8; bpos = 16;
+ break;
case PF_ARGB1555:
- return DRM_FORMAT_ARGB1555;
+ psize = 2;
+ alen = 1; apos = 15; rlen = 5; rpos = 10;
+ glen = 5; gpos = 5; blen = 5; bpos = 0;
+ break;
case PF_ARGB4444:
- return DRM_FORMAT_ARGB4444;
+ psize = 2;
+ alen = 4; apos = 12; rlen = 4; rpos = 8;
+ glen = 4; gpos = 4; blen = 4; bpos = 0;
+ break;
case PF_L8:
- return DRM_FORMAT_C8;
- case PF_AL44: /* No DRM support */
- case PF_AL88: /* No DRM support */
- case PF_NONE:
+ psize = 1;
+ alen = 0; apos = 0; rlen = 8; rpos = 0;
+ glen = 8; gpos = 0; blen = 8; bpos = 0;
+ break;
+ case PF_AL44:
+ psize = 1;
+ alen = 4; apos = 4; rlen = 4; rpos = 0;
+ glen = 4; gpos = 0; blen = 4; bpos = 0;
+ break;
+ case PF_AL88:
+ psize = 2;
+ alen = 8; apos = 8; rlen = 8; rpos = 0;
+ glen = 8; gpos = 0; blen = 8; bpos = 0;
+ break;
default:
- return 0;
+ ret = NB_PF; /* error case, trace msg is handled by the caller */
+ break;
}
+
+ if (ret == PF_FLEXIBLE) {
+ regmap_write(ldev->regmap, LTDC_L1FPF0R + lofs,
+ (rlen << 14) + (rpos << 9) + (alen << 5) + apos);
+
+ regmap_write(ldev->regmap, LTDC_L1FPF1R + lofs,
+ (psize << 18) + (blen << 14) + (bpos << 9) + (glen << 5) + gpos);
+ }
+
+ return ret;
}
-static inline u32 get_pixelformat_without_alpha(u32 drm)
+/*
+ * All non-alpha color formats derived from native alpha color formats are
+ * either characterized by a FourCC format code
+ */
+static inline u32 is_xrgb(u32 drm)
{
- switch (drm) {
- case DRM_FORMAT_ARGB4444:
- return DRM_FORMAT_XRGB4444;
- case DRM_FORMAT_RGBA4444:
- return DRM_FORMAT_RGBX4444;
- case DRM_FORMAT_ARGB1555:
- return DRM_FORMAT_XRGB1555;
- case DRM_FORMAT_RGBA5551:
- return DRM_FORMAT_RGBX5551;
- case DRM_FORMAT_ARGB8888:
- return DRM_FORMAT_XRGB8888;
- case DRM_FORMAT_RGBA8888:
- return DRM_FORMAT_RGBX8888;
+ 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:
- return 0;
+ /* RGB or not a YCbCr supported format */
+ DRM_ERROR("Unsupported pixel format: %u\n", drm_pix_fmt);
+ return;
+ }
+
+ /* 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 inline void ltdc_irq_crc_handle(struct ltdc_device *ldev,
+ struct drm_crtc *crtc)
+{
+ u32 crc;
+ int ret;
+
+ if (ldev->crc_skip_count < CRC_SKIP_FRAMES) {
+ ldev->crc_skip_count++;
+ return;
}
+
+ /* Get the CRC of the frame */
+ ret = regmap_read(ldev->regmap, LTDC_CCRCR, &crc);
+ if (ret)
+ return;
+
+ /* Report to DRM the CRC (hw dependent feature) */
+ drm_crtc_add_crc_entry(crtc, true, drm_crtc_accurate_vblank_count(crtc), &crc);
}
static irqreturn_t ltdc_irq_thread(int irq, void *arg)
@@ -371,9 +701,14 @@ static irqreturn_t ltdc_irq_thread(int irq, void *arg)
struct drm_crtc *crtc = drm_crtc_from_index(ddev, 0);
/* Line IRQ : trigger the vblank event */
- if (ldev->irq_status & ISR_LIF)
+ if (ldev->irq_status & ISR_LIF) {
drm_crtc_handle_vblank(crtc);
+ /* Early return if CRC is not active */
+ if (ldev->crc_active)
+ ltdc_irq_crc_handle(ldev, crtc);
+ }
+
/* Save FIFO Underrun & Transfer Error status */
mutex_lock(&ldev->err_lock);
if (ldev->irq_status & ISR_FUIF)
@@ -390,9 +725,13 @@ static irqreturn_t ltdc_irq(int irq, void *arg)
struct drm_device *ddev = arg;
struct ltdc_device *ldev = ddev->dev_private;
- /* Read & Clear the interrupt status */
- ldev->irq_status = reg_read(ldev->regs, LTDC_ISR);
- reg_write(ldev->regs, LTDC_ICR, ldev->irq_status);
+ /*
+ * Read & Clear the interrupt status
+ * In order to write / read registers in this critical section
+ * very quickly, the regmap functions are not used.
+ */
+ ldev->irq_status = readl_relaxed(ldev->regs + LTDC_ISR);
+ writel_relaxed(ldev->irq_status, ldev->regs + LTDC_ICR);
return IRQ_WAKE_THREAD;
}
@@ -416,7 +755,7 @@ static void ltdc_crtc_update_clut(struct drm_crtc *crtc)
for (i = 0; i < CLUT_SIZE; i++, lut++) {
val = ((lut->red << 8) & 0xff0000) | (lut->green & 0xff00) |
(lut->blue >> 8) | (i << 24);
- reg_write(ldev->regs, LTDC_L1CLUTWR, val);
+ regmap_write(ldev->regmap, LTDC_L1CLUTWR, val);
}
}
@@ -431,13 +770,14 @@ static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc,
pm_runtime_get_sync(ddev->dev);
/* Sets the background color value */
- reg_write(ldev->regs, LTDC_BCCR, BCCR_BCBLACK);
+ regmap_write(ldev->regmap, LTDC_BCCR, BCCR_BCBLACK);
/* Enable IRQ */
- reg_set(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+ regmap_set_bits(ldev->regmap, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
/* Commit shadow registers = update planes at next vblank */
- reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR);
+ if (!ldev->caps.plane_reg_shadow)
+ regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
drm_crtc_vblank_on(crtc);
}
@@ -453,10 +793,11 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc,
drm_crtc_vblank_off(crtc);
/* disable IRQ */
- reg_clear(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
+ regmap_clear_bits(ldev->regmap, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE);
/* immediately commit disable of layers before switching off LTDC */
- reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR);
+ if (!ldev->caps.plane_reg_shadow)
+ regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_IMR);
pm_runtime_put_sync(ddev->dev);
}
@@ -533,6 +874,7 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h;
u32 total_width, total_height;
+ u32 bus_formats = MEDIA_BUS_FMT_RGB888_1X24;
u32 bus_flags = 0;
u32 val;
int ret;
@@ -558,8 +900,11 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
if (bridge && bridge->timings)
bus_flags = bridge->timings->input_bus_flags;
- else if (connector)
+ else if (connector) {
bus_flags = connector->display_info.bus_flags;
+ if (connector->display_info.num_bus_formats)
+ bus_formats = connector->display_info.bus_formats[0];
+ }
if (!pm_runtime_active(ddev->dev)) {
ret = pm_runtime_get_sync(ddev->dev);
@@ -604,26 +949,56 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
val |= GCR_PCPOL;
- reg_update_bits(ldev->regs, LTDC_GCR,
- GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
+ regmap_update_bits(ldev->regmap, LTDC_GCR,
+ GCR_HSPOL | GCR_VSPOL | GCR_DEPOL | GCR_PCPOL, val);
/* Set Synchronization size */
val = (hsync << 16) | vsync;
- reg_update_bits(ldev->regs, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
+ regmap_update_bits(ldev->regmap, LTDC_SSCR, SSCR_VSH | SSCR_HSW, val);
/* Set Accumulated Back porch */
val = (accum_hbp << 16) | accum_vbp;
- reg_update_bits(ldev->regs, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
+ regmap_update_bits(ldev->regmap, LTDC_BPCR, BPCR_AVBP | BPCR_AHBP, val);
/* Set Accumulated Active Width */
val = (accum_act_w << 16) | accum_act_h;
- reg_update_bits(ldev->regs, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
+ regmap_update_bits(ldev->regmap, LTDC_AWCR, AWCR_AAW | AWCR_AAH, val);
/* Set total width & height */
val = (total_width << 16) | total_height;
- reg_update_bits(ldev->regs, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
+ regmap_update_bits(ldev->regmap, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
- reg_write(ldev->regs, LTDC_LIPCR, (accum_act_h + 1));
+ regmap_write(ldev->regmap, LTDC_LIPCR, (accum_act_h + 1));
+
+ /* Configure the output format (hw version dependent) */
+ if (ldev->caps.ycbcr_output) {
+ /* Input video dynamic_range & colorimetry */
+ int vic = drm_match_cea_mode(mode);
+ u32 val;
+
+ if (vic == 6 || vic == 7 || vic == 21 || vic == 22 ||
+ vic == 2 || vic == 3 || vic == 17 || vic == 18)
+ /* ITU-R BT.601 */
+ val = 0;
+ else
+ /* ITU-R BT.709 */
+ val = EDCR_OCYSEL;
+
+ switch (bus_formats) {
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ /* enable ycbcr output converter */
+ regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | val);
+ break;
+ case MEDIA_BUS_FMT_YVYU8_1X16:
+ /* enable ycbcr output converter & invert chrominance order */
+ regmap_write(ldev->regmap, LTDC_EDCR, EDCR_OCYEN | EDCR_OCYCO | val);
+ break;
+ default:
+ /* disable ycbcr output converter */
+ regmap_write(ldev->regmap, LTDC_EDCR, 0);
+ break;
+ }
+ }
}
static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
@@ -638,7 +1013,8 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc,
ltdc_crtc_update_clut(crtc);
/* Commit shadow registers = update planes at next vblank */
- reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR);
+ if (!ldev->caps.plane_reg_shadow)
+ regmap_set_bits(ldev->regmap, LTDC_SRCR, SRCR_VBR);
if (event) {
crtc->state->event = NULL;
@@ -680,10 +1056,14 @@ static bool ltdc_crtc_get_scanout_position(struct drm_crtc *crtc,
* simplify the code and only test if line > vactive_end
*/
if (pm_runtime_active(ddev->dev)) {
- line = reg_read(ldev->regs, LTDC_CPSR) & CPSR_CYPOS;
- vactive_start = reg_read(ldev->regs, LTDC_BPCR) & BPCR_AVBP;
- vactive_end = reg_read(ldev->regs, LTDC_AWCR) & AWCR_AAH;
- vtotal = reg_read(ldev->regs, LTDC_TWCR) & TWCR_TOTALH;
+ regmap_read(ldev->regmap, LTDC_CPSR, &line);
+ line &= CPSR_CYPOS;
+ regmap_read(ldev->regmap, LTDC_BPCR, &vactive_start);
+ vactive_start &= BPCR_AVBP;
+ regmap_read(ldev->regmap, LTDC_AWCR, &vactive_end);
+ vactive_end &= AWCR_AAH;
+ regmap_read(ldev->regmap, LTDC_TWCR, &vtotal);
+ vtotal &= TWCR_TOTALH;
if (line > vactive_end)
*vpos = line - vtotal - vactive_start;
@@ -719,7 +1099,7 @@ static int ltdc_crtc_enable_vblank(struct drm_crtc *crtc)
DRM_DEBUG_DRIVER("\n");
if (state->enable)
- reg_set(ldev->regs, LTDC_IER, IER_LIE);
+ regmap_set_bits(ldev->regmap, LTDC_IER, IER_LIE);
else
return -EPERM;
@@ -731,7 +1111,49 @@ static void ltdc_crtc_disable_vblank(struct drm_crtc *crtc)
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
DRM_DEBUG_DRIVER("\n");
- reg_clear(ldev->regs, LTDC_IER, IER_LIE);
+ regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE);
+}
+
+static int ltdc_crtc_set_crc_source(struct drm_crtc *crtc, const char *source)
+{
+ struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+ int ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ if (!crtc)
+ return -ENODEV;
+
+ if (source && strcmp(source, "auto") == 0) {
+ ldev->crc_active = true;
+ ret = regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
+ } else if (!source) {
+ ldev->crc_active = false;
+ ret = regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_CRCEN);
+ } else {
+ ret = -EINVAL;
+ }
+
+ ldev->crc_skip_count = 0;
+ return ret;
+}
+
+static int ltdc_crtc_verify_crc_source(struct drm_crtc *crtc,
+ const char *source, size_t *values_cnt)
+{
+ DRM_DEBUG_DRIVER("\n");
+
+ if (!crtc)
+ return -ENODEV;
+
+ if (source && strcmp(source, "auto") != 0) {
+ DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n",
+ source, crtc->name);
+ return -EINVAL;
+ }
+
+ *values_cnt = 1;
+ return 0;
}
static const struct drm_crtc_funcs ltdc_crtc_funcs = {
@@ -746,6 +1168,20 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
};
+static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = {
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .reset = drm_atomic_helper_crtc_reset,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ .enable_vblank = ltdc_crtc_enable_vblank,
+ .disable_vblank = ltdc_crtc_disable_vblank,
+ .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
+ .set_crc_source = ltdc_crtc_set_crc_source,
+ .verify_crc_source = ltdc_crtc_verify_crc_source,
+};
+
/*
* DRM_PLANE
*/
@@ -789,7 +1225,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) {
@@ -809,19 +1245,20 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
newstate->crtc_w, newstate->crtc_h,
newstate->crtc_x, newstate->crtc_y);
- bpcr = reg_read(ldev->regs, LTDC_BPCR);
+ regmap_read(ldev->regmap, LTDC_BPCR, &bpcr);
+
ahbp = (bpcr & BPCR_AHBP) >> 16;
avbp = bpcr & BPCR_AVBP;
/* Configures the horizontal start and stop position */
val = ((x1 + 1 + ahbp) << 16) + (x0 + 1 + ahbp);
- reg_update_bits(ldev->regs, LTDC_L1WHPCR + lofs,
- LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
+ regmap_write_bits(ldev->regmap, LTDC_L1WHPCR + lofs,
+ LXWHPCR_WHSTPOS | LXWHPCR_WHSPPOS, val);
/* Configures the vertical start and stop position */
val = ((y1 + 1 + avbp) << 16) + (y0 + 1 + avbp);
- reg_update_bits(ldev->regs, LTDC_L1WVPCR + lofs,
- LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
+ regmap_write_bits(ldev->regmap, LTDC_L1WVPCR + lofs,
+ LXWVPCR_WVSTPOS | LXWVPCR_WVSPPOS, val);
/* Specifies the pixel format */
pf = to_ltdc_pixelformat(fb->format->format);
@@ -829,24 +1266,27 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
if (ldev->caps.pix_fmt_hw[val] == pf)
break;
+ /* Use the flexible color format feature if necessary and available */
+ if (ldev->caps.pix_fmt_flex && val == NB_PF)
+ val = ltdc_set_flexible_pixel_format(plane, pf);
+
if (val == NB_PF) {
DRM_ERROR("Pixel format %.4s not supported\n",
(char *)&fb->format->format);
val = 0; /* set by default ARGB 32 bits */
}
- reg_update_bits(ldev->regs, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
+ regmap_write_bits(ldev->regmap, LTDC_L1PFCR + lofs, LXPFCR_PF, val);
/* Configures the color frame buffer pitch in bytes & line length */
pitch_in_bytes = fb->pitches[0];
line_length = fb->format->cpp[0] *
(x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1;
val = ((pitch_in_bytes << 16) | line_length);
- reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs,
- LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
+ regmap_write_bits(ldev->regmap, LTDC_L1CFBLR + lofs, LXCFBLR_CFBLL | LXCFBLR_CFBP, val);
/* Specifies the constant alpha value */
val = newstate->alpha >> 8;
- reg_update_bits(ldev->regs, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
+ regmap_write_bits(ldev->regmap, LTDC_L1CACR + lofs, LXCACR_CONSTA, val);
/* Specifies the blending factors */
val = BF1_PAXCA | BF2_1PAXCA;
@@ -858,24 +1298,98 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane,
plane->type != DRM_PLANE_TYPE_PRIMARY)
val = BF1_PAXCA | BF2_1PAXCA;
- reg_update_bits(ldev->regs, LTDC_L1BFCR + lofs,
- LXBFCR_BF2 | LXBFCR_BF1, val);
+ regmap_write_bits(ldev->regmap, LTDC_L1BFCR + lofs, LXBFCR_BF2 | LXBFCR_BF1, val);
/* Configures the frame buffer line number */
- val = y1 - y0 + 1;
- reg_update_bits(ldev->regs, 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);
DRM_DEBUG_DRIVER("fb: phys 0x%08x", paddr);
- reg_write(ldev->regs, LTDC_L1CFBAR + lofs, 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;
- reg_update_bits(ldev->regs, LTDC_L1CR + lofs,
- LXCR_LEN | LXCR_CLUTEN, val);
+ regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN | LXCR_CLUTEN, val);
+
+ /* Commit shadow registers = update plane at next vblank */
+ if (ldev->caps.plane_reg_shadow)
+ regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
+ LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
ldev->plane_fpsi[plane->index].counter++;
@@ -900,7 +1414,12 @@ static void ltdc_plane_atomic_disable(struct drm_plane *plane,
u32 lofs = plane->index * LAY_OFS;
/* disable layer */
- reg_clear(ldev->regs, LTDC_L1CR + lofs, LXCR_LEN);
+ regmap_write_bits(ldev->regmap, LTDC_L1CR + lofs, LXCR_LEN, 0);
+
+ /* Commit shadow registers = update plane at next vblank */
+ if (ldev->caps.plane_reg_shadow)
+ regmap_write_bits(ldev->regmap, LTDC_L1RCR + lofs,
+ LXRCR_IMR | LXRCR_VBR | LXRCR_GRMSK, LXRCR_VBR);
DRM_DEBUG_DRIVER("CRTC:%d plane:%d\n",
oldstate->crtc->base.id, plane->base.id);
@@ -925,16 +1444,6 @@ static void ltdc_plane_atomic_print_state(struct drm_printer *p,
fpsi->counter = 0;
}
-static bool ltdc_plane_format_mod_supported(struct drm_plane *plane,
- u32 format,
- u64 modifier)
-{
- if (modifier == DRM_FORMAT_MOD_LINEAR)
- return true;
-
- return false;
-}
-
static const struct drm_plane_funcs ltdc_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
@@ -943,7 +1452,6 @@ static const struct drm_plane_funcs ltdc_plane_funcs = {
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
.atomic_print_state = ltdc_plane_atomic_print_state,
- .format_mod_supported = ltdc_plane_format_mod_supported,
};
static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = {
@@ -953,36 +1461,58 @@ 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;
struct device *dev = ddev->dev;
struct drm_plane *plane;
unsigned int i, nb_fmt = 0;
- u32 formats[NB_PF * 2];
- u32 drm_fmt, drm_fmt_no_alpha;
+ u32 *formats;
+ u32 drm_fmt;
const u64 *modifiers = ltdc_format_modifiers;
+ u32 lofs = index * LAY_OFS;
+ u32 val;
int ret;
- /* Get supported pixel formats */
- for (i = 0; i < NB_PF; i++) {
- drm_fmt = to_drm_pixelformat(ldev->caps.pix_fmt_hw[i]);
- if (!drm_fmt)
- continue;
- formats[nb_fmt++] = drm_fmt;
+ /* 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);
- /* Add the no-alpha related format if any & supported */
- drm_fmt_no_alpha = get_pixelformat_without_alpha(drm_fmt);
- if (!drm_fmt_no_alpha)
- continue;
+ for (i = 0; i < ldev->caps.pix_fmt_nb; i++) {
+ drm_fmt = ldev->caps.pix_fmt_drm[i];
/* Manage hw-specific capabilities */
- if (ldev->caps.non_alpha_only_l1 &&
- type != DRM_PLANE_TYPE_PRIMARY)
- continue;
+ if (ldev->caps.non_alpha_only_l1)
+ /* XR24 & RX24 like formats supported only on primary layer */
+ if (type != DRM_PLANE_TYPE_PRIMARY && is_xrgb(drm_fmt))
+ continue;
+
+ formats[nb_fmt++] = drm_fmt;
+ }
- formats[nb_fmt++] = drm_fmt_no_alpha;
+ /* 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);
@@ -995,6 +1525,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, &ltdc_plane_helper_funcs);
drm_plane_create_alpha_property(plane);
@@ -1020,7 +1561,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;
@@ -1028,8 +1569,13 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc)
drm_plane_create_zpos_immutable_property(primary, 0);
- ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
- &ltdc_crtc_funcs, NULL);
+ /* Init CRTC according to its hardware features */
+ if (ldev->caps.crc)
+ ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
+ &ltdc_crtc_with_crc_support_funcs, NULL);
+ else
+ ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL,
+ &ltdc_crtc_funcs, NULL);
if (ret) {
DRM_ERROR("Can not initialize CRTC\n");
goto cleanup;
@@ -1044,7 +1590,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);
@@ -1068,7 +1614,7 @@ static void ltdc_encoder_disable(struct drm_encoder *encoder)
DRM_DEBUG_DRIVER("\n");
/* Disable LTDC */
- reg_clear(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+ regmap_clear_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
/* Set to sleep state the pinctrl whatever type of encoder */
pinctrl_pm_select_sleep_state(ddev->dev);
@@ -1082,7 +1628,7 @@ static void ltdc_encoder_enable(struct drm_encoder *encoder)
DRM_DEBUG_DRIVER("\n");
/* Enable LTDC */
- reg_set(ldev->regs, LTDC_GCR, GCR_LTDCEN);
+ regmap_set_bits(ldev->regmap, LTDC_GCR, GCR_LTDCEN);
}
static void ltdc_encoder_mode_set(struct drm_encoder *encoder,
@@ -1145,21 +1691,25 @@ static int ltdc_get_caps(struct drm_device *ddev)
* at least 1 layer must be managed & the number of layers
* must not exceed LTDC_MAX_LAYER
*/
- lcr = reg_read(ldev->regs, LTDC_LCR);
+ regmap_read(ldev->regmap, LTDC_LCR, &lcr);
ldev->caps.nb_layers = clamp((int)lcr, 1, LTDC_MAX_LAYER);
/* set data bus width */
- gc2r = reg_read(ldev->regs, LTDC_GC2R);
+ regmap_read(ldev->regmap, LTDC_GC2R, &gc2r);
bus_width_log2 = (gc2r & GC2R_BW) >> 4;
ldev->caps.bus_width = 8 << bus_width_log2;
- ldev->caps.hw_version = reg_read(ldev->regs, LTDC_IDR);
+ regmap_read(ldev->regmap, LTDC_IDR, &ldev->caps.hw_version);
switch (ldev->caps.hw_version) {
case HWVER_10200:
case HWVER_10300:
- ldev->caps.reg_ofs = REG_OFS_NONE;
+ ldev->caps.layer_ofs = LAY_OFS_0;
+ ldev->caps.layer_regs = ltdc_layer_regs_a0;
ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a0;
+ ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a0;
+ ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a0);
+ ldev->caps.pix_fmt_flex = false;
/*
* Hw older versions support non-alpha color formats derived
* from native alpha color formats only on the primary layer.
@@ -1172,13 +1722,40 @@ 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;
+ ldev->caps.crc = false;
break;
case HWVER_20101:
- ldev->caps.reg_ofs = REG_OFS_4;
+ ldev->caps.layer_ofs = LAY_OFS_0;
+ ldev->caps.layer_regs = ltdc_layer_regs_a1;
ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a1;
+ ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a1;
+ ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a1);
+ ldev->caps.pix_fmt_flex = false;
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;
+ ldev->caps.crc = false;
+ break;
+ case HWVER_40100:
+ ldev->caps.layer_ofs = LAY_OFS_1;
+ ldev->caps.layer_regs = ltdc_layer_regs_a2;
+ ldev->caps.pix_fmt_hw = ltdc_pix_fmt_a2;
+ ldev->caps.pix_fmt_drm = ltdc_drm_fmt_a2;
+ ldev->caps.pix_fmt_nb = ARRAY_SIZE(ltdc_drm_fmt_a2);
+ ldev->caps.pix_fmt_flex = true;
+ 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;
+ ldev->caps.crc = true;
break;
default:
return -ENODEV;
@@ -1296,9 +1873,15 @@ int ltdc_load(struct drm_device *ddev)
goto err;
}
+ ldev->regmap = devm_regmap_init_mmio(&pdev->dev, ldev->regs, &stm32_ltdc_regmap_cfg);
+ if (IS_ERR(ldev->regmap)) {
+ DRM_ERROR("Unable to regmap ltdc registers\n");
+ ret = PTR_ERR(ldev->regmap);
+ goto err;
+ }
+
/* Disable interrupts */
- reg_clear(ldev->regs, LTDC_IER,
- IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
+ regmap_clear_bits(ldev->regmap, LTDC_IER, IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE);
ret = ltdc_get_caps(ddev);
if (ret) {