[PATCH 2/5] drm/stm: ltdc: add YCbCr 422 output support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



LTDC 40100 hw version supports the YCbCr 422 output,
reducing the output pins from 24 to 16. This feature
is useful for some external devices like HDMI bridges.

Both ITU-R BT.601 & ITU-R BT.709 are supported.

It is also possible to choose the chrominance order between
* Cb is output first (Y0Cb, then Y1Cr, Y2Cb and so on).
* Cr is output first (Y0Cr, then Y1Cb, Y2Cr and so on).

Signed-off-by: Yannick Fertre <yannick.fertre@xxxxxxxxxxx>
---
 drivers/gpu/drm/stm/ltdc.c | 44 +++++++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/stm/ltdc.h |  1 +
 2 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 8dad3d00aa5c..b819f4cbcc3d 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -76,6 +76,7 @@
 #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_FUT	0x0090		/* Fifo underrun Threshold */
 
 /* Layer register offsets */
@@ -170,6 +171,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 */
@@ -625,6 +630,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;
@@ -650,8 +656,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);
@@ -716,6 +725,36 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc)
 	regmap_update_bits(ldev->regmap, LTDC_TWCR, TWCR_TOTALH | TWCR_TOTALW, val);
 
 	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,
@@ -1267,6 +1306,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_output = false;
 		break;
 	case HWVER_20101:
 		ldev->caps.layer_ofs = LAY_OFS_0;
@@ -1275,6 +1315,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_output = false;
 		break;
 	case HWVER_40100:
 		ldev->caps.layer_ofs = LAY_OFS_1;
@@ -1283,6 +1324,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_output = true;
 		break;
 	default:
 		return -ENODEV;
diff --git a/drivers/gpu/drm/stm/ltdc.h b/drivers/gpu/drm/stm/ltdc.h
index 20b3dcc7817b..f04fcebb5223 100644
--- a/drivers/gpu/drm/stm/ltdc.h
+++ b/drivers/gpu/drm/stm/ltdc.h
@@ -21,6 +21,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_output;	/* ycbcr output converter supported */
 };
 
 #define LTDC_MAX_LAYER	4
-- 
2.17.1




[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux