[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20190520133753.23871-4-narmstrong@baylibre.com>
Date: Mon, 20 May 2019 15:37:51 +0200
From: Neil Armstrong <narmstrong@...libre.com>
To: a.hajda@...sung.com, Laurent.pinchart@...asonboard.com
Cc: jonas@...boo.se, hverkuil@...all.nl,
Neil Armstrong <narmstrong@...libre.com>,
dri-devel@...ts.freedesktop.org, jernej.skrabec@...l.net,
heiko@...ech.de, maxime.ripard@...tlin.com, hjc@...k-chips.com,
linux-amlogic@...ts.infradead.org, linux-kernel@...r.kernel.org
Subject: [PATCH 3/5] drm/bridge: dw-hdmi: Add support for dynamic output format setup
In order to support the HDMI2.0 YUV420, YUV422 and the 10bit, 12bit and
16bits outpu use cases, add support for the recently introduced bridge
callback format_set().
This callback will setup the new input format and encoding from encoder,
then these information will be used instead of the default ones
in the dw_hdmi_setup() function.
To determine the output bus format, has been added :
- support for the connector display_info bus_formats, where a fixed
output bus format can be enforced by the encoder
- support for synami output bus format depending on the input format,
especially the YUV420 input bus format, enforcing YUV420 as output
with the correct bit depth
Signed-off-by: Neil Armstrong <narmstrong@...libre.com>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 121 ++++++++++++++++++++--
1 file changed, 112 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index b50c49caf7ae..284ce59be8f8 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -103,6 +103,8 @@ struct hdmi_vmode {
};
struct hdmi_data_info {
+ unsigned int bridge_in_bus_format;
+ unsigned int bridge_in_encoding;
unsigned int enc_in_bus_format;
unsigned int enc_out_bus_format;
unsigned int enc_in_encoding;
@@ -1838,8 +1840,51 @@ static void hdmi_disable_overflow_interrupts(struct dw_hdmi *hdmi)
HDMI_IH_MUTE_FC_STAT2);
}
+/*
+ * The DW-HDMI CSC can only interpolate and decimate from 4:2:2 to 4:4:4/RGB
+ * and from 4:4:4/RGB to 4:2:2.
+ * Default to RGB output except if 4:2:0 as input, which CSC cannot convert.
+ */
+static unsigned long dw_hdmi_determine_output_bus_format(struct dw_hdmi *hdmi)
+{
+ unsigned int depth = hdmi_bus_fmt_color_depth(
+ hdmi->hdmi_data.enc_in_bus_format);
+ bool is_420 = hdmi_bus_fmt_is_yuv420(hdmi->hdmi_data.enc_in_bus_format);
+ unsigned long fmt = MEDIA_BUS_FMT_RGB888_1X24;
+
+ switch (depth) {
+ case 8:
+ if (is_420)
+ fmt = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+ else
+ fmt = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ case 10:
+ if (is_420)
+ fmt = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+ else
+ fmt = MEDIA_BUS_FMT_RGB101010_1X30;
+ break;
+ case 12:
+ if (is_420)
+ fmt = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
+ else
+ fmt = MEDIA_BUS_FMT_RGB121212_1X36;
+ break;
+ case 16:
+ if (is_420)
+ fmt = MEDIA_BUS_FMT_UYYVYY16_0_5X48;
+ else
+ fmt = MEDIA_BUS_FMT_RGB161616_1X48;
+ break;
+ }
+
+ return fmt;
+}
+
static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
{
+ struct drm_display_info *display = &hdmi->connector.display_info;
int ret;
hdmi_disable_overflow_interrupts(hdmi);
@@ -1853,9 +1898,9 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
}
if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
- (hdmi->vic == 21) || (hdmi->vic == 22) ||
- (hdmi->vic == 2) || (hdmi->vic == 3) ||
- (hdmi->vic == 17) || (hdmi->vic == 18))
+ (hdmi->vic == 21) || (hdmi->vic == 22) ||
+ (hdmi->vic == 2) || (hdmi->vic == 3) ||
+ (hdmi->vic == 17) || (hdmi->vic == 18))
hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601;
else
hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709;
@@ -1863,22 +1908,29 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0;
- /* TOFIX: Get input format from plat data or fallback to RGB888 */
- if (hdmi->plat_data->input_bus_format)
+ if (hdmi->hdmi_data.bridge_in_bus_format)
+ hdmi->hdmi_data.enc_in_bus_format =
+ hdmi->hdmi_data.bridge_in_bus_format;
+ else if (hdmi->plat_data->input_bus_format)
hdmi->hdmi_data.enc_in_bus_format =
hdmi->plat_data->input_bus_format;
else
hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
- /* TOFIX: Get input encoding from plat data or fallback to none */
- if (hdmi->plat_data->input_bus_encoding)
+ if (hdmi->hdmi_data.bridge_in_encoding)
+ hdmi->hdmi_data.enc_in_encoding =
+ hdmi->hdmi_data.bridge_in_encoding;
+ else if (hdmi->plat_data->input_bus_encoding)
hdmi->hdmi_data.enc_in_encoding =
hdmi->plat_data->input_bus_encoding;
else
hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT;
- /* TOFIX: Default to RGB888 output format */
- hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+ if (display->num_bus_formats)
+ hdmi->hdmi_data.enc_out_bus_format = display->bus_formats[0];
+ else
+ hdmi->hdmi_data.enc_out_bus_format =
+ dw_hdmi_determine_output_bus_format(hdmi);
hdmi->hdmi_data.pix_repet_factor = 0;
hdmi->hdmi_data.hdcp_enable = 0;
@@ -2150,6 +2202,56 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
return mode_status;
}
+static bool dw_hdmi_drm_bridge_format_set(struct drm_bridge *bridge,
+ const u32 input_bus_format,
+ const u32 input_encoding)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ /* Filter supported input bus formats */
+ switch (input_bus_format) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_RGB121212_1X36:
+ case MEDIA_BUS_FMT_RGB161616_1X48:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ case MEDIA_BUS_FMT_YUV16_1X48:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ case MEDIA_BUS_FMT_UYVY12_1X24:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+ break;
+ default:
+ dev_dbg(hdmi->dev, "Unsupported Input bus format %x\n",
+ input_bus_format);
+ return false;
+ }
+
+ /* Filter supported input bus encoding */
+ switch (input_encoding) {
+ case V4L2_YCBCR_ENC_DEFAULT:
+ case V4L2_YCBCR_ENC_601:
+ case V4L2_YCBCR_ENC_709:
+ case V4L2_YCBCR_ENC_XV601:
+ case V4L2_YCBCR_ENC_XV709:
+ break;
+ default:
+ dev_dbg(hdmi->dev, "Unsupported Input encoding %x\n",
+ input_bus_format);
+ return false;
+ }
+
+ hdmi->hdmi_data.bridge_in_bus_format = input_bus_format;
+ hdmi->hdmi_data.bridge_in_encoding = input_encoding;
+
+ return true;
+}
+
static void dw_hdmi_bridge_mode_set(struct drm_bridge *bridge,
const struct drm_display_mode *orig_mode,
const struct drm_display_mode *mode)
@@ -2192,6 +2294,7 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
.disable = dw_hdmi_bridge_disable,
.mode_set = dw_hdmi_bridge_mode_set,
.mode_valid = dw_hdmi_bridge_mode_valid,
+ .format_set = dw_hdmi_drm_bridge_format_set,
};
static irqreturn_t dw_hdmi_i2c_irq(struct dw_hdmi *hdmi)
--
2.21.0
Powered by blists - more mailing lists