2020-02-06 19:19:51

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 00/11] drm/bridge: dw-hdmi: implement bus-format negotiation and YUV420 support

This patchset is based on Boris's v10 "drm: Add support for bus-format negotiation" at [1]
patchset to implement full bus-format negotiation for DW-HDMI, including YUV420 support and
10/12/16bit YUV444, YUV422 and RGB. The Color Space Converter support is already implemented.

And the counterpart implementation in the Amlogic Meson VPU dw-hdmi glue :
- basic bus-format negotiation to select YUV444 bus-format as DW-HDMI input
- YUV420 support when HDMI2.0 YUV420 modeset

This is a follow-up from the previous attempts :
- "drm/meson: Add support for HDMI2.0 YUV420 4k60" at [2]
- "drm/meson: Add support for HDMI2.0 4k60" at [3]

Changes sincd v3 at [6]:
- Added "Plug atomic state hooks to the default implementation" on drm/bridge: dw-hdmi
- Also added these atomic state hooks in meson-dw-hdmi in patch 7
- Rebased on latest drm-misc-next including patches 1-7 of [1]

Changes since RFC v2 at [5]:
- Added fixes from Jonas, who tested and integrated it for Rockchip SoCs
- Added support for 10/12/16bit tmds clock calculation
- Added support for max_bcp connector property
- Adapted to Boris's v4 patchset
- Fixed typos reported by boris

Changes since RFC v1 at [4]:
- Rewrote negociation using the v2 patchset, including full DW-HDMI fmt negociation

[1] https://patchwork.freedesktop.org/patch/msgid/[email protected]
[2] https://patchwork.freedesktop.org/patch/msgid/[email protected]
[3] https://patchwork.freedesktop.org/patch/msgid/[email protected]
[4] https://patchwork.freedesktop.org/patch/msgid/[email protected]
[5] https://patchwork.freedesktop.org/patch/msgid/[email protected]
[6] https://patchwork.freedesktop.org/patch/msgid/[email protected]

Jonas Karlman (2):
drm/bridge: dw-hdmi: set mtmdsclock for deep color
drm/bridge: dw-hdmi: add max bpc connector property

Neil Armstrong (9):
drm/bridge: dw-hdmi: Plug atomic state hooks to the default
implementation
drm/bridge: synopsys: dw-hdmi: add bus format negociation
drm/bridge: synopsys: dw-hdmi: allow ycbcr420 modes for >= 0x200a
drm/meson: venc: make drm_display_mode const
drm/meson: meson_dw_hdmi: add bridge and switch to drm_bridge_funcs
drm/meson: dw-hdmi: stop enforcing input_bus_format
drm/meson: venc: add support for YUV420 setup
drm/meson: vclk: add support for YUV420 setup
drm/meson: Add YUV420 output support

drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 302 +++++++++++++++++++++-
drivers/gpu/drm/meson/meson_dw_hdmi.c | 200 +++++++++++---
drivers/gpu/drm/meson/meson_vclk.c | 93 +++++--
drivers/gpu/drm/meson/meson_vclk.h | 7 +-
drivers/gpu/drm/meson/meson_venc.c | 10 +-
drivers/gpu/drm/meson/meson_venc.h | 4 +-
drivers/gpu/drm/meson/meson_venc_cvbs.c | 6 +-
include/drm/bridge/dw_hdmi.h | 1 +
8 files changed, 548 insertions(+), 75 deletions(-)

--
2.22.0


2020-02-06 19:19:58

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 02/11] drm/bridge: dw-hdmi: add max bpc connector property

From: Jonas Karlman <[email protected]>

Add the max_bpc property to the dw-hdmi connector to prepare support
for 10, 12 & 16bit output support.

Signed-off-by: Jonas Karlman <[email protected]>
Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 9e0927d22db6..051001f77dd4 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2406,6 +2406,10 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
DRM_MODE_CONNECTOR_HDMIA,
hdmi->ddc);

+ drm_atomic_helper_connector_reset(connector);
+
+ drm_connector_attach_max_bpc_property(connector, 8, 16);
+
if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
drm_object_attach_property(&connector->base,
connector->dev->mode_config.hdr_output_metadata_property, 0);
--
2.22.0

2020-02-06 19:20:12

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to negociate
the possible output and input formats for the current mode and monitor,
and use the negotiated formats in a basic atomic_check callback.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
1 file changed, 268 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index fec4a4bcd1fe..15048ad694bc 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2095,11 +2095,10 @@ 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)
hdmi->hdmi_data.enc_in_bus_format =
hdmi->plat_data->input_bus_format;
- else
+ else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;

/* TOFIX: Get input encoding from plat data or fallback to none */
@@ -2109,8 +2108,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
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 (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
+ hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;

hdmi->hdmi_data.pix_repet_factor = 0;
hdmi->hdmi_data.hdcp_enable = 0;
@@ -2388,6 +2387,267 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
.atomic_check = dw_hdmi_connector_atomic_check,
};

+/*
+ * Possible output formats :
+ * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
+ * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
+ * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
+ * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
+ * - MEDIA_BUS_FMT_YUV16_1X48,
+ * - MEDIA_BUS_FMT_RGB161616_1X48,
+ * - MEDIA_BUS_FMT_UYVY12_1X24,
+ * - MEDIA_BUS_FMT_YUV12_1X36,
+ * - MEDIA_BUS_FMT_RGB121212_1X36,
+ * - MEDIA_BUS_FMT_UYVY10_1X20,
+ * - MEDIA_BUS_FMT_YUV10_1X30,
+ * - MEDIA_BUS_FMT_RGB101010_1X30,
+ * - MEDIA_BUS_FMT_UYVY8_1X16,
+ * - MEDIA_BUS_FMT_YUV8_1X24,
+ * - MEDIA_BUS_FMT_RGB888_1X24,
+ */
+
+/* Can return a maximum of 12 possible output formats for a mode/connector */
+#define MAX_OUTPUT_SEL_FORMATS 12
+
+static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ unsigned int *num_output_fmts)
+{
+ struct drm_connector *conn = conn_state->connector;
+ struct drm_display_info *info = &conn->display_info;
+ struct drm_display_mode *mode = &crtc_state->mode;
+ u8 max_bpc = conn_state->max_requested_bpc;
+ bool is_hdmi2_sink = info->hdmi.scdc.supported ||
+ (info->color_formats & DRM_COLOR_FORMAT_YCRCB420);
+ u32 *output_fmts;
+ int i = 0;
+
+ *num_output_fmts = 0;
+
+ output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts),
+ GFP_KERNEL);
+ if (!output_fmts)
+ return NULL;
+
+ /*
+ * If the current mode enforces 4:2:0, force the output but format
+ * to 4:2:0 and do not add the YUV422/444/RGB formats
+ */
+ if (conn->ycbcr_420_allowed &&
+ (drm_mode_is_420_only(info, mode) ||
+ (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) {
+
+ /* Order bus formats from 16bit to 8bit if supported */
+ if (max_bpc >= 16 && info->bpc == 16 &&
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48;
+
+ if (max_bpc >= 12 && info->bpc >= 12 &&
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
+
+ if (max_bpc >= 10 && info->bpc >= 10 &&
+ (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30))
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
+
+ /* Default 8bit fallback */
+ output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
+
+ *num_output_fmts = i;
+
+ return output_fmts;
+ }
+
+ /*
+ * Order bus formats from 16bit to 8bit and from YUV422 to RGB
+ * if supported. In any case the default RGB888 format is added
+ */
+
+ if (max_bpc >= 16 && info->bpc == 16) {
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
+
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ }
+
+ if (max_bpc >= 12 && info->bpc >= 12) {
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ }
+
+ if (max_bpc >= 10 && info->bpc >= 10) {
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ }
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
+ output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+
+ if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+
+ /* Default 8bit RGB fallback */
+ output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+
+ *num_output_fmts = i;
+
+ return output_fmts;
+}
+
+/*
+ * Possible input formats :
+ * - MEDIA_BUS_FMT_RGB888_1X24
+ * - MEDIA_BUS_FMT_YUV8_1X24
+ * - MEDIA_BUS_FMT_UYVY8_1X16
+ * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
+ * - MEDIA_BUS_FMT_RGB101010_1X30
+ * - MEDIA_BUS_FMT_YUV10_1X30
+ * - MEDIA_BUS_FMT_UYVY10_1X20
+ * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
+ * - MEDIA_BUS_FMT_RGB121212_1X36
+ * - MEDIA_BUS_FMT_YUV12_1X36
+ * - MEDIA_BUS_FMT_UYVY12_1X24
+ * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
+ * - MEDIA_BUS_FMT_RGB161616_1X48
+ * - MEDIA_BUS_FMT_YUV16_1X48
+ * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
+ */
+
+/* Can return a maximum of 4 possible input formats for an output format */
+#define MAX_INPUT_SEL_FORMATS 4
+
+static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ u32 *input_fmts;
+ int i = 0;
+
+ *num_input_fmts = 0;
+
+ input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
+ GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ switch (output_fmt) {
+ /* 8bit */
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ break;
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
+ break;
+
+ /* 10bit */
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ break;
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ break;
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
+ break;
+
+ /* 12bit */
+ case MEDIA_BUS_FMT_RGB121212_1X36:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ break;
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ break;
+ case MEDIA_BUS_FMT_UYVY12_1X24:
+ input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
+ break;
+
+ /* 16bit */
+ case MEDIA_BUS_FMT_RGB161616_1X48:
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
+ break;
+ case MEDIA_BUS_FMT_YUV16_1X48:
+ input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
+ input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
+ break;
+
+ /* 420 */
+ 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:
+ input_fmts[i++] = output_fmt;
+ break;
+ }
+
+ *num_input_fmts = i;
+
+ if (*num_input_fmts == 0) {
+ kfree(input_fmts);
+ input_fmts = NULL;
+ }
+
+ return input_fmts;
+}
+
+static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct dw_hdmi *hdmi = bridge->driver_private;
+
+ dev_dbg(hdmi->dev, "selected output format %x\n",
+ bridge_state->output_bus_cfg.format);
+
+ hdmi->hdmi_data.enc_out_bus_format =
+ bridge_state->output_bus_cfg.format;
+
+ dev_dbg(hdmi->dev, "selected input format %x\n",
+ bridge_state->input_bus_cfg.format);
+
+ hdmi->hdmi_data.enc_in_bus_format =
+ bridge_state->input_bus_cfg.format;
+
+ return 0;
+}
+
static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
{
struct dw_hdmi *hdmi = bridge->driver_private;
@@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
.atomic_reset = drm_atomic_helper_bridge_reset,
.attach = dw_hdmi_bridge_attach,
.detach = dw_hdmi_bridge_detach,
+ .atomic_check = dw_hdmi_bridge_atomic_check,
+ .atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts,
+ .atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts,
.enable = dw_hdmi_bridge_enable,
.disable = dw_hdmi_bridge_disable,
.mode_set = dw_hdmi_bridge_mode_set,
@@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,

hdmi->bridge.driver_private = hdmi;
hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
+
#ifdef CONFIG_OF
hdmi->bridge.of_node = pdev->dev.of_node;
#endif
--
2.22.0

2020-02-06 19:20:15

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 09/11] drm/meson: venc: add support for YUV420 setup

This patch adds encoding support for the YUV420 output from the
Amlogic Meson SoCs Video Processing Unit to the HDMI Controller.

The YUV420 is obtained by generating a YUV444 pixel stream like
the classic HDMI display modes, but then the Video Encoder output
can be configured to down-sample the YUV444 pixel stream to a YUV420
stream.

In addition if pixel stream down-sampling, the Y Cb Cr components must
also be mapped differently to align with the HDMI2.0 specifications.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/gpu/drm/meson/meson_dw_hdmi.c | 3 ++-
drivers/gpu/drm/meson/meson_venc.c | 8 +++++---
drivers/gpu/drm/meson/meson_venc.h | 2 ++
3 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index 686c47106a18..f5d46d082534 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
@@ -772,7 +772,8 @@ static void meson_venc_hdmi_encoder_mode_set(struct drm_bridge *bridge,
DRM_DEBUG_DRIVER("\"%s\" vic %d\n", mode->name, vic);

/* VENC + VENC-DVI Mode setup */
- meson_venc_hdmi_mode_set(priv, vic, mode);
+ meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, false,
+ VPU_HDMI_OUTPUT_CBYCR);

/* VCLK Set clock */
dw_hdmi_set_vclk(dw_hdmi, mode);
diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c
index a9ab78970bfe..f93c725b6f02 100644
--- a/drivers/gpu/drm/meson/meson_venc.c
+++ b/drivers/gpu/drm/meson/meson_venc.c
@@ -946,6 +946,8 @@ bool meson_venc_hdmi_venc_repeat(int vic)
EXPORT_SYMBOL_GPL(meson_venc_hdmi_venc_repeat);

void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
+ unsigned int ycrcb_map,
+ bool yuv420_mode,
const struct drm_display_mode *mode)
{
union meson_hdmi_venc_mode *vmode = NULL;
@@ -1528,14 +1530,14 @@ void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
reg |= VPU_HDMI_INV_VSYNC;

- /* Output data format: CbYCr */
- reg |= VPU_HDMI_OUTPUT_CBYCR;
+ /* Output data format */
+ reg |= ycrcb_map;

/*
* Write rate to the async FIFO between VENC and HDMI.
* One write every 2 wr_clk.
*/
- if (venc_repeat)
+ if (venc_repeat || yuv420_mode)
reg |= VPU_HDMI_WR_RATE(2);

/*
diff --git a/drivers/gpu/drm/meson/meson_venc.h b/drivers/gpu/drm/meson/meson_venc.h
index 1abdcbdf51c0..9138255ffc9e 100644
--- a/drivers/gpu/drm/meson/meson_venc.h
+++ b/drivers/gpu/drm/meson/meson_venc.h
@@ -60,6 +60,8 @@ extern struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc;
void meson_venci_cvbs_mode_set(struct meson_drm *priv,
struct meson_cvbs_enci_mode *mode);
void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
+ unsigned int ycrcb_map,
+ bool yuv420_mode,
const struct drm_display_mode *mode);
unsigned int meson_venci_get_field(struct meson_drm *priv);

--
2.22.0

2020-02-06 19:20:26

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 10/11] drm/meson: vclk: add support for YUV420 setup

This patch adds clocking support for the YUV420 output from the
Amlogic Meson SoCs Video Processing Unit to the HDMI Controller.

The YUV420 is obtained by generating a YUV444 pixel stream like
the classic HDMI display modes, but then the Video Encoder output
can be configured to down-sample the YUV444 pixel stream to a YUV420
stream.

This mode needs a different clock generation scheme since the TMDS PHY
clock must match the 10x ratio with the YUV420 pixel clock, but
the video encoder must run at 2x the pixel clock.

This patch adds the TMDS PHY clock value in all the video clock setup
in order to better support these specific uses cases and switch
to the Common Clock framework for clocks handling in the future.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/gpu/drm/meson/meson_dw_hdmi.c | 24 ++++---
drivers/gpu/drm/meson/meson_vclk.c | 93 +++++++++++++++++++------
drivers/gpu/drm/meson/meson_vclk.h | 7 +-
drivers/gpu/drm/meson/meson_venc_cvbs.c | 6 +-
4 files changed, 95 insertions(+), 35 deletions(-)

diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index f5d46d082534..94f206bf795d 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
@@ -376,15 +376,19 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
{
struct meson_drm *priv = dw_hdmi->priv;
int vic = drm_match_cea_mode(mode);
+ unsigned int phy_freq;
unsigned int vclk_freq;
unsigned int venc_freq;
unsigned int hdmi_freq;

vclk_freq = mode->clock;

+ /* TMDS clock is pixel_clock * 10 */
+ phy_freq = vclk_freq * 10;
+
if (!vic) {
- meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, vclk_freq,
- vclk_freq, vclk_freq, false);
+ meson_vclk_setup(priv, MESON_VCLK_TARGET_DMT, phy_freq,
+ vclk_freq, vclk_freq, vclk_freq, false);
return;
}

@@ -402,11 +406,11 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
venc_freq /= 2;

- DRM_DEBUG_DRIVER("vclk:%d venc=%d hdmi=%d enci=%d\n",
- vclk_freq, venc_freq, hdmi_freq,
+ DRM_DEBUG_DRIVER("vclk:%d phy=%d venc=%d hdmi=%d enci=%d\n",
+ phy_freq, vclk_freq, venc_freq, hdmi_freq,
priv->venc.hdmi_use_enci);

- meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, vclk_freq,
+ meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, phy_freq, vclk_freq,
venc_freq, hdmi_freq, priv->venc.hdmi_use_enci);
}

@@ -617,6 +621,7 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
const struct drm_display_mode *mode)
{
struct meson_drm *priv = connector->dev->dev_private;
+ unsigned int phy_freq;
unsigned int vclk_freq;
unsigned int venc_freq;
unsigned int hdmi_freq;
@@ -643,6 +648,9 @@ dw_hdmi_mode_valid(struct drm_connector *connector,

vclk_freq = mode->clock;

+ /* TMDS clock is pixel_clock * 10 */
+ phy_freq = vclk_freq * 10;
+
/* 480i/576i needs global pixel doubling */
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
vclk_freq *= 2;
@@ -659,10 +667,10 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
venc_freq /= 2;

- dev_dbg(connector->dev->dev, "%s: vclk:%d venc=%d hdmi=%d\n", __func__,
- vclk_freq, venc_freq, hdmi_freq);
+ dev_dbg(connector->dev->dev, "%s: vclk:%d phy=%d venc=%d hdmi=%d\n",
+ __func__, phy_freq, vclk_freq, venc_freq, hdmi_freq);

- return meson_vclk_vic_supported_freq(vclk_freq);
+ return meson_vclk_vic_supported_freq(phy_freq, vclk_freq);
}

/* Encoder */
diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c
index f690793ae2d5..fdf26dac9fa8 100644
--- a/drivers/gpu/drm/meson/meson_vclk.c
+++ b/drivers/gpu/drm/meson/meson_vclk.c
@@ -354,12 +354,17 @@ enum {
/* 2970 /1 /1 /1 /5 /2 => /1 /1 */
MESON_VCLK_HDMI_297000,
/* 5940 /1 /1 /2 /5 /1 => /1 /1 */
- MESON_VCLK_HDMI_594000
+ MESON_VCLK_HDMI_594000,
+/* 2970 /1 /1 /1 /5 /1 => /1 /2 */
+ MESON_VCLK_HDMI_594000_YUV420,
};

struct meson_vclk_params {
+ unsigned int pll_freq;
+ unsigned int phy_freq;
+ unsigned int vclk_freq;
+ unsigned int venc_freq;
unsigned int pixel_freq;
- unsigned int pll_base_freq;
unsigned int pll_od1;
unsigned int pll_od2;
unsigned int pll_od3;
@@ -367,8 +372,11 @@ struct meson_vclk_params {
unsigned int vclk_div;
} params[] = {
[MESON_VCLK_HDMI_ENCI_54000] = {
+ .pll_freq = 4320000,
+ .phy_freq = 270000,
+ .vclk_freq = 54000,
+ .venc_freq = 54000,
.pixel_freq = 54000,
- .pll_base_freq = 4320000,
.pll_od1 = 4,
.pll_od2 = 4,
.pll_od3 = 1,
@@ -376,8 +384,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_DDR_54000] = {
- .pixel_freq = 54000,
- .pll_base_freq = 4320000,
+ .pll_freq = 4320000,
+ .phy_freq = 270000,
+ .vclk_freq = 54000,
+ .venc_freq = 54000,
+ .pixel_freq = 27000,
.pll_od1 = 4,
.pll_od2 = 4,
.pll_od3 = 1,
@@ -385,8 +396,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_DDR_148500] = {
- .pixel_freq = 148500,
- .pll_base_freq = 2970000,
+ .pll_freq = 2970000,
+ .phy_freq = 742500,
+ .vclk_freq = 148500,
+ .venc_freq = 148500,
+ .pixel_freq = 74250,
.pll_od1 = 4,
.pll_od2 = 1,
.pll_od3 = 1,
@@ -394,8 +408,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_74250] = {
+ .pll_freq = 2970000,
+ .phy_freq = 742500,
+ .vclk_freq = 74250,
+ .venc_freq = 74250,
.pixel_freq = 74250,
- .pll_base_freq = 2970000,
.pll_od1 = 2,
.pll_od2 = 2,
.pll_od3 = 2,
@@ -403,8 +420,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_148500] = {
+ .pll_freq = 2970000,
+ .phy_freq = 1485000,
+ .vclk_freq = 148500,
+ .venc_freq = 148500,
.pixel_freq = 148500,
- .pll_base_freq = 2970000,
.pll_od1 = 1,
.pll_od2 = 2,
.pll_od3 = 2,
@@ -412,8 +432,11 @@ struct meson_vclk_params {
.vclk_div = 1,
},
[MESON_VCLK_HDMI_297000] = {
+ .pll_freq = 5940000,
+ .phy_freq = 2970000,
+ .venc_freq = 297000,
+ .vclk_freq = 297000,
.pixel_freq = 297000,
- .pll_base_freq = 5940000,
.pll_od1 = 2,
.pll_od2 = 1,
.pll_od3 = 1,
@@ -421,14 +444,29 @@ struct meson_vclk_params {
.vclk_div = 2,
},
[MESON_VCLK_HDMI_594000] = {
+ .pll_freq = 5940000,
+ .phy_freq = 5940000,
+ .venc_freq = 594000,
+ .vclk_freq = 594000,
.pixel_freq = 594000,
- .pll_base_freq = 5940000,
.pll_od1 = 1,
.pll_od2 = 1,
.pll_od3 = 2,
.vid_pll_div = VID_PLL_DIV_5,
.vclk_div = 1,
},
+ [MESON_VCLK_HDMI_594000_YUV420] = {
+ .pll_freq = 5940000,
+ .phy_freq = 2970000,
+ .venc_freq = 594000,
+ .vclk_freq = 594000,
+ .pixel_freq = 297000,
+ .pll_od1 = 2,
+ .pll_od2 = 1,
+ .pll_od3 = 1,
+ .vid_pll_div = VID_PLL_DIV_5,
+ .vclk_div = 1,
+ },
{ /* sentinel */ },
};

@@ -701,6 +739,7 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
unsigned int od, m, frac, od1, od2, od3;

if (meson_hdmi_pll_find_params(priv, pll_freq, &m, &frac, &od)) {
+ /* OD2 goes to the PHY, and needs to be *10, so keep OD3=1 */
od3 = 1;
if (od < 4) {
od1 = 2;
@@ -723,21 +762,28 @@ static void meson_hdmi_pll_generic_set(struct meson_drm *priv,
}

enum drm_mode_status
-meson_vclk_vic_supported_freq(unsigned int freq)
+meson_vclk_vic_supported_freq(unsigned int phy_freq,
+ unsigned int vclk_freq)
{
int i;

- DRM_DEBUG_DRIVER("freq = %d\n", freq);
+ DRM_DEBUG_DRIVER("phy_freq = %d vclk_freq = %d\n",
+ phy_freq, vclk_freq);

for (i = 0 ; params[i].pixel_freq ; ++i) {
DRM_DEBUG_DRIVER("i = %d pixel_freq = %d alt = %d\n",
i, params[i].pixel_freq,
FREQ_1000_1001(params[i].pixel_freq));
+ DRM_DEBUG_DRIVER("i = %d phy_freq = %d alt = %d\n",
+ i, params[i].phy_freq,
+ FREQ_1000_1001(params[i].phy_freq/10)*10);
/* Match strict frequency */
- if (freq == params[i].pixel_freq)
+ if (phy_freq == params[i].phy_freq &&
+ vclk_freq == params[i].vclk_freq)
return MODE_OK;
/* Match 1000/1001 variant */
- if (freq == FREQ_1000_1001(params[i].pixel_freq))
+ if (phy_freq == (FREQ_1000_1001(params[i].phy_freq/10)*10) &&
+ vclk_freq == FREQ_1000_1001(params[i].vclk_freq))
return MODE_OK;
}

@@ -965,8 +1011,9 @@ static void meson_vclk_set(struct meson_drm *priv, unsigned int pll_base_freq,
}

void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
- unsigned int vclk_freq, unsigned int venc_freq,
- unsigned int dac_freq, bool hdmi_use_enci)
+ unsigned int phy_freq, unsigned int vclk_freq,
+ unsigned int venc_freq, unsigned int dac_freq,
+ bool hdmi_use_enci)
{
bool vic_alternate_clock = false;
unsigned int freq;
@@ -986,7 +1033,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
* - venc_div = 1
* - encp encoder
*/
- meson_vclk_set(priv, vclk_freq * 10, 0, 0, 0,
+ meson_vclk_set(priv, phy_freq, 0, 0, 0,
VID_PLL_DIV_5, 2, 1, 1, false, false);
return;
}
@@ -1008,9 +1055,11 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
}

for (freq = 0 ; params[freq].pixel_freq ; ++freq) {
- if (vclk_freq == params[freq].pixel_freq ||
- vclk_freq == FREQ_1000_1001(params[freq].pixel_freq)) {
- if (vclk_freq != params[freq].pixel_freq)
+ if ((phy_freq == params[freq].phy_freq ||
+ phy_freq == FREQ_1000_1001(params[freq].phy_freq/10)*10) &&
+ (vclk_freq == params[freq].vclk_freq ||
+ vclk_freq == FREQ_1000_1001(params[freq].vclk_freq))) {
+ if (vclk_freq != params[freq].vclk_freq)
vic_alternate_clock = true;
else
vic_alternate_clock = false;
@@ -1039,7 +1088,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
return;
}

- meson_vclk_set(priv, params[freq].pll_base_freq,
+ meson_vclk_set(priv, params[freq].pll_freq,
params[freq].pll_od1, params[freq].pll_od2,
params[freq].pll_od3, params[freq].vid_pll_div,
params[freq].vclk_div, hdmi_tx_div, venc_div,
diff --git a/drivers/gpu/drm/meson/meson_vclk.h b/drivers/gpu/drm/meson/meson_vclk.h
index b62125540aef..aed0ab2efa71 100644
--- a/drivers/gpu/drm/meson/meson_vclk.h
+++ b/drivers/gpu/drm/meson/meson_vclk.h
@@ -25,10 +25,11 @@ enum {
enum drm_mode_status
meson_vclk_dmt_supported_freq(struct meson_drm *priv, unsigned int freq);
enum drm_mode_status
-meson_vclk_vic_supported_freq(unsigned int freq);
+meson_vclk_vic_supported_freq(unsigned int phy_freq, unsigned int vclk_freq);

void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
- unsigned int vclk_freq, unsigned int venc_freq,
- unsigned int dac_freq, bool hdmi_use_enci);
+ unsigned int phy_freq, unsigned int vclk_freq,
+ unsigned int venc_freq, unsigned int dac_freq,
+ bool hdmi_use_enci);

#endif /* __MESON_VCLK_H */
diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.c b/drivers/gpu/drm/meson/meson_venc_cvbs.c
index 1bd6b6d15ffb..541f9eb2a135 100644
--- a/drivers/gpu/drm/meson/meson_venc_cvbs.c
+++ b/drivers/gpu/drm/meson/meson_venc_cvbs.c
@@ -213,8 +213,10 @@ static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder,
meson_venci_cvbs_mode_set(priv, meson_mode->enci);

/* Setup 27MHz vclk2 for ENCI and VDAC */
- meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, MESON_VCLK_CVBS,
- MESON_VCLK_CVBS, MESON_VCLK_CVBS, true);
+ meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS,
+ MESON_VCLK_CVBS, MESON_VCLK_CVBS,
+ MESON_VCLK_CVBS, MESON_VCLK_CVBS,
+ true);
}
}

--
2.22.0

2020-02-06 19:21:03

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 08/11] drm/meson: dw-hdmi: stop enforcing input_bus_format

To allow using formats from negotiation, stop enforcing input_bus_format
in the private dw-plat-data struct.

Signed-off-by: Neil Armstrong <[email protected]>
Reviewed-by: Boris Brezillon <[email protected]>
---
drivers/gpu/drm/meson/meson_dw_hdmi.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index 4b3809626f7e..686c47106a18 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
@@ -1035,7 +1035,6 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
dw_plat_data->phy_ops = &meson_dw_hdmi_phy_ops;
dw_plat_data->phy_name = "meson_dw_hdmi_phy";
dw_plat_data->phy_data = meson_dw_hdmi;
- dw_plat_data->input_bus_format = MEDIA_BUS_FMT_YUV8_1X24;
dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;

if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
--
2.22.0

2020-02-06 19:21:03

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 03/11] drm/bridge: dw-hdmi: Plug atomic state hooks to the default implementation

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3 +++
1 file changed, 3 insertions(+)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 051001f77dd4..fec4a4bcd1fe 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2494,6 +2494,9 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
}

static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
.attach = dw_hdmi_bridge_attach,
.detach = dw_hdmi_bridge_detach,
.enable = dw_hdmi_bridge_enable,
--
2.22.0

2020-02-06 19:21:05

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 05/11] drm/bridge: synopsys: dw-hdmi: allow ycbcr420 modes for >= 0x200a

Now the DW-HDMI Controller supports the HDMI2.0 modes, enable support
for these modes in the connector if the platform supports them.
We limit these modes to DW-HDMI IP version >= 0x200a which
are designed to support HDMI2.0 display modes.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 6 ++++++
include/drm/bridge/dw_hdmi.h | 1 +
2 files changed, 7 insertions(+)

diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 15048ad694bc..4b35ea1427df 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -3231,6 +3231,12 @@ __dw_hdmi_probe(struct platform_device *pdev,
hdmi->bridge.of_node = pdev->dev.of_node;
#endif

+ if (hdmi->version >= 0x200a)
+ hdmi->connector.ycbcr_420_allowed =
+ hdmi->plat_data->ycbcr_420_allowed;
+ else
+ hdmi->connector.ycbcr_420_allowed = false;
+
memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo.parent = dev;
pdevinfo.id = PLATFORM_DEVID_AUTO;
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
index 9d4d5cc47969..0b34a12c4a1c 100644
--- a/include/drm/bridge/dw_hdmi.h
+++ b/include/drm/bridge/dw_hdmi.h
@@ -129,6 +129,7 @@ struct dw_hdmi_plat_data {
unsigned long input_bus_format;
unsigned long input_bus_encoding;
bool use_drm_infoframe;
+ bool ycbcr_420_allowed;

/* Vendor PHY support */
const struct dw_hdmi_phy_ops *phy_ops;
--
2.22.0

2020-02-06 19:21:36

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 11/11] drm/meson: Add YUV420 output support

This patch adds support for the YUV420 output from the Amlogic Meson SoCs
Video Processing Unit to the HDMI Controller.

The YUV420 is obtained by generating a YUV444 pixel stream like
the classic HDMI display modes, but then the Video Encoder output
can be configured to down-sample the YUV444 pixel stream to a YUV420
stream.
In addition if pixel stream down-sampling, the Y Cb Cr components must
also be mapped differently to align with the HDMI2.0 specifications.

This mode needs a different clock generation scheme since the TMDS PHY
clock must match the 10x ration with the YUV420 pixel clock, but
the video encoder must run at 2x the pixel clock.

This patch enables the bridge bus format negociation, and handles
the YUV420 case if selected by the negociation.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/gpu/drm/meson/meson_dw_hdmi.c | 91 ++++++++++++++++++++-------
1 file changed, 70 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index 94f206bf795d..5962afbfc8ab 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
@@ -150,6 +150,7 @@ struct meson_dw_hdmi {
struct regulator *hdmi_supply;
u32 irq_stat;
struct dw_hdmi *hdmi;
+ unsigned long output_bus_fmt;
};
#define encoder_to_meson_dw_hdmi(x) \
container_of(x, struct meson_dw_hdmi, encoder)
@@ -301,6 +302,10 @@ static void meson_hdmi_phy_setup_mode(struct meson_dw_hdmi *dw_hdmi,
struct meson_drm *priv = dw_hdmi->priv;
unsigned int pixel_clock = mode->clock;

+ /* For 420, pixel clock is half unlike venc clock */
+ if (dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
+ pixel_clock /= 2;
+
if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi")) {
if (pixel_clock >= 371250) {
@@ -383,6 +388,10 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,

vclk_freq = mode->clock;

+ /* For 420, pixel clock is half unlike venc clock */
+ if (dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
+ vclk_freq /= 2;
+
/* TMDS clock is pixel_clock * 10 */
phy_freq = vclk_freq * 10;

@@ -392,13 +401,16 @@ static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
return;
}

+ /* 480i/576i needs global pixel doubling */
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
vclk_freq *= 2;

venc_freq = vclk_freq;
hdmi_freq = vclk_freq;

- if (meson_venc_hdmi_venc_repeat(vic))
+ /* VENC double pixels for 1080i, 720p and YUV420 modes */
+ if (meson_venc_hdmi_venc_repeat(vic) ||
+ dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
venc_freq *= 2;

vclk_freq = max(venc_freq, hdmi_freq);
@@ -445,8 +457,9 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
/* Enable normal output to PHY */
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));

- /* TMDS pattern setup (TOFIX Handle the YUV420 case) */
- if (mode->clock > 340000) {
+ /* TMDS pattern setup */
+ if (mode->clock > 340000 &&
+ dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_YUV8_1X24) {
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
0);
dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23,
@@ -621,6 +634,7 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
const struct drm_display_mode *mode)
{
struct meson_drm *priv = connector->dev->dev_private;
+ bool is_hdmi2_sink = connector->display_info.hdmi.scdc.supported;
unsigned int phy_freq;
unsigned int vclk_freq;
unsigned int venc_freq;
@@ -630,9 +644,11 @@ dw_hdmi_mode_valid(struct drm_connector *connector,

DRM_DEBUG_DRIVER("Modeline " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode));

- /* If sink max TMDS clock, we reject the mode */
+ /* If sink does not support 540MHz, reject the non-420 HDMI2 modes */
if (connector->display_info.max_tmds_clock &&
- mode->clock > connector->display_info.max_tmds_clock)
+ mode->clock > connector->display_info.max_tmds_clock &&
+ !drm_mode_is_420_only(&connector->display_info, mode) &&
+ !drm_mode_is_420_also(&connector->display_info, mode))
return MODE_BAD;

/* Check against non-VIC supported modes */
@@ -648,6 +664,12 @@ dw_hdmi_mode_valid(struct drm_connector *connector,

vclk_freq = mode->clock;

+ /* For 420, pixel clock is half unlike venc clock */
+ if (drm_mode_is_420_only(&connector->display_info, mode) ||
+ (!is_hdmi2_sink &&
+ drm_mode_is_420_also(&connector->display_info, mode)))
+ vclk_freq /= 2;
+
/* TMDS clock is pixel_clock * 10 */
phy_freq = vclk_freq * 10;

@@ -658,8 +680,11 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
venc_freq = vclk_freq;
hdmi_freq = vclk_freq;

- /* VENC double pixels for 1080i and 720p modes */
- if (meson_venc_hdmi_venc_repeat(vic))
+ /* VENC double pixels for 1080i, 720p and YUV420 modes */
+ if (meson_venc_hdmi_venc_repeat(vic) ||
+ drm_mode_is_420_only(&connector->display_info, mode) ||
+ (!is_hdmi2_sink &&
+ drm_mode_is_420_also(&connector->display_info, mode)))
venc_freq *= 2;

vclk_freq = max(venc_freq, hdmi_freq);
@@ -677,6 +702,7 @@ dw_hdmi_mode_valid(struct drm_connector *connector,

static const u32 meson_dw_hdmi_out_bus_fmts[] = {
MEDIA_BUS_FMT_YUV8_1X24,
+ MEDIA_BUS_FMT_UYYVYY8_0_5X24,
};

static void meson_venc_hdmi_encoder_destroy(struct drm_encoder *encoder)
@@ -717,18 +743,23 @@ meson_venc_hdmi_encoder_get_inp_bus_fmts(struct drm_bridge *bridge,
unsigned int *num_input_fmts)
{
u32 *input_fmts = NULL;
+ int i;

- if (output_fmt == meson_dw_hdmi_out_bus_fmts[0]) {
- *num_input_fmts = 1;
- input_fmts = kcalloc(*num_input_fmts,
- sizeof(*input_fmts),
- GFP_KERNEL);
- if (!input_fmts)
- return NULL;
+ *num_input_fmts = 0;

- input_fmts[0] = output_fmt;
- } else {
- *num_input_fmts = 0;
+ for (i = 0 ; i < ARRAY_SIZE(meson_dw_hdmi_out_bus_fmts) ; ++i) {
+ if (output_fmt == meson_dw_hdmi_out_bus_fmts[i]) {
+ *num_input_fmts = 1;
+ input_fmts = kcalloc(*num_input_fmts,
+ sizeof(*input_fmts),
+ GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ input_fmts[0] = output_fmt;
+
+ break;
+ }
}

return input_fmts;
@@ -739,6 +770,12 @@ static int meson_venc_hdmi_encoder_atomic_check(struct drm_bridge *bridge,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
+ struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
+
+ dw_hdmi->output_bus_fmt = bridge_state->output_bus_cfg.format;
+
+ DRM_DEBUG_DRIVER("output_bus_fmt %lx\n", dw_hdmi->output_bus_fmt);
+
return 0;
}

@@ -776,18 +813,29 @@ static void meson_venc_hdmi_encoder_mode_set(struct drm_bridge *bridge,
struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
struct meson_drm *priv = dw_hdmi->priv;
int vic = drm_match_cea_mode(mode);
+ unsigned int ycrcb_map = VPU_HDMI_OUTPUT_CBYCR;
+ bool yuv420_mode = false;

DRM_DEBUG_DRIVER("\"%s\" vic %d\n", mode->name, vic);

+ if (dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24) {
+ ycrcb_map = VPU_HDMI_OUTPUT_CRYCB;
+ yuv420_mode = true;
+ }
+
/* VENC + VENC-DVI Mode setup */
- meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, false,
- VPU_HDMI_OUTPUT_CBYCR);
+ meson_venc_hdmi_mode_set(priv, vic, ycrcb_map, yuv420_mode, mode);

/* VCLK Set clock */
dw_hdmi_set_vclk(dw_hdmi, mode);

- /* Setup YUV444 to HDMI-TX, no 10bit diphering */
- writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
+ if (dw_hdmi->output_bus_fmt == MEDIA_BUS_FMT_UYYVYY8_0_5X24)
+ /* Setup YUV420 to HDMI-TX, no 10bit diphering */
+ writel_relaxed(2 | (2 << 2),
+ priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
+ else
+ /* Setup YUV444 to HDMI-TX, no 10bit diphering */
+ writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
}

static const struct drm_bridge_funcs meson_venc_hdmi_encoder_bridge_funcs = {
@@ -1045,6 +1093,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
dw_plat_data->phy_name = "meson_dw_hdmi_phy";
dw_plat_data->phy_data = meson_dw_hdmi;
dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709;
+ dw_plat_data->ycbcr_420_allowed = true;

if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
--
2.22.0

2020-02-06 19:21:56

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 07/11] drm/meson: meson_dw_hdmi: add bridge and switch to drm_bridge_funcs

Switch the dw-hdmi driver to drm_bridge_funcs by implementing a new local
bridge, connecting it to the dw-hdmi bridge, then implement the
atomic_get_input_bus_fmts/atomic_get_output_bus_fmts.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/gpu/drm/meson/meson_dw_hdmi.c | 105 +++++++++++++++++++++-----
1 file changed, 85 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
index 3bb7ffe5fc39..4b3809626f7e 100644
--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
@@ -16,6 +16,7 @@

#include <drm/bridge/dw_hdmi.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
#include <drm/drm_device.h>
#include <drm/drm_edid.h>
#include <drm/drm_probe_helper.h>
@@ -135,6 +136,7 @@ struct meson_dw_hdmi_data {

struct meson_dw_hdmi {
struct drm_encoder encoder;
+ struct drm_bridge bridge;
struct dw_hdmi_plat_data dw_plat_data;
struct meson_drm *priv;
struct device *dev;
@@ -151,6 +153,8 @@ struct meson_dw_hdmi {
};
#define encoder_to_meson_dw_hdmi(x) \
container_of(x, struct meson_dw_hdmi, encoder)
+#define bridge_to_meson_dw_hdmi(x) \
+ container_of(x, struct meson_dw_hdmi, bridge)

static inline int dw_hdmi_is_compatible(struct meson_dw_hdmi *dw_hdmi,
const char *compat)
@@ -368,7 +372,7 @@ static inline void meson_dw_hdmi_phy_reset(struct meson_dw_hdmi *dw_hdmi)
}

static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *mode)
{
struct meson_drm *priv = dw_hdmi->priv;
int vic = drm_match_cea_mode(mode);
@@ -663,6 +667,10 @@ dw_hdmi_mode_valid(struct drm_connector *connector,

/* Encoder */

+static const u32 meson_dw_hdmi_out_bus_fmts[] = {
+ MEDIA_BUS_FMT_YUV8_1X24,
+};
+
static void meson_venc_hdmi_encoder_destroy(struct drm_encoder *encoder)
{
drm_encoder_cleanup(encoder);
@@ -672,16 +680,63 @@ static const struct drm_encoder_funcs meson_venc_hdmi_encoder_funcs = {
.destroy = meson_venc_hdmi_encoder_destroy,
};

-static int meson_venc_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
+static u32 *
+meson_venc_hdmi_encoder_get_out_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ unsigned int *num_output_fmts)
+{
+ u32 *output_fmts;
+
+ *num_output_fmts = ARRAY_SIZE(meson_dw_hdmi_out_bus_fmts);
+ output_fmts = kcalloc(*num_output_fmts, sizeof(*output_fmts),
+ GFP_KERNEL);
+ if (!output_fmts)
+ return NULL;
+
+ memcpy(output_fmts, meson_dw_hdmi_out_bus_fmts, *num_output_fmts);
+
+ return output_fmts;
+}
+
+static u32 *
+meson_venc_hdmi_encoder_get_inp_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ u32 *input_fmts = NULL;
+
+ if (output_fmt == meson_dw_hdmi_out_bus_fmts[0]) {
+ *num_input_fmts = 1;
+ input_fmts = kcalloc(*num_input_fmts,
+ sizeof(*input_fmts),
+ GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ input_fmts[0] = output_fmt;
+ } else {
+ *num_input_fmts = 0;
+ }
+
+ return input_fmts;
+}
+
+static int meson_venc_hdmi_encoder_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
return 0;
}

-static void meson_venc_hdmi_encoder_disable(struct drm_encoder *encoder)
+static void meson_venc_hdmi_encoder_disable(struct drm_bridge *bridge)
{
- struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder);
+ struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
struct meson_drm *priv = dw_hdmi->priv;

DRM_DEBUG_DRIVER("\n");
@@ -693,9 +748,9 @@ static void meson_venc_hdmi_encoder_disable(struct drm_encoder *encoder)
writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
}

-static void meson_venc_hdmi_encoder_enable(struct drm_encoder *encoder)
+static void meson_venc_hdmi_encoder_enable(struct drm_bridge *bridge)
{
- struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder);
+ struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
struct meson_drm *priv = dw_hdmi->priv;

DRM_DEBUG_DRIVER("%s\n", priv->venc.hdmi_use_enci ? "VENCI" : "VENCP");
@@ -706,11 +761,11 @@ static void meson_venc_hdmi_encoder_enable(struct drm_encoder *encoder)
writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN));
}

-static void meson_venc_hdmi_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+static void meson_venc_hdmi_encoder_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
{
- struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder);
+ struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
struct meson_drm *priv = dw_hdmi->priv;
int vic = drm_match_cea_mode(mode);

@@ -726,12 +781,16 @@ static void meson_venc_hdmi_encoder_mode_set(struct drm_encoder *encoder,
writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
}

-static const struct drm_encoder_helper_funcs
- meson_venc_hdmi_encoder_helper_funcs = {
- .atomic_check = meson_venc_hdmi_encoder_atomic_check,
- .disable = meson_venc_hdmi_encoder_disable,
- .enable = meson_venc_hdmi_encoder_enable,
- .mode_set = meson_venc_hdmi_encoder_mode_set,
+static const struct drm_bridge_funcs meson_venc_hdmi_encoder_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_get_output_bus_fmts = meson_venc_hdmi_encoder_get_out_bus_fmts,
+ .atomic_get_input_bus_fmts = meson_venc_hdmi_encoder_get_inp_bus_fmts,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_check = meson_venc_hdmi_encoder_atomic_check,
+ .enable = meson_venc_hdmi_encoder_enable,
+ .disable = meson_venc_hdmi_encoder_disable,
+ .mode_set = meson_venc_hdmi_encoder_mode_set,
};

/* DW HDMI Regmap */
@@ -852,6 +911,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
struct drm_device *drm = data;
struct meson_drm *priv = drm->dev_private;
struct dw_hdmi_plat_data *dw_plat_data;
+ struct drm_bridge *next_bridge;
struct drm_encoder *encoder;
struct resource *res;
int irq;
@@ -953,8 +1013,6 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,

/* Encoder */

- drm_encoder_helper_add(encoder, &meson_venc_hdmi_encoder_helper_funcs);
-
ret = drm_encoder_init(drm, encoder, &meson_venc_hdmi_encoder_funcs,
DRM_MODE_ENCODER_TMDS, "meson_hdmi");
if (ret) {
@@ -962,6 +1020,9 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
return ret;
}

+ meson_dw_hdmi->bridge.funcs = &meson_venc_hdmi_encoder_bridge_funcs;
+ drm_bridge_attach(encoder, &meson_dw_hdmi->bridge, NULL);
+
encoder->possible_crtcs = BIT(0);

DRM_DEBUG_DRIVER("encoder initialized\n");
@@ -984,11 +1045,15 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,

platform_set_drvdata(pdev, meson_dw_hdmi);

- meson_dw_hdmi->hdmi = dw_hdmi_bind(pdev, encoder,
- &meson_dw_hdmi->dw_plat_data);
+ meson_dw_hdmi->hdmi = dw_hdmi_probe(pdev,
+ &meson_dw_hdmi->dw_plat_data);
if (IS_ERR(meson_dw_hdmi->hdmi))
return PTR_ERR(meson_dw_hdmi->hdmi);

+ next_bridge = of_drm_find_bridge(pdev->dev.of_node);
+ if (next_bridge)
+ drm_bridge_attach(encoder, next_bridge, &meson_dw_hdmi->bridge);
+
DRM_DEBUG_DRIVER("HDMI controller initialized\n");

return 0;
--
2.22.0

2020-02-06 19:21:56

by Neil Armstrong

[permalink] [raw]
Subject: [PATCH v4 06/11] drm/meson: venc: make drm_display_mode const

Before switching to bridge funcs, make sure drm_display_mode is passed
as const to the venc functions.

Signed-off-by: Neil Armstrong <[email protected]>
---
drivers/gpu/drm/meson/meson_venc.c | 2 +-
drivers/gpu/drm/meson/meson_venc.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c
index 4efd7864d5bf..a9ab78970bfe 100644
--- a/drivers/gpu/drm/meson/meson_venc.c
+++ b/drivers/gpu/drm/meson/meson_venc.c
@@ -946,7 +946,7 @@ bool meson_venc_hdmi_venc_repeat(int vic)
EXPORT_SYMBOL_GPL(meson_venc_hdmi_venc_repeat);

void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *mode)
{
union meson_hdmi_venc_mode *vmode = NULL;
union meson_hdmi_venc_mode vmode_dmt;
diff --git a/drivers/gpu/drm/meson/meson_venc.h b/drivers/gpu/drm/meson/meson_venc.h
index 576768bdd08d..1abdcbdf51c0 100644
--- a/drivers/gpu/drm/meson/meson_venc.h
+++ b/drivers/gpu/drm/meson/meson_venc.h
@@ -60,7 +60,7 @@ extern struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc;
void meson_venci_cvbs_mode_set(struct meson_drm *priv,
struct meson_cvbs_enci_mode *mode);
void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
- struct drm_display_mode *mode);
+ const struct drm_display_mode *mode);
unsigned int meson_venci_get_field(struct meson_drm *priv);

void meson_venc_enable_vsync(struct meson_drm *priv);
--
2.22.0

2020-02-07 10:59:51

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v4 03/11] drm/bridge: dw-hdmi: Plug atomic state hooks to the default implementation

On Thu, 6 Feb 2020 20:18:26 +0100
Neil Armstrong <[email protected]> wrote:

> Signed-off-by: Neil Armstrong <[email protected]>

Reviewed-by: Boris Brezillon <[email protected]>

> ---
> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> index 051001f77dd4..fec4a4bcd1fe 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -2494,6 +2494,9 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
> }
>
> static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
> + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> + .atomic_reset = drm_atomic_helper_bridge_reset,
> .attach = dw_hdmi_bridge_attach,
> .detach = dw_hdmi_bridge_detach,
> .enable = dw_hdmi_bridge_enable,

2020-02-07 11:05:12

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

On Thu, 6 Feb 2020 20:18:27 +0100
Neil Armstrong <[email protected]> wrote:

> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to negociate

^ hooks?

> the possible output and input formats for the current mode and monitor,
> and use the negotiated formats in a basic atomic_check callback.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---

> +
> +/* Can return a maximum of 4 possible input formats for an output format */
> +#define MAX_INPUT_SEL_FORMATS 4

It seems to only be 3 in practice (based on the code) unless I missed
something.

> +
> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state,
> + u32 output_fmt,
> + unsigned int *num_input_fmts)
> +{
> + u32 *input_fmts;
> + int i = 0;
> +
> + *num_input_fmts = 0;
> +
> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
> + GFP_KERNEL);
> + if (!input_fmts)
> + return NULL;
> +
> + switch (output_fmt) {
> + /* 8bit */
> + case MEDIA_BUS_FMT_RGB888_1X24:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> + break;
> + case MEDIA_BUS_FMT_YUV8_1X24:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> + break;
> + case MEDIA_BUS_FMT_UYVY8_1X16:
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> + break;
> +
> + /* 10bit */
> + case MEDIA_BUS_FMT_RGB101010_1X30:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> + break;
> + case MEDIA_BUS_FMT_YUV10_1X30:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + break;
> + case MEDIA_BUS_FMT_UYVY10_1X20:
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + break;
> +
> + /* 12bit */
> + case MEDIA_BUS_FMT_RGB121212_1X36:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> + break;
> + case MEDIA_BUS_FMT_YUV12_1X36:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + break;
> + case MEDIA_BUS_FMT_UYVY12_1X24:
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + break;
> +
> + /* 16bit */
> + case MEDIA_BUS_FMT_RGB161616_1X48:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> + break;
> + case MEDIA_BUS_FMT_YUV16_1X48:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> + break;
> +
> + /* 420 */
> + 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:
> + input_fmts[i++] = output_fmt;
> + break;
> + }
> +
> + *num_input_fmts = i;
> +
> + if (*num_input_fmts == 0) {
> + kfree(input_fmts);
> + input_fmts = NULL;
> + }
> +
> + return input_fmts;
> +}
> +
> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state)
> +{
> + struct dw_hdmi *hdmi = bridge->driver_private;
> +
> + dev_dbg(hdmi->dev, "selected output format %x\n",
> + bridge_state->output_bus_cfg.format);

Nit: not aligned on the open parens.

> +
> + hdmi->hdmi_data.enc_out_bus_format =
> + bridge_state->output_bus_cfg.format;
> +
> + dev_dbg(hdmi->dev, "selected input format %x\n",
> + bridge_state->input_bus_cfg.format);
> +
> + hdmi->hdmi_data.enc_in_bus_format =
> + bridge_state->input_bus_cfg.format;
> +
> + return 0;
> +}
> +
> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
> {
> struct dw_hdmi *hdmi = bridge->driver_private;
> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
> .atomic_reset = drm_atomic_helper_bridge_reset,
> .attach = dw_hdmi_bridge_attach,
> .detach = dw_hdmi_bridge_detach,
> + .atomic_check = dw_hdmi_bridge_atomic_check,
> + .atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts,
> + .atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts,
> .enable = dw_hdmi_bridge_enable,
> .disable = dw_hdmi_bridge_disable,
> .mode_set = dw_hdmi_bridge_mode_set,
> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
>
> hdmi->bridge.driver_private = hdmi;
> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
> +

Nit: not sure this has to be part of that patch.

Looks good otherwise.

Reviewed-by: Boris Brezillon <[email protected]>

> #ifdef CONFIG_OF
> hdmi->bridge.of_node = pdev->dev.of_node;
> #endif

2020-02-07 13:37:58

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

On 07/02/2020 12:02, Boris Brezillon wrote:
> On Thu, 6 Feb 2020 20:18:27 +0100
> Neil Armstrong <[email protected]> wrote:
>
>> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to negociate
>
> ^ hooks?
>
>> the possible output and input formats for the current mode and monitor,
>> and use the negotiated formats in a basic atomic_check callback.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>
>> +
>> +/* Can return a maximum of 4 possible input formats for an output format */
>> +#define MAX_INPUT_SEL_FORMATS 4
>
> It seems to only be 3 in practice (based on the code) unless I missed
> something.

You're right...

>
>> +
>> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
>> + struct drm_bridge_state *bridge_state,
>> + struct drm_crtc_state *crtc_state,
>> + struct drm_connector_state *conn_state,
>> + u32 output_fmt,
>> + unsigned int *num_input_fmts)
>> +{
>> + u32 *input_fmts;
>> + int i = 0;
>> +
>> + *num_input_fmts = 0;
>> +
>> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
>> + GFP_KERNEL);
>> + if (!input_fmts)
>> + return NULL;
>> +
>> + switch (output_fmt) {
>> + /* 8bit */
>> + case MEDIA_BUS_FMT_RGB888_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + break;
>> + case MEDIA_BUS_FMT_YUV8_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY8_1X16:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + break;
>> +
>> + /* 10bit */
>> + case MEDIA_BUS_FMT_RGB101010_1X30:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + break;
>> + case MEDIA_BUS_FMT_YUV10_1X30:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY10_1X20:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + break;
>> +
>> + /* 12bit */
>> + case MEDIA_BUS_FMT_RGB121212_1X36:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + break;
>> + case MEDIA_BUS_FMT_YUV12_1X36:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY12_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + break;
>> +
>> + /* 16bit */
>> + case MEDIA_BUS_FMT_RGB161616_1X48:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> + break;
>> + case MEDIA_BUS_FMT_YUV16_1X48:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + break;
>> +
>> + /* 420 */
>> + 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:
>> + input_fmts[i++] = output_fmt;
>> + break;
>> + }
>> +
>> + *num_input_fmts = i;
>> +
>> + if (*num_input_fmts == 0) {
>> + kfree(input_fmts);
>> + input_fmts = NULL;
>> + }
>> +
>> + return input_fmts;
>> +}
>> +
>> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
>> + struct drm_bridge_state *bridge_state,
>> + struct drm_crtc_state *crtc_state,
>> + struct drm_connector_state *conn_state)
>> +{
>> + struct dw_hdmi *hdmi = bridge->driver_private;
>> +
>> + dev_dbg(hdmi->dev, "selected output format %x\n",
>> + bridge_state->output_bus_cfg.format);
>
> Nit: not aligned on the open parens.
>
>> +
>> + hdmi->hdmi_data.enc_out_bus_format =
>> + bridge_state->output_bus_cfg.format;
>> +
>> + dev_dbg(hdmi->dev, "selected input format %x\n",
>> + bridge_state->input_bus_cfg.format);
>> +
>> + hdmi->hdmi_data.enc_in_bus_format =
>> + bridge_state->input_bus_cfg.format;
>> +
>> + return 0;
>> +}
>> +
>> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
>> {
>> struct dw_hdmi *hdmi = bridge->driver_private;
>> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
>> .atomic_reset = drm_atomic_helper_bridge_reset,
>> .attach = dw_hdmi_bridge_attach,
>> .detach = dw_hdmi_bridge_detach,
>> + .atomic_check = dw_hdmi_bridge_atomic_check,
>> + .atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts,
>> + .atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts,
>> .enable = dw_hdmi_bridge_enable,
>> .disable = dw_hdmi_bridge_disable,
>> .mode_set = dw_hdmi_bridge_mode_set,
>> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
>>
>> hdmi->bridge.driver_private = hdmi;
>> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
>> +
>
> Nit: not sure this has to be part of that patch.
>
> Looks good otherwise.
>
> Reviewed-by: Boris Brezillon <[email protected]>


Thanks, will fixup in a v5 or while applying.

Neil

>
>> #ifdef CONFIG_OF
>> hdmi->bridge.of_node = pdev->dev.of_node;
>> #endif
>

2020-02-07 13:40:19

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 05/11] drm/bridge: synopsys: dw-hdmi: allow ycbcr420 modes for >= 0x200a

On 06/02/2020 20:18, Neil Armstrong wrote:
> Now the DW-HDMI Controller supports the HDMI2.0 modes, enable support
> for these modes in the connector if the platform supports them.
> We limit these modes to DW-HDMI IP version >= 0x200a which
> are designed to support HDMI2.0 display modes.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 6 ++++++
> include/drm/bridge/dw_hdmi.h | 1 +
> 2 files changed, 7 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> index 15048ad694bc..4b35ea1427df 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -3231,6 +3231,12 @@ __dw_hdmi_probe(struct platform_device *pdev,
> hdmi->bridge.of_node = pdev->dev.of_node;
> #endif
>
> + if (hdmi->version >= 0x200a)
> + hdmi->connector.ycbcr_420_allowed =
> + hdmi->plat_data->ycbcr_420_allowed;
> + else
> + hdmi->connector.ycbcr_420_allowed = false;
> +
> memset(&pdevinfo, 0, sizeof(pdevinfo));
> pdevinfo.parent = dev;
> pdevinfo.id = PLATFORM_DEVID_AUTO;
> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
> index 9d4d5cc47969..0b34a12c4a1c 100644
> --- a/include/drm/bridge/dw_hdmi.h
> +++ b/include/drm/bridge/dw_hdmi.h
> @@ -129,6 +129,7 @@ struct dw_hdmi_plat_data {
> unsigned long input_bus_format;
> unsigned long input_bus_encoding;
> bool use_drm_infoframe;
> + bool ycbcr_420_allowed;
>
> /* Vendor PHY support */
> const struct dw_hdmi_phy_ops *phy_ops;
>

Forgot to add:
Reviewed-by: Andrzej Hajda <[email protected]>

from [email protected]

Neil

2020-02-07 13:42:25

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 06/11] drm/meson: venc: make drm_display_mode const

On 06/02/2020 20:18, Neil Armstrong wrote:
> Before switching to bridge funcs, make sure drm_display_mode is passed
> as const to the venc functions.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/gpu/drm/meson/meson_venc.c | 2 +-
> drivers/gpu/drm/meson/meson_venc.h | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c
> index 4efd7864d5bf..a9ab78970bfe 100644
> --- a/drivers/gpu/drm/meson/meson_venc.c
> +++ b/drivers/gpu/drm/meson/meson_venc.c
> @@ -946,7 +946,7 @@ bool meson_venc_hdmi_venc_repeat(int vic)
> EXPORT_SYMBOL_GPL(meson_venc_hdmi_venc_repeat);
>
> void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
> - struct drm_display_mode *mode)
> + const struct drm_display_mode *mode)
> {
> union meson_hdmi_venc_mode *vmode = NULL;
> union meson_hdmi_venc_mode vmode_dmt;
> diff --git a/drivers/gpu/drm/meson/meson_venc.h b/drivers/gpu/drm/meson/meson_venc.h
> index 576768bdd08d..1abdcbdf51c0 100644
> --- a/drivers/gpu/drm/meson/meson_venc.h
> +++ b/drivers/gpu/drm/meson/meson_venc.h
> @@ -60,7 +60,7 @@ extern struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc;
> void meson_venci_cvbs_mode_set(struct meson_drm *priv,
> struct meson_cvbs_enci_mode *mode);
> void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
> - struct drm_display_mode *mode);
> + const struct drm_display_mode *mode);
> unsigned int meson_venci_get_field(struct meson_drm *priv);
>
> void meson_venc_enable_vsync(struct meson_drm *priv);
>


Forgot to add:

Reviewed-by: Boris Brezillon <[email protected]>

from [email protected]

Neil

2020-02-07 15:00:00

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v4 07/11] drm/meson: meson_dw_hdmi: add bridge and switch to drm_bridge_funcs

On Thu, 6 Feb 2020 20:18:30 +0100
Neil Armstrong <[email protected]> wrote:

> Switch the dw-hdmi driver to drm_bridge_funcs by implementing a new local
> bridge, connecting it to the dw-hdmi bridge, then implement the
> atomic_get_input_bus_fmts/atomic_get_output_bus_fmts.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/gpu/drm/meson/meson_dw_hdmi.c | 105 +++++++++++++++++++++-----
> 1 file changed, 85 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
> index 3bb7ffe5fc39..4b3809626f7e 100644
> --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
> +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
> @@ -16,6 +16,7 @@
>
> #include <drm/bridge/dw_hdmi.h>
> #include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> #include <drm/drm_device.h>
> #include <drm/drm_edid.h>
> #include <drm/drm_probe_helper.h>
> @@ -135,6 +136,7 @@ struct meson_dw_hdmi_data {
>
> struct meson_dw_hdmi {
> struct drm_encoder encoder;
> + struct drm_bridge bridge;
> struct dw_hdmi_plat_data dw_plat_data;
> struct meson_drm *priv;
> struct device *dev;
> @@ -151,6 +153,8 @@ struct meson_dw_hdmi {
> };
> #define encoder_to_meson_dw_hdmi(x) \
> container_of(x, struct meson_dw_hdmi, encoder)
> +#define bridge_to_meson_dw_hdmi(x) \
> + container_of(x, struct meson_dw_hdmi, bridge)
>
> static inline int dw_hdmi_is_compatible(struct meson_dw_hdmi *dw_hdmi,
> const char *compat)
> @@ -368,7 +372,7 @@ static inline void meson_dw_hdmi_phy_reset(struct meson_dw_hdmi *dw_hdmi)
> }
>
> static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
> - struct drm_display_mode *mode)
> + const struct drm_display_mode *mode)
> {
> struct meson_drm *priv = dw_hdmi->priv;
> int vic = drm_match_cea_mode(mode);
> @@ -663,6 +667,10 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
>
> /* Encoder */
>
> +static const u32 meson_dw_hdmi_out_bus_fmts[] = {
> + MEDIA_BUS_FMT_YUV8_1X24,
> +};
> +
> static void meson_venc_hdmi_encoder_destroy(struct drm_encoder *encoder)
> {
> drm_encoder_cleanup(encoder);
> @@ -672,16 +680,63 @@ static const struct drm_encoder_funcs meson_venc_hdmi_encoder_funcs = {
> .destroy = meson_venc_hdmi_encoder_destroy,
> };
>
> -static int meson_venc_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
> +static u32 *
> +meson_venc_hdmi_encoder_get_out_bus_fmts(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state,
> + unsigned int *num_output_fmts)
> +{
> + u32 *output_fmts;
> +
> + *num_output_fmts = ARRAY_SIZE(meson_dw_hdmi_out_bus_fmts);
> + output_fmts = kcalloc(*num_output_fmts, sizeof(*output_fmts),
> + GFP_KERNEL);
> + if (!output_fmts)
> + return NULL;
> +
> + memcpy(output_fmts, meson_dw_hdmi_out_bus_fmts, *num_output_fmts);
> +
> + return output_fmts;
> +}
> +
> +static u32 *
> +meson_venc_hdmi_encoder_get_inp_bus_fmts(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state,
> + u32 output_fmt,
> + unsigned int *num_input_fmts)
> +{
> + u32 *input_fmts = NULL;
> +
> + if (output_fmt == meson_dw_hdmi_out_bus_fmts[0]) {
> + *num_input_fmts = 1;
> + input_fmts = kcalloc(*num_input_fmts,
> + sizeof(*input_fmts),
> + GFP_KERNEL);
> + if (!input_fmts)
> + return NULL;
> +
> + input_fmts[0] = output_fmt;
> + } else {
> + *num_input_fmts = 0;
> + }
> +
> + return input_fmts;
> +}
> +
> +static int meson_venc_hdmi_encoder_atomic_check(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> struct drm_crtc_state *crtc_state,
> struct drm_connector_state *conn_state)
> {
> return 0;
> }
>
> -static void meson_venc_hdmi_encoder_disable(struct drm_encoder *encoder)
> +static void meson_venc_hdmi_encoder_disable(struct drm_bridge *bridge)
> {
> - struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder);
> + struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
> struct meson_drm *priv = dw_hdmi->priv;
>
> DRM_DEBUG_DRIVER("\n");
> @@ -693,9 +748,9 @@ static void meson_venc_hdmi_encoder_disable(struct drm_encoder *encoder)
> writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
> }
>
> -static void meson_venc_hdmi_encoder_enable(struct drm_encoder *encoder)
> +static void meson_venc_hdmi_encoder_enable(struct drm_bridge *bridge)
> {
> - struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder);
> + struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
> struct meson_drm *priv = dw_hdmi->priv;
>
> DRM_DEBUG_DRIVER("%s\n", priv->venc.hdmi_use_enci ? "VENCI" : "VENCP");
> @@ -706,11 +761,11 @@ static void meson_venc_hdmi_encoder_enable(struct drm_encoder *encoder)
> writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN));
> }
>
> -static void meson_venc_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> - struct drm_display_mode *mode,
> - struct drm_display_mode *adjusted_mode)
> +static void meson_venc_hdmi_encoder_mode_set(struct drm_bridge *bridge,
> + const struct drm_display_mode *mode,
> + const struct drm_display_mode *adjusted_mode)
> {
> - struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder);
> + struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
> struct meson_drm *priv = dw_hdmi->priv;
> int vic = drm_match_cea_mode(mode);
>
> @@ -726,12 +781,16 @@ static void meson_venc_hdmi_encoder_mode_set(struct drm_encoder *encoder,
> writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
> }
>
> -static const struct drm_encoder_helper_funcs
> - meson_venc_hdmi_encoder_helper_funcs = {
> - .atomic_check = meson_venc_hdmi_encoder_atomic_check,
> - .disable = meson_venc_hdmi_encoder_disable,
> - .enable = meson_venc_hdmi_encoder_enable,
> - .mode_set = meson_venc_hdmi_encoder_mode_set,
> +static const struct drm_bridge_funcs meson_venc_hdmi_encoder_bridge_funcs = {
> + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> + .atomic_get_output_bus_fmts = meson_venc_hdmi_encoder_get_out_bus_fmts,

Hm, can this bridge element be connected directly to the HDMI connector
without the dw-hdmi bridge in the middle? If that's not the case, then
you probably don't have to implement ->atomic_get_output_bus_fmts()
(only used on the last bridge element).

> + .atomic_get_input_bus_fmts = meson_venc_hdmi_encoder_get_inp_bus_fmts,
> + .atomic_reset = drm_atomic_helper_bridge_reset,
> + .atomic_check = meson_venc_hdmi_encoder_atomic_check,
> + .enable = meson_venc_hdmi_encoder_enable,
> + .disable = meson_venc_hdmi_encoder_disable,
> + .mode_set = meson_venc_hdmi_encoder_mode_set,
> };
>
> /* DW HDMI Regmap */
> @@ -852,6 +911,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
> struct drm_device *drm = data;
> struct meson_drm *priv = drm->dev_private;
> struct dw_hdmi_plat_data *dw_plat_data;
> + struct drm_bridge *next_bridge;
> struct drm_encoder *encoder;
> struct resource *res;
> int irq;
> @@ -953,8 +1013,6 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
>
> /* Encoder */
>
> - drm_encoder_helper_add(encoder, &meson_venc_hdmi_encoder_helper_funcs);
> -
> ret = drm_encoder_init(drm, encoder, &meson_venc_hdmi_encoder_funcs,
> DRM_MODE_ENCODER_TMDS, "meson_hdmi");
> if (ret) {
> @@ -962,6 +1020,9 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
> return ret;
> }
>
> + meson_dw_hdmi->bridge.funcs = &meson_venc_hdmi_encoder_bridge_funcs;
> + drm_bridge_attach(encoder, &meson_dw_hdmi->bridge, NULL);
> +
> encoder->possible_crtcs = BIT(0);
>
> DRM_DEBUG_DRIVER("encoder initialized\n");
> @@ -984,11 +1045,15 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
>
> platform_set_drvdata(pdev, meson_dw_hdmi);
>
> - meson_dw_hdmi->hdmi = dw_hdmi_bind(pdev, encoder,
> - &meson_dw_hdmi->dw_plat_data);
> + meson_dw_hdmi->hdmi = dw_hdmi_probe(pdev,
> + &meson_dw_hdmi->dw_plat_data);
> if (IS_ERR(meson_dw_hdmi->hdmi))
> return PTR_ERR(meson_dw_hdmi->hdmi);
>
> + next_bridge = of_drm_find_bridge(pdev->dev.of_node);
> + if (next_bridge)
> + drm_bridge_attach(encoder, next_bridge, &meson_dw_hdmi->bridge);
> +
> DRM_DEBUG_DRIVER("HDMI controller initialized\n");
>
> return 0;

2020-02-07 15:37:18

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 07/11] drm/meson: meson_dw_hdmi: add bridge and switch to drm_bridge_funcs

On 07/02/2020 15:57, Boris Brezillon wrote:
> On Thu, 6 Feb 2020 20:18:30 +0100
> Neil Armstrong <[email protected]> wrote:
>
>> Switch the dw-hdmi driver to drm_bridge_funcs by implementing a new local
>> bridge, connecting it to the dw-hdmi bridge, then implement the
>> atomic_get_input_bus_fmts/atomic_get_output_bus_fmts.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/gpu/drm/meson/meson_dw_hdmi.c | 105 +++++++++++++++++++++-----
>> 1 file changed, 85 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
>> index 3bb7ffe5fc39..4b3809626f7e 100644
>> --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
>> +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
>> @@ -16,6 +16,7 @@
>>
>> #include <drm/bridge/dw_hdmi.h>
>> #include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_bridge.h>
>> #include <drm/drm_device.h>
>> #include <drm/drm_edid.h>
>> #include <drm/drm_probe_helper.h>
>> @@ -135,6 +136,7 @@ struct meson_dw_hdmi_data {
>>
>> struct meson_dw_hdmi {
>> struct drm_encoder encoder;
>> + struct drm_bridge bridge;
>> struct dw_hdmi_plat_data dw_plat_data;
>> struct meson_drm *priv;
>> struct device *dev;
>> @@ -151,6 +153,8 @@ struct meson_dw_hdmi {
>> };
>> #define encoder_to_meson_dw_hdmi(x) \
>> container_of(x, struct meson_dw_hdmi, encoder)
>> +#define bridge_to_meson_dw_hdmi(x) \
>> + container_of(x, struct meson_dw_hdmi, bridge)
>>
>> static inline int dw_hdmi_is_compatible(struct meson_dw_hdmi *dw_hdmi,
>> const char *compat)
>> @@ -368,7 +372,7 @@ static inline void meson_dw_hdmi_phy_reset(struct meson_dw_hdmi *dw_hdmi)
>> }
>>
>> static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi,
>> - struct drm_display_mode *mode)
>> + const struct drm_display_mode *mode)
>> {
>> struct meson_drm *priv = dw_hdmi->priv;
>> int vic = drm_match_cea_mode(mode);
>> @@ -663,6 +667,10 @@ dw_hdmi_mode_valid(struct drm_connector *connector,
>>
>> /* Encoder */
>>
>> +static const u32 meson_dw_hdmi_out_bus_fmts[] = {
>> + MEDIA_BUS_FMT_YUV8_1X24,
>> +};
>> +
>> static void meson_venc_hdmi_encoder_destroy(struct drm_encoder *encoder)
>> {
>> drm_encoder_cleanup(encoder);
>> @@ -672,16 +680,63 @@ static const struct drm_encoder_funcs meson_venc_hdmi_encoder_funcs = {
>> .destroy = meson_venc_hdmi_encoder_destroy,
>> };
>>
>> -static int meson_venc_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
>> +static u32 *
>> +meson_venc_hdmi_encoder_get_out_bus_fmts(struct drm_bridge *bridge,
>> + struct drm_bridge_state *bridge_state,
>> + struct drm_crtc_state *crtc_state,
>> + struct drm_connector_state *conn_state,
>> + unsigned int *num_output_fmts)
>> +{
>> + u32 *output_fmts;
>> +
>> + *num_output_fmts = ARRAY_SIZE(meson_dw_hdmi_out_bus_fmts);
>> + output_fmts = kcalloc(*num_output_fmts, sizeof(*output_fmts),
>> + GFP_KERNEL);
>> + if (!output_fmts)
>> + return NULL;
>> +
>> + memcpy(output_fmts, meson_dw_hdmi_out_bus_fmts, *num_output_fmts);
>> +
>> + return output_fmts;
>> +}
>> +
>> +static u32 *
>> +meson_venc_hdmi_encoder_get_inp_bus_fmts(struct drm_bridge *bridge,
>> + struct drm_bridge_state *bridge_state,
>> + struct drm_crtc_state *crtc_state,
>> + struct drm_connector_state *conn_state,
>> + u32 output_fmt,
>> + unsigned int *num_input_fmts)
>> +{
>> + u32 *input_fmts = NULL;
>> +
>> + if (output_fmt == meson_dw_hdmi_out_bus_fmts[0]) {
>> + *num_input_fmts = 1;
>> + input_fmts = kcalloc(*num_input_fmts,
>> + sizeof(*input_fmts),
>> + GFP_KERNEL);
>> + if (!input_fmts)
>> + return NULL;
>> +
>> + input_fmts[0] = output_fmt;
>> + } else {
>> + *num_input_fmts = 0;
>> + }
>> +
>> + return input_fmts;
>> +}
>> +
>> +static int meson_venc_hdmi_encoder_atomic_check(struct drm_bridge *bridge,
>> + struct drm_bridge_state *bridge_state,
>> struct drm_crtc_state *crtc_state,
>> struct drm_connector_state *conn_state)
>> {
>> return 0;
>> }
>>
>> -static void meson_venc_hdmi_encoder_disable(struct drm_encoder *encoder)
>> +static void meson_venc_hdmi_encoder_disable(struct drm_bridge *bridge)
>> {
>> - struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder);
>> + struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
>> struct meson_drm *priv = dw_hdmi->priv;
>>
>> DRM_DEBUG_DRIVER("\n");
>> @@ -693,9 +748,9 @@ static void meson_venc_hdmi_encoder_disable(struct drm_encoder *encoder)
>> writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN));
>> }
>>
>> -static void meson_venc_hdmi_encoder_enable(struct drm_encoder *encoder)
>> +static void meson_venc_hdmi_encoder_enable(struct drm_bridge *bridge)
>> {
>> - struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder);
>> + struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
>> struct meson_drm *priv = dw_hdmi->priv;
>>
>> DRM_DEBUG_DRIVER("%s\n", priv->venc.hdmi_use_enci ? "VENCI" : "VENCP");
>> @@ -706,11 +761,11 @@ static void meson_venc_hdmi_encoder_enable(struct drm_encoder *encoder)
>> writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN));
>> }
>>
>> -static void meson_venc_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>> - struct drm_display_mode *mode,
>> - struct drm_display_mode *adjusted_mode)
>> +static void meson_venc_hdmi_encoder_mode_set(struct drm_bridge *bridge,
>> + const struct drm_display_mode *mode,
>> + const struct drm_display_mode *adjusted_mode)
>> {
>> - struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder);
>> + struct meson_dw_hdmi *dw_hdmi = bridge_to_meson_dw_hdmi(bridge);
>> struct meson_drm *priv = dw_hdmi->priv;
>> int vic = drm_match_cea_mode(mode);
>>
>> @@ -726,12 +781,16 @@ static void meson_venc_hdmi_encoder_mode_set(struct drm_encoder *encoder,
>> writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL));
>> }
>>
>> -static const struct drm_encoder_helper_funcs
>> - meson_venc_hdmi_encoder_helper_funcs = {
>> - .atomic_check = meson_venc_hdmi_encoder_atomic_check,
>> - .disable = meson_venc_hdmi_encoder_disable,
>> - .enable = meson_venc_hdmi_encoder_enable,
>> - .mode_set = meson_venc_hdmi_encoder_mode_set,
>> +static const struct drm_bridge_funcs meson_venc_hdmi_encoder_bridge_funcs = {
>> + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
>> + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
>> + .atomic_get_output_bus_fmts = meson_venc_hdmi_encoder_get_out_bus_fmts,
>
> Hm, can this bridge element be connected directly to the HDMI connector
> without the dw-hdmi bridge in the middle? If that's not the case, then
> you probably don't have to implement ->atomic_get_output_bus_fmts()
> (only used on the last bridge element).

No, the HDMI bridge is mandatory here.

Ok, makes sense, I'll remove it then.

Neil

>
>> + .atomic_get_input_bus_fmts = meson_venc_hdmi_encoder_get_inp_bus_fmts,
>> + .atomic_reset = drm_atomic_helper_bridge_reset,
>> + .atomic_check = meson_venc_hdmi_encoder_atomic_check,
>> + .enable = meson_venc_hdmi_encoder_enable,
>> + .disable = meson_venc_hdmi_encoder_disable,
>> + .mode_set = meson_venc_hdmi_encoder_mode_set,
>> };
>>
>> /* DW HDMI Regmap */
>> @@ -852,6 +911,7 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
>> struct drm_device *drm = data;
>> struct meson_drm *priv = drm->dev_private;
>> struct dw_hdmi_plat_data *dw_plat_data;
>> + struct drm_bridge *next_bridge;
>> struct drm_encoder *encoder;
>> struct resource *res;
>> int irq;
>> @@ -953,8 +1013,6 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
>>
>> /* Encoder */
>>
>> - drm_encoder_helper_add(encoder, &meson_venc_hdmi_encoder_helper_funcs);
>> -
>> ret = drm_encoder_init(drm, encoder, &meson_venc_hdmi_encoder_funcs,
>> DRM_MODE_ENCODER_TMDS, "meson_hdmi");
>> if (ret) {
>> @@ -962,6 +1020,9 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
>> return ret;
>> }
>>
>> + meson_dw_hdmi->bridge.funcs = &meson_venc_hdmi_encoder_bridge_funcs;
>> + drm_bridge_attach(encoder, &meson_dw_hdmi->bridge, NULL);
>> +
>> encoder->possible_crtcs = BIT(0);
>>
>> DRM_DEBUG_DRIVER("encoder initialized\n");
>> @@ -984,11 +1045,15 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master,
>>
>> platform_set_drvdata(pdev, meson_dw_hdmi);
>>
>> - meson_dw_hdmi->hdmi = dw_hdmi_bind(pdev, encoder,
>> - &meson_dw_hdmi->dw_plat_data);
>> + meson_dw_hdmi->hdmi = dw_hdmi_probe(pdev,
>> + &meson_dw_hdmi->dw_plat_data);
>> if (IS_ERR(meson_dw_hdmi->hdmi))
>> return PTR_ERR(meson_dw_hdmi->hdmi);
>>
>> + next_bridge = of_drm_find_bridge(pdev->dev.of_node);
>> + if (next_bridge)
>> + drm_bridge_attach(encoder, next_bridge, &meson_dw_hdmi->bridge);
>> +
>> DRM_DEBUG_DRIVER("HDMI controller initialized\n");
>>
>> return 0;
>

2020-02-17 06:40:16

by Jernej Skrabec

[permalink] [raw]
Subject: Re: [PATCH v4 02/11] drm/bridge: dw-hdmi: add max bpc connector property

Hi!

Dne četrtek, 06. februar 2020 ob 20:18:25 CET je Neil Armstrong napisal(a):
> From: Jonas Karlman <[email protected]>
>
> Add the max_bpc property to the dw-hdmi connector to prepare support
> for 10, 12 & 16bit output support.
>
> Signed-off-by: Jonas Karlman <[email protected]>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
> 9e0927d22db6..051001f77dd4 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -2406,6 +2406,10 @@ static int dw_hdmi_bridge_attach(struct drm_bridge
> *bridge) DRM_MODE_CONNECTOR_HDMIA,
> hdmi->ddc);
>
> + drm_atomic_helper_connector_reset(connector);

Why is this reset needed?

Best regards,
Jernej

> +
> + drm_connector_attach_max_bpc_property(connector, 8, 16);
> +
> if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
> drm_object_attach_property(&connector->base,
> connector->dev-
>mode_config.hdr_output_metadata_property, 0);




2020-02-21 08:51:03

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 02/11] drm/bridge: dw-hdmi: add max bpc connector property

Hi Jernej,

On 17/02/2020 07:38, Jernej Škrabec wrote:
> Hi!
>
> Dne četrtek, 06. februar 2020 ob 20:18:25 CET je Neil Armstrong napisal(a):
>> From: Jonas Karlman <[email protected]>
>>
>> Add the max_bpc property to the dw-hdmi connector to prepare support
>> for 10, 12 & 16bit output support.
>>
>> Signed-off-by: Jonas Karlman <[email protected]>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 4 ++++
>> 1 file changed, 4 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
>> 9e0927d22db6..051001f77dd4 100644
>> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> @@ -2406,6 +2406,10 @@ static int dw_hdmi_bridge_attach(struct drm_bridge
>> *bridge) DRM_MODE_CONNECTOR_HDMIA,
>> hdmi->ddc);
>>
>> + drm_atomic_helper_connector_reset(connector);
>
> Why is this reset needed?

I assume it's to allocate a new connector state to attach a the bpc propery.

But indeed, this helper is never used here, but only as callback to the drm_connector_funcs->reset.

But, amdgpu calls :
/*
* Some of the properties below require access to state, like bpc.
* Allocate some default initial connector state with our reset helper.
*/
if (aconnector->base.funcs->reset)
aconnector->base.funcs->reset(&aconnector->base);

which is the same.

Neil

>
> Best regards,
> Jernej
>
>> +
>> + drm_connector_attach_max_bpc_property(connector, 8, 16);
>> +
>> if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
>> drm_object_attach_property(&connector->base,
>> connector->dev-
>> mode_config.hdr_output_metadata_property, 0);
>
>
>
>

2020-02-29 07:48:16

by Jernej Skrabec

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

Hi Neil!

Dne četrtek, 06. februar 2020 ob 20:18:27 CET je Neil Armstrong napisal(a):
> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to negociate
> the possible output and input formats for the current mode and monitor,
> and use the negotiated formats in a basic atomic_check callback.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
> 1 file changed, 268 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
> fec4a4bcd1fe..15048ad694bc 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -2095,11 +2095,10 @@ 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)
> hdmi->hdmi_data.enc_in_bus_format =
> hdmi->plat_data->input_bus_format;
> - else
> + else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
> hdmi->hdmi_data.enc_in_bus_format =
MEDIA_BUS_FMT_RGB888_1X24;
>
> /* TOFIX: Get input encoding from plat data or fallback to none */
> @@ -2109,8 +2108,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct
> drm_display_mode *mode) 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 (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
> + hdmi->hdmi_data.enc_out_bus_format =
MEDIA_BUS_FMT_RGB888_1X24;
>
> hdmi->hdmi_data.pix_repet_factor = 0;
> hdmi->hdmi_data.hdcp_enable = 0;
> @@ -2388,6 +2387,267 @@ static const struct drm_connector_helper_funcs
> dw_hdmi_connector_helper_funcs = .atomic_check =
> dw_hdmi_connector_atomic_check,
> };
>
> +/*
> + * Possible output formats :
> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
> + * - MEDIA_BUS_FMT_YUV16_1X48,
> + * - MEDIA_BUS_FMT_RGB161616_1X48,
> + * - MEDIA_BUS_FMT_UYVY12_1X24,
> + * - MEDIA_BUS_FMT_YUV12_1X36,
> + * - MEDIA_BUS_FMT_RGB121212_1X36,
> + * - MEDIA_BUS_FMT_UYVY10_1X20,
> + * - MEDIA_BUS_FMT_YUV10_1X30,
> + * - MEDIA_BUS_FMT_RGB101010_1X30,
> + * - MEDIA_BUS_FMT_UYVY8_1X16,
> + * - MEDIA_BUS_FMT_YUV8_1X24,
> + * - MEDIA_BUS_FMT_RGB888_1X24,
> + */
> +
> +/* Can return a maximum of 12 possible output formats for a mode/connector
> */ +#define MAX_OUTPUT_SEL_FORMATS 12
> +
> +static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge
> *bridge, + struct
drm_bridge_state *bridge_state,
> + struct drm_crtc_state
*crtc_state,
> + struct
drm_connector_state *conn_state,
> + unsigned int
*num_output_fmts)
> +{
> + struct drm_connector *conn = conn_state->connector;
> + struct drm_display_info *info = &conn->display_info;
> + struct drm_display_mode *mode = &crtc_state->mode;
> + u8 max_bpc = conn_state->max_requested_bpc;
> + bool is_hdmi2_sink = info->hdmi.scdc.supported ||
> + (info->color_formats &
DRM_COLOR_FORMAT_YCRCB420);
> + u32 *output_fmts;
> + int i = 0;
> +
> + *num_output_fmts = 0;
> +
> + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts),
> + GFP_KERNEL);
> + if (!output_fmts)
> + return NULL;
> +
> + /*
> + * If the current mode enforces 4:2:0, force the output but format
> + * to 4:2:0 and do not add the YUV422/444/RGB formats
> + */
> + if (conn->ycbcr_420_allowed &&
> + (drm_mode_is_420_only(info, mode) ||
> + ())) {
> +
> + /* Order bus formats from 16bit to 8bit if supported */
> + if (max_bpc >= 16 && info->bpc == 16 &&
> + (info->hdmi.y420_dc_modes &
DRM_EDID_YCBCR420_DC_48))
> + output_fmts[i++] =
MEDIA_BUS_FMT_UYYVYY16_0_5X48;
> +
> + if (max_bpc >= 12 && info->bpc >= 12 &&
> + (info->hdmi.y420_dc_modes &
DRM_EDID_YCBCR420_DC_36))
> + output_fmts[i++] =
MEDIA_BUS_FMT_UYYVYY12_0_5X36;
> +
> + if (max_bpc >= 10 && info->bpc >= 10 &&
> + (info->hdmi.y420_dc_modes &
DRM_EDID_YCBCR420_DC_30))
> + output_fmts[i++] =
MEDIA_BUS_FMT_UYYVYY10_0_5X30;
> +
> + /* Default 8bit fallback */
> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
> +
> + *num_output_fmts = i;
> +
> + return output_fmts;

Driver shouldn't return just yet for case "is_hdmi2_sink &&
drm_mode_is_420_also(info, mode)", because monitor/TV also supports YCbCr
4:4:4 in that case. IMO YCbCr 4:4:4 should be even prefered. What do you
think?

Best regards,
Jernej

> + }
> +
> + /*
> + * Order bus formats from 16bit to 8bit and from YUV422 to RGB
> + * if supported. In any case the default RGB888 format is added
> + */
> +
> + if (max_bpc >= 16 && info->bpc == 16) {
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> +
> + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> + }
> +
> + if (max_bpc >= 12 && info->bpc >= 12) {
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> +
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> +
> + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + }
> +
> + if (max_bpc >= 10 && info->bpc >= 10) {
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> +
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> +
> + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + }
> +
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> +
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> +
> + /* Default 8bit RGB fallback */
> + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> +
> + *num_output_fmts = i;
> +
> + return output_fmts;
> +}
> +
> +/*
> + * Possible input formats :
> + * - MEDIA_BUS_FMT_RGB888_1X24
> + * - MEDIA_BUS_FMT_YUV8_1X24
> + * - MEDIA_BUS_FMT_UYVY8_1X16
> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
> + * - MEDIA_BUS_FMT_RGB101010_1X30
> + * - MEDIA_BUS_FMT_YUV10_1X30
> + * - MEDIA_BUS_FMT_UYVY10_1X20
> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
> + * - MEDIA_BUS_FMT_RGB121212_1X36
> + * - MEDIA_BUS_FMT_YUV12_1X36
> + * - MEDIA_BUS_FMT_UYVY12_1X24
> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
> + * - MEDIA_BUS_FMT_RGB161616_1X48
> + * - MEDIA_BUS_FMT_YUV16_1X48
> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
> + */
> +
> +/* Can return a maximum of 4 possible input formats for an output format */
> +#define MAX_INPUT_SEL_FORMATS 4
> +
> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge
> *bridge, + struct
drm_bridge_state *bridge_state,
> + struct drm_crtc_state
*crtc_state,
> + struct
drm_connector_state *conn_state,
> + u32 output_fmt,
> + unsigned int
*num_input_fmts)
> +{
> + u32 *input_fmts;
> + int i = 0;
> +
> + *num_input_fmts = 0;
> +
> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
> + GFP_KERNEL);
> + if (!input_fmts)
> + return NULL;
> +
> + switch (output_fmt) {
> + /* 8bit */
> + case MEDIA_BUS_FMT_RGB888_1X24:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> + break;
> + case MEDIA_BUS_FMT_YUV8_1X24:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> + break;
> + case MEDIA_BUS_FMT_UYVY8_1X16:
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> + break;
> +
> + /* 10bit */
> + case MEDIA_BUS_FMT_RGB101010_1X30:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> + break;
> + case MEDIA_BUS_FMT_YUV10_1X30:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + break;
> + case MEDIA_BUS_FMT_UYVY10_1X20:
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + break;
> +
> + /* 12bit */
> + case MEDIA_BUS_FMT_RGB121212_1X36:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> + break;
> + case MEDIA_BUS_FMT_YUV12_1X36:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + break;
> + case MEDIA_BUS_FMT_UYVY12_1X24:
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + break;
> +
> + /* 16bit */
> + case MEDIA_BUS_FMT_RGB161616_1X48:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> + break;
> + case MEDIA_BUS_FMT_YUV16_1X48:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> + break;
> +
> + /* 420 */
> + 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:
> + input_fmts[i++] = output_fmt;
> + break;
> + }
> +
> + *num_input_fmts = i;
> +
> + if (*num_input_fmts == 0) {
> + kfree(input_fmts);
> + input_fmts = NULL;
> + }
> +
> + return input_fmts;
> +}
> +
> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
> + struct drm_bridge_state
*bridge_state,
> + struct drm_crtc_state
*crtc_state,
> + struct drm_connector_state
*conn_state)
> +{
> + struct dw_hdmi *hdmi = bridge->driver_private;
> +
> + dev_dbg(hdmi->dev, "selected output format %x\n",
> + bridge_state->output_bus_cfg.format);
> +
> + hdmi->hdmi_data.enc_out_bus_format =
> + bridge_state->output_bus_cfg.format;
> +
> + dev_dbg(hdmi->dev, "selected input format %x\n",
> + bridge_state->input_bus_cfg.format);
> +
> + hdmi->hdmi_data.enc_in_bus_format =
> + bridge_state->input_bus_cfg.format;
> +
> + return 0;
> +}
> +
> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
> {
> struct dw_hdmi *hdmi = bridge->driver_private;
> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs
> dw_hdmi_bridge_funcs = { .atomic_reset = drm_atomic_helper_bridge_reset,
> .attach = dw_hdmi_bridge_attach,
> .detach = dw_hdmi_bridge_detach,
> + .atomic_check = dw_hdmi_bridge_atomic_check,
> + .atomic_get_output_bus_fmts =
dw_hdmi_bridge_atomic_get_output_bus_fmts,
> + .atomic_get_input_bus_fmts =
dw_hdmi_bridge_atomic_get_input_bus_fmts,
> .enable = dw_hdmi_bridge_enable,
> .disable = dw_hdmi_bridge_disable,
> .mode_set = dw_hdmi_bridge_mode_set,
> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
>
> hdmi->bridge.driver_private = hdmi;
> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
> +
> #ifdef CONFIG_OF
> hdmi->bridge.of_node = pdev->dev.of_node;
> #endif




2020-02-29 10:14:45

by Jonas Karlman

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

Hi Jernej,

On 2020-02-29 08:42, Jernej Škrabec wrote:
> Hi Neil!
>
> Dne četrtek, 06. februar 2020 ob 20:18:27 CET je Neil Armstrong napisal(a):
>> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to negociate
>> the possible output and input formats for the current mode and monitor,
>> and use the negotiated formats in a basic atomic_check callback.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
>> 1 file changed, 268 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
>> fec4a4bcd1fe..15048ad694bc 100644
>> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> @@ -2095,11 +2095,10 @@ 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)
>> hdmi->hdmi_data.enc_in_bus_format =
>> hdmi->plat_data->input_bus_format;
>> - else
>> + else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
>> hdmi->hdmi_data.enc_in_bus_format =
> MEDIA_BUS_FMT_RGB888_1X24;
>>
>> /* TOFIX: Get input encoding from plat data or fallback to none */
>> @@ -2109,8 +2108,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct
>> drm_display_mode *mode) 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 (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
>> + hdmi->hdmi_data.enc_out_bus_format =
> MEDIA_BUS_FMT_RGB888_1X24;
>>
>> hdmi->hdmi_data.pix_repet_factor = 0;
>> hdmi->hdmi_data.hdcp_enable = 0;
>> @@ -2388,6 +2387,267 @@ static const struct drm_connector_helper_funcs
>> dw_hdmi_connector_helper_funcs = .atomic_check =
>> dw_hdmi_connector_atomic_check,
>> };
>>
>> +/*
>> + * Possible output formats :
>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
>> + * - MEDIA_BUS_FMT_YUV16_1X48,
>> + * - MEDIA_BUS_FMT_RGB161616_1X48,
>> + * - MEDIA_BUS_FMT_UYVY12_1X24,
>> + * - MEDIA_BUS_FMT_YUV12_1X36,
>> + * - MEDIA_BUS_FMT_RGB121212_1X36,
>> + * - MEDIA_BUS_FMT_UYVY10_1X20,
>> + * - MEDIA_BUS_FMT_YUV10_1X30,
>> + * - MEDIA_BUS_FMT_RGB101010_1X30,
>> + * - MEDIA_BUS_FMT_UYVY8_1X16,
>> + * - MEDIA_BUS_FMT_YUV8_1X24,
>> + * - MEDIA_BUS_FMT_RGB888_1X24,
>> + */
>> +
>> +/* Can return a maximum of 12 possible output formats for a mode/connector
>> */ +#define MAX_OUTPUT_SEL_FORMATS 12
>> +
>> +static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge
>> *bridge, + struct
> drm_bridge_state *bridge_state,
>> + struct drm_crtc_state
> *crtc_state,
>> + struct
> drm_connector_state *conn_state,
>> + unsigned int
> *num_output_fmts)
>> +{
>> + struct drm_connector *conn = conn_state->connector;
>> + struct drm_display_info *info = &conn->display_info;
>> + struct drm_display_mode *mode = &crtc_state->mode;
>> + u8 max_bpc = conn_state->max_requested_bpc;
>> + bool is_hdmi2_sink = info->hdmi.scdc.supported ||
>> + (info->color_formats &
> DRM_COLOR_FORMAT_YCRCB420);
>> + u32 *output_fmts;
>> + int i = 0;
>> +
>> + *num_output_fmts = 0;
>> +
>> + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts),
>> + GFP_KERNEL);
>> + if (!output_fmts)
>> + return NULL;
>> +
>> + /*
>> + * If the current mode enforces 4:2:0, force the output but format
>> + * to 4:2:0 and do not add the YUV422/444/RGB formats
>> + */
>> + if (conn->ycbcr_420_allowed &&
>> + (drm_mode_is_420_only(info, mode) ||
>> + ())) {
>> +
>> + /* Order bus formats from 16bit to 8bit if supported */
>> + if (max_bpc >= 16 && info->bpc == 16 &&
>> + (info->hdmi.y420_dc_modes &
> DRM_EDID_YCBCR420_DC_48))
>> + output_fmts[i++] =
> MEDIA_BUS_FMT_UYYVYY16_0_5X48;
>> +
>> + if (max_bpc >= 12 && info->bpc >= 12 &&
>> + (info->hdmi.y420_dc_modes &
> DRM_EDID_YCBCR420_DC_36))
>> + output_fmts[i++] =
> MEDIA_BUS_FMT_UYYVYY12_0_5X36;
>> +
>> + if (max_bpc >= 10 && info->bpc >= 10 &&
>> + (info->hdmi.y420_dc_modes &
> DRM_EDID_YCBCR420_DC_30))
>> + output_fmts[i++] =
> MEDIA_BUS_FMT_UYYVYY10_0_5X30;
>> +
>> + /* Default 8bit fallback */
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
>> +
>> + *num_output_fmts = i;
>> +
>> + return output_fmts;
>
> Driver shouldn't return just yet for case "is_hdmi2_sink &&
> drm_mode_is_420_also(info, mode)", because monitor/TV also supports YCbCr
> 4:4:4 in that case. IMO YCbCr 4:4:4 should be even prefered. What do you
> think?

I think we need to have some way for controller driver and userspace to control what
hdmi output format gets selected. I know for a fact that some Samsung TV have issues
with 444 YCbCr modes at 4k 50/60hz but have no problems with 420 modes.
The Samsung TV edid lie and/or the TV is not fully following HDMI specs.

From a personal and mediaplayer userspace perspective I would like to prefer
420/444 YCbCr mode as soon as any yuv drm plane is active and rgb 444 anytime else.

On Rockchip SoCs the display controller cannot output yuv422 to dw-hdmi block,
the optimal output format selection in such case should put yuv422 last.

Maybe dw-hdmi can call a dw-hdmi glue driver callback to get the preferred output format order?

On a side note but related issue, the dw-hdmi format negotiation code should
probably also filter modes on tmds rate, something like [1].
It is needed to filter out deep color modes that is not supported by the sink or hdmi spec.

[1] https://github.com/Kwiboo/linux-rockchip/commit/fc3df6903384e764ab6ac59879c489cbef55fcbe

Best regards,
Jonas

>
> Best regards,
> Jernej
>
>> + }
>> +
>> + /*
>> + * Order bus formats from 16bit to 8bit and from YUV422 to RGB
>> + * if supported. In any case the default RGB888 format is added
>> + */
>> +
>> + if (max_bpc >= 16 && info->bpc == 16) {
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> +
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + }
>> +
>> + if (max_bpc >= 12 && info->bpc >= 12) {
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> +
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + }
>> +
>> + if (max_bpc >= 10 && info->bpc >= 10) {
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> +
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + }
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> +
>> + /* Default 8bit RGB fallback */
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> +
>> + *num_output_fmts = i;
>> +
>> + return output_fmts;
>> +}
>> +
>> +/*
>> + * Possible input formats :
>> + * - MEDIA_BUS_FMT_RGB888_1X24
>> + * - MEDIA_BUS_FMT_YUV8_1X24
>> + * - MEDIA_BUS_FMT_UYVY8_1X16
>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
>> + * - MEDIA_BUS_FMT_RGB101010_1X30
>> + * - MEDIA_BUS_FMT_YUV10_1X30
>> + * - MEDIA_BUS_FMT_UYVY10_1X20
>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
>> + * - MEDIA_BUS_FMT_RGB121212_1X36
>> + * - MEDIA_BUS_FMT_YUV12_1X36
>> + * - MEDIA_BUS_FMT_UYVY12_1X24
>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
>> + * - MEDIA_BUS_FMT_RGB161616_1X48
>> + * - MEDIA_BUS_FMT_YUV16_1X48
>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
>> + */
>> +
>> +/* Can return a maximum of 4 possible input formats for an output format */
>> +#define MAX_INPUT_SEL_FORMATS 4
>> +
>> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge
>> *bridge, + struct
> drm_bridge_state *bridge_state,
>> + struct drm_crtc_state
> *crtc_state,
>> + struct
> drm_connector_state *conn_state,
>> + u32 output_fmt,
>> + unsigned int
> *num_input_fmts)
>> +{
>> + u32 *input_fmts;
>> + int i = 0;
>> +
>> + *num_input_fmts = 0;
>> +
>> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
>> + GFP_KERNEL);
>> + if (!input_fmts)
>> + return NULL;
>> +
>> + switch (output_fmt) {
>> + /* 8bit */
>> + case MEDIA_BUS_FMT_RGB888_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + break;
>> + case MEDIA_BUS_FMT_YUV8_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY8_1X16:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + break;
>> +
>> + /* 10bit */
>> + case MEDIA_BUS_FMT_RGB101010_1X30:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + break;
>> + case MEDIA_BUS_FMT_YUV10_1X30:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY10_1X20:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + break;
>> +
>> + /* 12bit */
>> + case MEDIA_BUS_FMT_RGB121212_1X36:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + break;
>> + case MEDIA_BUS_FMT_YUV12_1X36:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY12_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + break;
>> +
>> + /* 16bit */
>> + case MEDIA_BUS_FMT_RGB161616_1X48:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> + break;
>> + case MEDIA_BUS_FMT_YUV16_1X48:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + break;
>> +
>> + /* 420 */
>> + 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:
>> + input_fmts[i++] = output_fmt;
>> + break;
>> + }
>> +
>> + *num_input_fmts = i;
>> +
>> + if (*num_input_fmts == 0) {
>> + kfree(input_fmts);
>> + input_fmts = NULL;
>> + }
>> +
>> + return input_fmts;
>> +}
>> +
>> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
>> + struct drm_bridge_state
> *bridge_state,
>> + struct drm_crtc_state
> *crtc_state,
>> + struct drm_connector_state
> *conn_state)
>> +{
>> + struct dw_hdmi *hdmi = bridge->driver_private;
>> +
>> + dev_dbg(hdmi->dev, "selected output format %x\n",
>> + bridge_state->output_bus_cfg.format);
>> +
>> + hdmi->hdmi_data.enc_out_bus_format =
>> + bridge_state->output_bus_cfg.format;
>> +
>> + dev_dbg(hdmi->dev, "selected input format %x\n",
>> + bridge_state->input_bus_cfg.format);
>> +
>> + hdmi->hdmi_data.enc_in_bus_format =
>> + bridge_state->input_bus_cfg.format;
>> +
>> + return 0;
>> +}
>> +
>> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
>> {
>> struct dw_hdmi *hdmi = bridge->driver_private;
>> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs
>> dw_hdmi_bridge_funcs = { .atomic_reset = drm_atomic_helper_bridge_reset,
>> .attach = dw_hdmi_bridge_attach,
>> .detach = dw_hdmi_bridge_detach,
>> + .atomic_check = dw_hdmi_bridge_atomic_check,
>> + .atomic_get_output_bus_fmts =
> dw_hdmi_bridge_atomic_get_output_bus_fmts,
>> + .atomic_get_input_bus_fmts =
> dw_hdmi_bridge_atomic_get_input_bus_fmts,
>> .enable = dw_hdmi_bridge_enable,
>> .disable = dw_hdmi_bridge_disable,
>> .mode_set = dw_hdmi_bridge_mode_set,
>> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
>>
>> hdmi->bridge.driver_private = hdmi;
>> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
>> +
>> #ifdef CONFIG_OF
>> hdmi->bridge.of_node = pdev->dev.of_node;
>> #endif
>
>
>
>

2020-02-29 11:07:52

by Jernej Skrabec

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

Dne sobota, 29. februar 2020 ob 11:09:14 CET je Jonas Karlman napisal(a):
> Hi Jernej,
>
> On 2020-02-29 08:42, Jernej Škrabec wrote:
> > Hi Neil!
> >
> > Dne četrtek, 06. februar 2020 ob 20:18:27 CET je Neil Armstrong
napisal(a):
> >> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to
> >> negociate
> >> the possible output and input formats for the current mode and monitor,
> >> and use the negotiated formats in a basic atomic_check callback.
> >>
> >> Signed-off-by: Neil Armstrong <[email protected]>
> >> ---
> >>
> >> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
> >> 1 file changed, 268 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> >> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
> >> fec4a4bcd1fe..15048ad694bc 100644
> >> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> >> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> >> @@ -2095,11 +2095,10 @@ 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)
> >>
> >> hdmi->hdmi_data.enc_in_bus_format =
> >>
> >> hdmi->plat_data->input_bus_format;
> >>
> >> - else
> >> + else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
> >>
> >> hdmi->hdmi_data.enc_in_bus_format =
> >
> > MEDIA_BUS_FMT_RGB888_1X24;
> >
> >> /* TOFIX: Get input encoding from plat data or fallback to none */
> >>
> >> @@ -2109,8 +2108,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
> >> struct
> >> drm_display_mode *mode) 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 (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
> >> + hdmi->hdmi_data.enc_out_bus_format =
> >
> > MEDIA_BUS_FMT_RGB888_1X24;
> >
> >> hdmi->hdmi_data.pix_repet_factor = 0;
> >> hdmi->hdmi_data.hdcp_enable = 0;
> >>
> >> @@ -2388,6 +2387,267 @@ static const struct drm_connector_helper_funcs
> >> dw_hdmi_connector_helper_funcs = .atomic_check =
> >> dw_hdmi_connector_atomic_check,
> >>
> >> };
> >>
> >> +/*
> >> + * Possible output formats :
> >> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
> >> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
> >> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
> >> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
> >> + * - MEDIA_BUS_FMT_YUV16_1X48,
> >> + * - MEDIA_BUS_FMT_RGB161616_1X48,
> >> + * - MEDIA_BUS_FMT_UYVY12_1X24,
> >> + * - MEDIA_BUS_FMT_YUV12_1X36,
> >> + * - MEDIA_BUS_FMT_RGB121212_1X36,
> >> + * - MEDIA_BUS_FMT_UYVY10_1X20,
> >> + * - MEDIA_BUS_FMT_YUV10_1X30,
> >> + * - MEDIA_BUS_FMT_RGB101010_1X30,
> >> + * - MEDIA_BUS_FMT_UYVY8_1X16,
> >> + * - MEDIA_BUS_FMT_YUV8_1X24,
> >> + * - MEDIA_BUS_FMT_RGB888_1X24,
> >> + */
> >> +
> >> +/* Can return a maximum of 12 possible output formats for a
> >> mode/connector
> >> */ +#define MAX_OUTPUT_SEL_FORMATS 12
> >> +
> >> +static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge
> >> *bridge, + struct
> >
> > drm_bridge_state *bridge_state,
> >
> >> + struct drm_crtc_state
> >
> > *crtc_state,
> >
> >> + struct
> >
> > drm_connector_state *conn_state,
> >
> >> + unsigned int
> >
> > *num_output_fmts)
> >
> >> +{
> >> + struct drm_connector *conn = conn_state->connector;
> >> + struct drm_display_info *info = &conn->display_info;
> >> + struct drm_display_mode *mode = &crtc_state->mode;
> >> + u8 max_bpc = conn_state->max_requested_bpc;
> >> + bool is_hdmi2_sink = info->hdmi.scdc.supported ||
> >> + (info->color_formats &
> >
> > DRM_COLOR_FORMAT_YCRCB420);
> >
> >> + u32 *output_fmts;
> >> + int i = 0;
> >> +
> >> + *num_output_fmts = 0;
> >> +
> >> + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS,
sizeof(*output_fmts),
> >> + GFP_KERNEL);
> >> + if (!output_fmts)
> >> + return NULL;
> >> +
> >> + /*
> >> + * If the current mode enforces 4:2:0, force the output but format
> >> + * to 4:2:0 and do not add the YUV422/444/RGB formats
> >> + */
> >> + if (conn->ycbcr_420_allowed &&
> >> + (drm_mode_is_420_only(info, mode) ||
> >> + ())) {
> >> +
> >> + /* Order bus formats from 16bit to 8bit if supported */
> >> + if (max_bpc >= 16 && info->bpc == 16 &&
> >> + (info->hdmi.y420_dc_modes &
> >
> > DRM_EDID_YCBCR420_DC_48))
> >
> >> + output_fmts[i++] =
> >
> > MEDIA_BUS_FMT_UYYVYY16_0_5X48;
> >
> >> +
> >> + if (max_bpc >= 12 && info->bpc >= 12 &&
> >> + (info->hdmi.y420_dc_modes &
> >
> > DRM_EDID_YCBCR420_DC_36))
> >
> >> + output_fmts[i++] =
> >
> > MEDIA_BUS_FMT_UYYVYY12_0_5X36;
> >
> >> +
> >> + if (max_bpc >= 10 && info->bpc >= 10 &&
> >> + (info->hdmi.y420_dc_modes &
> >
> > DRM_EDID_YCBCR420_DC_30))
> >
> >> + output_fmts[i++] =
> >
> > MEDIA_BUS_FMT_UYYVYY10_0_5X30;
> >
> >> +
> >> + /* Default 8bit fallback */
> >> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
> >> +
> >> + *num_output_fmts = i;
> >> +
> >> + return output_fmts;
> >
> > Driver shouldn't return just yet for case "is_hdmi2_sink &&
> > drm_mode_is_420_also(info, mode)", because monitor/TV also supports YCbCr
> > 4:4:4 in that case. IMO YCbCr 4:4:4 should be even prefered. What do you
> > think?
>
> I think we need to have some way for controller driver and userspace to
> control what hdmi output format gets selected. I know for a fact that some
> Samsung TV have issues with 444 YCbCr modes at 4k 50/60hz but have no
> problems with 420 modes. The Samsung TV edid lie and/or the TV is not fully
> following HDMI specs

Interesting, maybe just some bandwith issues? I guess we should have a
blacklist for such cases. I know that at least Allwinner BSP driver for DW
HDMI has a blacklist, in this case for 2 monitors which claim to support YCbCr
4:4:4 mode but they not.

>
> From a personal and mediaplayer userspace perspective I would like to prefer
> 420/444 YCbCr mode as soon as any yuv drm plane is active and rgb 444
> anytime else.

I would argue that YCbCr is always prefered:
- CEA 861 prefers it for all CEA modes
- avoid dealing with quantization range - it's always limited range for < hdmi
2.0 and selectable on some hdmi 2.0 capable sinks

Anyway, there is no universal solution to avoid color space conversion in all
cases. For example, due to design of Allwinner Display Engine 2, it can only
work in RGB format internally, but it can convert final output to YCbCr right
before it's feeded to DW HDMI. On the other hand, you have meson display
pipeline which works in YCbCr format internally and relies on DW HDMI CSC unit
to convert output to RGB. Fortunately, there are also display pipelines which
can work in any colorspace internally, like Allwinner Display Engine 3. Not
sure in which category Rockchip display pipeline falls into.

>
> On Rockchip SoCs the display controller cannot output yuv422 to dw-hdmi
> block, the optimal output format selection in such case should put yuv422
> last.

Note that DW HDMI has decimation support which converts 444 to 422. This is
part of CSC unit.

Side note: CSC unit is optional feature of DW-HDMI and presence is indicated
by config register. However, it seems that CSC support is broken in DW HDMI
controller on 40nm Allwinner SoCs. If it is enabled, TV loses signal. I have
to investigate if only CSC is broken or decimation also don't work.

>
> Maybe dw-hdmi can call a dw-hdmi glue driver callback to get the preferred
> output format order?
>
> On a side note but related issue, the dw-hdmi format negotiation code should
> probably also filter modes on tmds rate, something like [1].
> It is needed to filter out deep color modes that is not supported by the
> sink or hdmi spec.

Ah, you mean on TMDS rates supported by the sink. Could this avoid Samsung
deep color issues? If so, we don't need blacklist then.

Best regards,
Jernej

>
> [1]
> https://github.com/Kwiboo/linux-rockchip/commit/fc3df6903384e764ab6ac59879c
> 489cbef55fcbe
>
> Best regards,
> Jonas
>
> > Best regards,
> > Jernej
> >
> >> + }
> >> +
> >> + /*
> >> + * Order bus formats from 16bit to 8bit and from YUV422 to RGB
> >> + * if supported. In any case the default RGB888 format is added
> >> + */
> >> +
> >> + if (max_bpc >= 16 && info->bpc == 16) {
> >> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> >> + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> >> +
> >> + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> >> + }
> >> +
> >> + if (max_bpc >= 12 && info->bpc >= 12) {
> >> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> >> + output_fmts[i++] =
MEDIA_BUS_FMT_UYVY12_1X24;
> >> +
> >> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> >> + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> >> +
> >> + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> >> + }
> >> +
> >> + if (max_bpc >= 10 && info->bpc >= 10) {
> >> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> >> + output_fmts[i++] =
MEDIA_BUS_FMT_UYVY10_1X20;
> >> +
> >> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> >> + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> >> +
> >> + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> >> + }
> >> +
> >> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> >> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> >> +
> >> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> >> + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> >> +
> >> + /* Default 8bit RGB fallback */
> >> + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> >> +
> >> + *num_output_fmts = i;
> >> +
> >> + return output_fmts;
> >> +}
> >> +
> >> +/*
> >> + * Possible input formats :
> >> + * - MEDIA_BUS_FMT_RGB888_1X24
> >> + * - MEDIA_BUS_FMT_YUV8_1X24
> >> + * - MEDIA_BUS_FMT_UYVY8_1X16
> >> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
> >> + * - MEDIA_BUS_FMT_RGB101010_1X30
> >> + * - MEDIA_BUS_FMT_YUV10_1X30
> >> + * - MEDIA_BUS_FMT_UYVY10_1X20
> >> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
> >> + * - MEDIA_BUS_FMT_RGB121212_1X36
> >> + * - MEDIA_BUS_FMT_YUV12_1X36
> >> + * - MEDIA_BUS_FMT_UYVY12_1X24
> >> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
> >> + * - MEDIA_BUS_FMT_RGB161616_1X48
> >> + * - MEDIA_BUS_FMT_YUV16_1X48
> >> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
> >> + */
> >> +
> >> +/* Can return a maximum of 4 possible input formats for an output format
> >> */ +#define MAX_INPUT_SEL_FORMATS 4
> >> +
> >> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge
> >> *bridge, + struct
> >
> > drm_bridge_state *bridge_state,
> >
> >> + struct drm_crtc_state
> >
> > *crtc_state,
> >
> >> + struct
> >
> > drm_connector_state *conn_state,
> >
> >> + u32 output_fmt,
> >> + unsigned int
> >
> > *num_input_fmts)
> >
> >> +{
> >> + u32 *input_fmts;
> >> + int i = 0;
> >> +
> >> + *num_input_fmts = 0;
> >> +
> >> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
> >> + GFP_KERNEL);
> >> + if (!input_fmts)
> >> + return NULL;
> >> +
> >> + switch (output_fmt) {
> >> + /* 8bit */
> >> + case MEDIA_BUS_FMT_RGB888_1X24:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> >> + break;
> >> + case MEDIA_BUS_FMT_YUV8_1X24:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> >> + break;
> >> + case MEDIA_BUS_FMT_UYVY8_1X16:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> >> + break;
> >> +
> >> + /* 10bit */
> >> + case MEDIA_BUS_FMT_RGB101010_1X30:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> >> + break;
> >> + case MEDIA_BUS_FMT_YUV10_1X30:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> >> + break;
> >> + case MEDIA_BUS_FMT_UYVY10_1X20:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> >> + break;
> >> +
> >> + /* 12bit */
> >> + case MEDIA_BUS_FMT_RGB121212_1X36:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> >> + break;
> >> + case MEDIA_BUS_FMT_YUV12_1X36:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> >> + break;
> >> + case MEDIA_BUS_FMT_UYVY12_1X24:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> >> + break;
> >> +
> >> + /* 16bit */
> >> + case MEDIA_BUS_FMT_RGB161616_1X48:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> >> + break;
> >> + case MEDIA_BUS_FMT_YUV16_1X48:
> >> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> >> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> >> + break;
> >> +
> >> + /* 420 */
> >> + 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:
> >> + input_fmts[i++] = output_fmt;
> >> + break;
> >> + }
> >> +
> >> + *num_input_fmts = i;
> >> +
> >> + if (*num_input_fmts == 0) {
> >> + kfree(input_fmts);
> >> + input_fmts = NULL;
> >> + }
> >> +
> >> + return input_fmts;
> >> +}
> >> +
> >> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
> >> + struct drm_bridge_state
> >
> > *bridge_state,
> >
> >> + struct drm_crtc_state
> >
> > *crtc_state,
> >
> >> + struct drm_connector_state
> >
> > *conn_state)
> >
> >> +{
> >> + struct dw_hdmi *hdmi = bridge->driver_private;
> >> +
> >> + dev_dbg(hdmi->dev, "selected output format %x\n",
> >> + bridge_state->output_bus_cfg.format);
> >> +
> >> + hdmi->hdmi_data.enc_out_bus_format =
> >> + bridge_state->output_bus_cfg.format;
> >> +
> >> + dev_dbg(hdmi->dev, "selected input format %x\n",
> >> + bridge_state->input_bus_cfg.format);
> >> +
> >> + hdmi->hdmi_data.enc_in_bus_format =
> >> + bridge_state->input_bus_cfg.format;
> >> +
> >> + return 0;
> >> +}
> >> +
> >>
> >> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
> >> {
> >>
> >> struct dw_hdmi *hdmi = bridge->driver_private;
> >>
> >> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs
> >> dw_hdmi_bridge_funcs = { .atomic_reset = drm_atomic_helper_bridge_reset,
> >>
> >> .attach = dw_hdmi_bridge_attach,
> >> .detach = dw_hdmi_bridge_detach,
> >>
> >> + .atomic_check = dw_hdmi_bridge_atomic_check,
> >> + .atomic_get_output_bus_fmts =
> >
> > dw_hdmi_bridge_atomic_get_output_bus_fmts,
> >
> >> + .atomic_get_input_bus_fmts =
> >
> > dw_hdmi_bridge_atomic_get_input_bus_fmts,
> >
> >> .enable = dw_hdmi_bridge_enable,
> >> .disable = dw_hdmi_bridge_disable,
> >> .mode_set = dw_hdmi_bridge_mode_set,
> >>
> >> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
> >>
> >> hdmi->bridge.driver_private = hdmi;
> >> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
> >>
> >> +
> >>
> >> #ifdef CONFIG_OF
> >>
> >> hdmi->bridge.of_node = pdev->dev.of_node;
> >>
> >> #endif




2020-02-29 12:15:53

by Jonas Karlman

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

On 2020-02-29 12:07, Jernej Škrabec wrote:
> Dne sobota, 29. februar 2020 ob 11:09:14 CET je Jonas Karlman napisal(a):
>> Hi Jernej,
>>
>> On 2020-02-29 08:42, Jernej Škrabec wrote:
>>> Hi Neil!
>>>
>>> Dne četrtek, 06. februar 2020 ob 20:18:27 CET je Neil Armstrong
> napisal(a):
>>>> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to
>>>> negociate
>>>> the possible output and input formats for the current mode and monitor,
>>>> and use the negotiated formats in a basic atomic_check callback.
>>>>
>>>> Signed-off-by: Neil Armstrong <[email protected]>
>>>> ---
>>>>
>>>> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
>>>> 1 file changed, 268 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>>>> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
>>>> fec4a4bcd1fe..15048ad694bc 100644
>>>> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>>>> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>>>> @@ -2095,11 +2095,10 @@ 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)
>>>>
>>>> hdmi->hdmi_data.enc_in_bus_format =
>>>>
>>>> hdmi->plat_data->input_bus_format;
>>>>
>>>> - else
>>>> + else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
>>>>
>>>> hdmi->hdmi_data.enc_in_bus_format =
>>>
>>> MEDIA_BUS_FMT_RGB888_1X24;
>>>
>>>> /* TOFIX: Get input encoding from plat data or fallback to none */
>>>>
>>>> @@ -2109,8 +2108,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
>>>> struct
>>>> drm_display_mode *mode) 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 (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
>>>> + hdmi->hdmi_data.enc_out_bus_format =
>>>
>>> MEDIA_BUS_FMT_RGB888_1X24;
>>>
>>>> hdmi->hdmi_data.pix_repet_factor = 0;
>>>> hdmi->hdmi_data.hdcp_enable = 0;
>>>>
>>>> @@ -2388,6 +2387,267 @@ static const struct drm_connector_helper_funcs
>>>> dw_hdmi_connector_helper_funcs = .atomic_check =
>>>> dw_hdmi_connector_atomic_check,
>>>>
>>>> };
>>>>
>>>> +/*
>>>> + * Possible output formats :
>>>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
>>>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
>>>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
>>>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
>>>> + * - MEDIA_BUS_FMT_YUV16_1X48,
>>>> + * - MEDIA_BUS_FMT_RGB161616_1X48,
>>>> + * - MEDIA_BUS_FMT_UYVY12_1X24,
>>>> + * - MEDIA_BUS_FMT_YUV12_1X36,
>>>> + * - MEDIA_BUS_FMT_RGB121212_1X36,
>>>> + * - MEDIA_BUS_FMT_UYVY10_1X20,
>>>> + * - MEDIA_BUS_FMT_YUV10_1X30,
>>>> + * - MEDIA_BUS_FMT_RGB101010_1X30,
>>>> + * - MEDIA_BUS_FMT_UYVY8_1X16,
>>>> + * - MEDIA_BUS_FMT_YUV8_1X24,
>>>> + * - MEDIA_BUS_FMT_RGB888_1X24,
>>>> + */
>>>> +
>>>> +/* Can return a maximum of 12 possible output formats for a
>>>> mode/connector
>>>> */ +#define MAX_OUTPUT_SEL_FORMATS 12
>>>> +
>>>> +static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge
>>>> *bridge, + struct
>>>
>>> drm_bridge_state *bridge_state,
>>>
>>>> + struct drm_crtc_state
>>>
>>> *crtc_state,
>>>
>>>> + struct
>>>
>>> drm_connector_state *conn_state,
>>>
>>>> + unsigned int
>>>
>>> *num_output_fmts)
>>>
>>>> +{
>>>> + struct drm_connector *conn = conn_state->connector;
>>>> + struct drm_display_info *info = &conn->display_info;
>>>> + struct drm_display_mode *mode = &crtc_state->mode;
>>>> + u8 max_bpc = conn_state->max_requested_bpc;
>>>> + bool is_hdmi2_sink = info->hdmi.scdc.supported ||
>>>> + (info->color_formats &
>>>
>>> DRM_COLOR_FORMAT_YCRCB420);
>>>
>>>> + u32 *output_fmts;
>>>> + int i = 0;
>>>> +
>>>> + *num_output_fmts = 0;
>>>> +
>>>> + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS,
> sizeof(*output_fmts),
>>>> + GFP_KERNEL);
>>>> + if (!output_fmts)
>>>> + return NULL;
>>>> +
>>>> + /*
>>>> + * If the current mode enforces 4:2:0, force the output but format
>>>> + * to 4:2:0 and do not add the YUV422/444/RGB formats
>>>> + */
>>>> + if (conn->ycbcr_420_allowed &&
>>>> + (drm_mode_is_420_only(info, mode) ||
>>>> + ())) {
>>>> +
>>>> + /* Order bus formats from 16bit to 8bit if supported */
>>>> + if (max_bpc >= 16 && info->bpc == 16 &&
>>>> + (info->hdmi.y420_dc_modes &
>>>
>>> DRM_EDID_YCBCR420_DC_48))
>>>
>>>> + output_fmts[i++] =
>>>
>>> MEDIA_BUS_FMT_UYYVYY16_0_5X48;
>>>
>>>> +
>>>> + if (max_bpc >= 12 && info->bpc >= 12 &&
>>>> + (info->hdmi.y420_dc_modes &
>>>
>>> DRM_EDID_YCBCR420_DC_36))
>>>
>>>> + output_fmts[i++] =
>>>
>>> MEDIA_BUS_FMT_UYYVYY12_0_5X36;
>>>
>>>> +
>>>> + if (max_bpc >= 10 && info->bpc >= 10 &&
>>>> + (info->hdmi.y420_dc_modes &
>>>
>>> DRM_EDID_YCBCR420_DC_30))
>>>
>>>> + output_fmts[i++] =
>>>
>>> MEDIA_BUS_FMT_UYYVYY10_0_5X30;
>>>
>>>> +
>>>> + /* Default 8bit fallback */
>>>> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
>>>> +
>>>> + *num_output_fmts = i;
>>>> +
>>>> + return output_fmts;
>>>
>>> Driver shouldn't return just yet for case "is_hdmi2_sink &&
>>> drm_mode_is_420_also(info, mode)", because monitor/TV also supports YCbCr
>>> 4:4:4 in that case. IMO YCbCr 4:4:4 should be even prefered. What do you
>>> think?
>>
>> I think we need to have some way for controller driver and userspace to
>> control what hdmi output format gets selected. I know for a fact that some
>> Samsung TV have issues with 444 YCbCr modes at 4k 50/60hz but have no
>> problems with 420 modes. The Samsung TV edid lie and/or the TV is not fully
>> following HDMI specs
>
> Interesting, maybe just some bandwith issues? I guess we should have a
> blacklist for such cases. I know that at least Allwinner BSP driver for DW
> HDMI has a blacklist, in this case for 2 monitors which claim to support YCbCr
> 4:4:4 mode but they not.
>
>>
>> From a personal and mediaplayer userspace perspective I would like to prefer
>> 420/444 YCbCr mode as soon as any yuv drm plane is active and rgb 444
>> anytime else.
>
> I would argue that YCbCr is always prefered:
> - CEA 861 prefers it for all CEA modes
> - avoid dealing with quantization range - it's always limited range for < hdmi
> 2.0 and selectable on some hdmi 2.0 capable sinks

This is probably true if your sink is a TV, for monitors with HDMI connections
I would expect or at least like to have an option to use rgb444 full range mode
for any desktop or gaming or similar use-case.

>
> Anyway, there is no universal solution to avoid color space conversion in all
> cases. For example, due to design of Allwinner Display Engine 2, it can only
> work in RGB format internally, but it can convert final output to YCbCr right
> before it's feeded to DW HDMI. On the other hand, you have meson display
> pipeline which works in YCbCr format internally and relies on DW HDMI CSC unit
> to convert output to RGB. Fortunately, there are also display pipelines which
> can work in any colorspace internally, like Allwinner Display Engine 3. Not
> sure in which category Rockchip display pipeline falls into.

Agree, and for Rockchip to my knowledge RK3288 VOP (Video Output Processor)
can only output 8/10-bit rgb (full range) to DW-HDMI.
And the VOP in RK322x/RK3328/RK3399 can output 8/10-bit rgb and 444/420 yuv.
I do not know how the VOP handles internal color conversion.

>
>>
>> On Rockchip SoCs the display controller cannot output yuv422 to dw-hdmi
>> block, the optimal output format selection in such case should put yuv422
>> last.
>
> Note that DW HDMI has decimation support which converts 444 to 422. This is
> part of CSC unit.

This works as it should (after a patch to disable color conversion for 444 to 422),
the issue I see is that the format negotiation code will currently favor 444 to 422
decimation over using 444 output.
Note: 422 will not be selected over 444 currently in case sink report deep color support
due to drm edid parsing code following HDMI 1.3 spec, see [1].

[1] https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/drm_edid.c#L4801-L4806

>
> Side note: CSC unit is optional feature of DW-HDMI and presence is indicated
> by config register. However, it seems that CSC support is broken in DW HDMI
> controller on 40nm Allwinner SoCs. If it is enabled, TV loses signal. I have
> to investigate if only CSC is broken or decimation also don't work.
>
>>
>> Maybe dw-hdmi can call a dw-hdmi glue driver callback to get the preferred
>> output format order?
>>
>> On a side note but related issue, the dw-hdmi format negotiation code should
>> probably also filter modes on tmds rate, something like [1].
>> It is needed to filter out deep color modes that is not supported by the
>> sink or hdmi spec.
>
> Ah, you mean on TMDS rates supported by the sink. Could this avoid Samsung
> deep color issues? If so, we don't need blacklist then.

I do not think so, the initial issue was reported with intel graphics but looking
at the edid and comparing to the manual of supported modes they do not fully match.
See manual info at [2] and edid when UHD mode is off at [3] and when on at [4].

[2] https://gitlab.freedesktop.org/drm/intel/uploads/36630c8a727bf8c66a53fd7470c88383/MU7000_manual_-_UHD_colour_modes.PNG
[3] http://ix.io/1H25
[4] http://ix.io/1H2e

>
> Best regards,
> Jernej
>
>>
>> [1]
>> https://github.com/Kwiboo/linux-rockchip/commit/fc3df6903384e764ab6ac59879c
>> 489cbef55fcbe
>>
>> Best regards,
>> Jonas
>>
>>> Best regards,
>>> Jernej
>>>
>>>> + }
>>>> +
>>>> + /*
>>>> + * Order bus formats from 16bit to 8bit and from YUV422 to RGB
>>>> + * if supported. In any case the default RGB888 format is added
>>>> + */
>>>> +
>>>> + if (max_bpc >= 16 && info->bpc == 16) {
>>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>>>> +
>>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>>>> + }
>>>> +
>>>> + if (max_bpc >= 12 && info->bpc >= 12) {
>>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>>>> + output_fmts[i++] =
> MEDIA_BUS_FMT_UYVY12_1X24;
>>>> +
>>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>>>> +
>>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>>>> + }
>>>> +
>>>> + if (max_bpc >= 10 && info->bpc >= 10) {
>>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>>>> + output_fmts[i++] =
> MEDIA_BUS_FMT_UYVY10_1X20;
>>>> +
>>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>>>> +
>>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>>>> + }
>>>> +
>>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>>>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>>>> +
>>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>>>> +
>>>> + /* Default 8bit RGB fallback */
>>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>>>> +
>>>> + *num_output_fmts = i;
>>>> +
>>>> + return output_fmts;
>>>> +}
>>>> +
>>>> +/*
>>>> + * Possible input formats :
>>>> + * - MEDIA_BUS_FMT_RGB888_1X24
>>>> + * - MEDIA_BUS_FMT_YUV8_1X24
>>>> + * - MEDIA_BUS_FMT_UYVY8_1X16
>>>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
>>>> + * - MEDIA_BUS_FMT_RGB101010_1X30
>>>> + * - MEDIA_BUS_FMT_YUV10_1X30
>>>> + * - MEDIA_BUS_FMT_UYVY10_1X20
>>>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
>>>> + * - MEDIA_BUS_FMT_RGB121212_1X36
>>>> + * - MEDIA_BUS_FMT_YUV12_1X36
>>>> + * - MEDIA_BUS_FMT_UYVY12_1X24
>>>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
>>>> + * - MEDIA_BUS_FMT_RGB161616_1X48
>>>> + * - MEDIA_BUS_FMT_YUV16_1X48
>>>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
>>>> + */
>>>> +
>>>> +/* Can return a maximum of 4 possible input formats for an output format
>>>> */ +#define MAX_INPUT_SEL_FORMATS 4
>>>> +
>>>> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge
>>>> *bridge, + struct
>>>
>>> drm_bridge_state *bridge_state,
>>>
>>>> + struct drm_crtc_state
>>>
>>> *crtc_state,
>>>
>>>> + struct
>>>
>>> drm_connector_state *conn_state,
>>>
>>>> + u32 output_fmt,
>>>> + unsigned int
>>>
>>> *num_input_fmts)
>>>
>>>> +{
>>>> + u32 *input_fmts;
>>>> + int i = 0;
>>>> +
>>>> + *num_input_fmts = 0;
>>>> +
>>>> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
>>>> + GFP_KERNEL);
>>>> + if (!input_fmts)
>>>> + return NULL;
>>>> +
>>>> + switch (output_fmt) {
>>>> + /* 8bit */
>>>> + case MEDIA_BUS_FMT_RGB888_1X24:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>>>> + break;
>>>> + case MEDIA_BUS_FMT_YUV8_1X24:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>>>> + break;
>>>> + case MEDIA_BUS_FMT_UYVY8_1X16:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>>>> + break;
>>>> +
>>>> + /* 10bit */
>>>> + case MEDIA_BUS_FMT_RGB101010_1X30:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>>>> + break;
>>>> + case MEDIA_BUS_FMT_YUV10_1X30:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>>>> + break;
>>>> + case MEDIA_BUS_FMT_UYVY10_1X20:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>>>> + break;
>>>> +
>>>> + /* 12bit */
>>>> + case MEDIA_BUS_FMT_RGB121212_1X36:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>>>> + break;
>>>> + case MEDIA_BUS_FMT_YUV12_1X36:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>>>> + break;
>>>> + case MEDIA_BUS_FMT_UYVY12_1X24:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>>>> + break;
>>>> +
>>>> + /* 16bit */
>>>> + case MEDIA_BUS_FMT_RGB161616_1X48:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>>>> + break;
>>>> + case MEDIA_BUS_FMT_YUV16_1X48:
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>>>> + break;
>>>> +
>>>> + /* 420 */
>>>> + 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:
>>>> + input_fmts[i++] = output_fmt;
>>>> + break;
>>>> + }
>>>> +
>>>> + *num_input_fmts = i;
>>>> +
>>>> + if (*num_input_fmts == 0) {
>>>> + kfree(input_fmts);
>>>> + input_fmts = NULL;
>>>> + }
>>>> +
>>>> + return input_fmts;
>>>> +}
>>>> +
>>>> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
>>>> + struct drm_bridge_state
>>>
>>> *bridge_state,
>>>
>>>> + struct drm_crtc_state
>>>
>>> *crtc_state,
>>>
>>>> + struct drm_connector_state
>>>
>>> *conn_state)
>>>
>>>> +{
>>>> + struct dw_hdmi *hdmi = bridge->driver_private;
>>>> +
>>>> + dev_dbg(hdmi->dev, "selected output format %x\n",
>>>> + bridge_state->output_bus_cfg.format);
>>>> +
>>>> + hdmi->hdmi_data.enc_out_bus_format =
>>>> + bridge_state->output_bus_cfg.format;
>>>> +
>>>> + dev_dbg(hdmi->dev, "selected input format %x\n",
>>>> + bridge_state->input_bus_cfg.format);
>>>> +
>>>> + hdmi->hdmi_data.enc_in_bus_format =
>>>> + bridge_state->input_bus_cfg.format;
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>>
>>>> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
>>>> {
>>>>
>>>> struct dw_hdmi *hdmi = bridge->driver_private;
>>>>
>>>> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs
>>>> dw_hdmi_bridge_funcs = { .atomic_reset = drm_atomic_helper_bridge_reset,
>>>>
>>>> .attach = dw_hdmi_bridge_attach,
>>>> .detach = dw_hdmi_bridge_detach,
>>>>
>>>> + .atomic_check = dw_hdmi_bridge_atomic_check,
>>>> + .atomic_get_output_bus_fmts =
>>>
>>> dw_hdmi_bridge_atomic_get_output_bus_fmts,
>>>
>>>> + .atomic_get_input_bus_fmts =
>>>
>>> dw_hdmi_bridge_atomic_get_input_bus_fmts,
>>>
>>>> .enable = dw_hdmi_bridge_enable,
>>>> .disable = dw_hdmi_bridge_disable,
>>>> .mode_set = dw_hdmi_bridge_mode_set,
>>>>
>>>> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
>>>>
>>>> hdmi->bridge.driver_private = hdmi;
>>>> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
>>>>
>>>> +
>>>>
>>>> #ifdef CONFIG_OF
>>>>
>>>> hdmi->bridge.of_node = pdev->dev.of_node;
>>>>
>>>> #endif
>
>
>
>

2020-02-29 14:42:59

by Jernej Skrabec

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

Dne sobota, 29. februar 2020 ob 13:15:26 CET je Jonas Karlman napisal(a):
> On 2020-02-29 12:07, Jernej Škrabec wrote:
> > Dne sobota, 29. februar 2020 ob 11:09:14 CET je Jonas Karlman napisal(a):
> >> Hi Jernej,
> >>
> >> On 2020-02-29 08:42, Jernej Škrabec wrote:
> >>> Hi Neil!
> >>>
> >>> Dne četrtek, 06. februar 2020 ob 20:18:27 CET je Neil Armstrong
> >
> > napisal(a):
> >>>> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to
> >>>> negociate
> >>>> the possible output and input formats for the current mode and monitor,
> >>>> and use the negotiated formats in a basic atomic_check callback.
> >>>>
> >>>> Signed-off-by: Neil Armstrong <[email protected]>
> >>>> ---
> >>>>
> >>>> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
> >>>> 1 file changed, 268 insertions(+), 4 deletions(-)
> >>>>
> >>>> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> >>>> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
> >>>> fec4a4bcd1fe..15048ad694bc 100644
> >>>> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> >>>> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> >>>> @@ -2095,11 +2095,10 @@ 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)
> >>>>
> >>>> hdmi->hdmi_data.enc_in_bus_format =
> >>>>
> >>>> hdmi->plat_data->input_bus_format;
> >>>>
> >>>> - else
> >>>> + else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
> >>>>
> >>>> hdmi->hdmi_data.enc_in_bus_format =
> >>>
> >>> MEDIA_BUS_FMT_RGB888_1X24;
> >>>
> >>>> /* TOFIX: Get input encoding from plat data or fallback to none */
> >>>>
> >>>> @@ -2109,8 +2108,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi,
> >>>> struct
> >>>> drm_display_mode *mode) 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 (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
> >>>> + hdmi->hdmi_data.enc_out_bus_format =
> >>>
> >>> MEDIA_BUS_FMT_RGB888_1X24;
> >>>
> >>>> hdmi->hdmi_data.pix_repet_factor = 0;
> >>>> hdmi->hdmi_data.hdcp_enable = 0;
> >>>>
> >>>> @@ -2388,6 +2387,267 @@ static const struct drm_connector_helper_funcs
> >>>> dw_hdmi_connector_helper_funcs = .atomic_check =
> >>>> dw_hdmi_connector_atomic_check,
> >>>>
> >>>> };
> >>>>
> >>>> +/*
> >>>> + * Possible output formats :
> >>>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
> >>>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
> >>>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
> >>>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
> >>>> + * - MEDIA_BUS_FMT_YUV16_1X48,
> >>>> + * - MEDIA_BUS_FMT_RGB161616_1X48,
> >>>> + * - MEDIA_BUS_FMT_UYVY12_1X24,
> >>>> + * - MEDIA_BUS_FMT_YUV12_1X36,
> >>>> + * - MEDIA_BUS_FMT_RGB121212_1X36,
> >>>> + * - MEDIA_BUS_FMT_UYVY10_1X20,
> >>>> + * - MEDIA_BUS_FMT_YUV10_1X30,
> >>>> + * - MEDIA_BUS_FMT_RGB101010_1X30,
> >>>> + * - MEDIA_BUS_FMT_UYVY8_1X16,
> >>>> + * - MEDIA_BUS_FMT_YUV8_1X24,
> >>>> + * - MEDIA_BUS_FMT_RGB888_1X24,
> >>>> + */
> >>>> +
> >>>> +/* Can return a maximum of 12 possible output formats for a
> >>>> mode/connector
> >>>> */ +#define MAX_OUTPUT_SEL_FORMATS 12
> >>>> +
> >>>> +static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct
> >>>> drm_bridge
> >>>> *bridge, + struct
> >>>
> >>> drm_bridge_state *bridge_state,
> >>>
> >>>> + struct drm_crtc_state
> >>>
> >>> *crtc_state,
> >>>
> >>>> + struct
> >>>
> >>> drm_connector_state *conn_state,
> >>>
> >>>> + unsigned int
> >>>
> >>> *num_output_fmts)
> >>>
> >>>> +{
> >>>> + struct drm_connector *conn = conn_state->connector;
> >>>> + struct drm_display_info *info = &conn->display_info;
> >>>> + struct drm_display_mode *mode = &crtc_state->mode;
> >>>> + u8 max_bpc = conn_state->max_requested_bpc;
> >>>> + bool is_hdmi2_sink = info->hdmi.scdc.supported ||
> >>>> + (info->color_formats &
> >>>
> >>> DRM_COLOR_FORMAT_YCRCB420);
> >>>
> >>>> + u32 *output_fmts;
> >>>> + int i = 0;
> >>>> +
> >>>> + *num_output_fmts = 0;
> >>>> +
> >>>> + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS,
> >
> > sizeof(*output_fmts),
> >
> >>>> + GFP_KERNEL);
> >>>> + if (!output_fmts)
> >>>> + return NULL;
> >>>> +
> >>>> + /*
> >>>> + * If the current mode enforces 4:2:0, force the output but format
> >>>> + * to 4:2:0 and do not add the YUV422/444/RGB formats
> >>>> + */
> >>>> + if (conn->ycbcr_420_allowed &&
> >>>> + (drm_mode_is_420_only(info, mode) ||
> >>>> + ())) {
> >>>> +
> >>>> + /* Order bus formats from 16bit to 8bit if supported */
> >>>> + if (max_bpc >= 16 && info->bpc == 16 &&
> >>>> + (info->hdmi.y420_dc_modes &
> >>>
> >>> DRM_EDID_YCBCR420_DC_48))
> >>>
> >>>> + output_fmts[i++] =
> >>>
> >>> MEDIA_BUS_FMT_UYYVYY16_0_5X48;
> >>>
> >>>> +
> >>>> + if (max_bpc >= 12 && info->bpc >= 12 &&
> >>>> + (info->hdmi.y420_dc_modes &
> >>>
> >>> DRM_EDID_YCBCR420_DC_36))
> >>>
> >>>> + output_fmts[i++] =
> >>>
> >>> MEDIA_BUS_FMT_UYYVYY12_0_5X36;
> >>>
> >>>> +
> >>>> + if (max_bpc >= 10 && info->bpc >= 10 &&
> >>>> + (info->hdmi.y420_dc_modes &
> >>>
> >>> DRM_EDID_YCBCR420_DC_30))
> >>>
> >>>> + output_fmts[i++] =
> >>>
> >>> MEDIA_BUS_FMT_UYYVYY10_0_5X30;
> >>>
> >>>> +
> >>>> + /* Default 8bit fallback */
> >>>> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
> >>>> +
> >>>> + *num_output_fmts = i;
> >>>> +
> >>>> + return output_fmts;
> >>>
> >>> Driver shouldn't return just yet for case "is_hdmi2_sink &&
> >>> drm_mode_is_420_also(info, mode)", because monitor/TV also supports
> >>> YCbCr
> >>> 4:4:4 in that case. IMO YCbCr 4:4:4 should be even prefered. What do you
> >>> think?
> >>
> >> I think we need to have some way for controller driver and userspace to
> >> control what hdmi output format gets selected. I know for a fact that
> >> some
> >> Samsung TV have issues with 444 YCbCr modes at 4k 50/60hz but have no
> >> problems with 420 modes. The Samsung TV edid lie and/or the TV is not
> >> fully
> >> following HDMI specs
> >
> > Interesting, maybe just some bandwith issues? I guess we should have a
> > blacklist for such cases. I know that at least Allwinner BSP driver for DW
> > HDMI has a blacklist, in this case for 2 monitors which claim to support
> > YCbCr 4:4:4 mode but they not.
> >
> >> From a personal and mediaplayer userspace perspective I would like to
> >> prefer 420/444 YCbCr mode as soon as any yuv drm plane is active and rgb
> >> 444 anytime else.
> >
> > I would argue that YCbCr is always prefered:
> > - CEA 861 prefers it for all CEA modes
> > - avoid dealing with quantization range - it's always limited range for <
> > hdmi 2.0 and selectable on some hdmi 2.0 capable sinks
>
> This is probably true if your sink is a TV, for monitors with HDMI
> connections I would expect or at least like to have an option to use rgb444
> full range mode for any desktop or gaming or similar use-case.

Well, thing is that CEA 861 clearly states that for CEA modes (720p, 1080p,
2160p, etc.) limited range should be also used for RGB color space. With
implemented logic to detect if RGB limited range is needed or not and
indicating that in AVI frame, I finally get proper (better?) color reproduction
with PC monitor and TV at the same time. I'm not sure if there is any way to
detect "full range RGB" capable displays. However, we can always implement
connector property like Intel did in i915 driver "Broadcast RGB", which can
override RGB quantization mode.

>
> > Anyway, there is no universal solution to avoid color space conversion in
> > all cases. For example, due to design of Allwinner Display Engine 2, it
> > can only work in RGB format internally, but it can convert final output
> > to YCbCr right before it's feeded to DW HDMI. On the other hand, you have
> > meson display pipeline which works in YCbCr format internally and relies
> > on DW HDMI CSC unit to convert output to RGB. Fortunately, there are also
> > display pipelines which can work in any colorspace internally, like
> > Allwinner Display Engine 3. Not sure in which category Rockchip display
> > pipeline falls into.
>
> Agree, and for Rockchip to my knowledge RK3288 VOP (Video Output Processor)
> can only output 8/10-bit rgb (full range) to DW-HDMI.
> And the VOP in RK322x/RK3328/RK3399 can output 8/10-bit rgb and 444/420 yuv.
> I do not know how the VOP handles internal color conversion.
>
> >> On Rockchip SoCs the display controller cannot output yuv422 to dw-hdmi
> >> block, the optimal output format selection in such case should put yuv422
> >> last.
> >
> > Note that DW HDMI has decimation support which converts 444 to 422. This
> > is
> > part of CSC unit.
>
> This works as it should (after a patch to disable color conversion for 444
> to 422),

Yeah, I noticed that too, I plan to send a patch for that soon.

> the issue I see is that the format negotiation code will currently
> favor 444 to 422 decimation over using 444 output.
> Note: 422 will not be selected over 444 currently in case sink report deep
> color support due to drm edid parsing code following HDMI 1.3 spec, see
> [1].
>
> [1]
> https://github.com/torvalds/linux/blob/master/drivers/gpu/drm/drm_edid.c#L4
> 801-L4806
> > Side note: CSC unit is optional feature of DW-HDMI and presence is
> > indicated by config register. However, it seems that CSC support is
> > broken in DW HDMI controller on 40nm Allwinner SoCs. If it is enabled, TV
> > loses signal. I have to investigate if only CSC is broken or decimation
> > also don't work.>

Scratch above, DW HDMI CSC unit works ok on Allwinner SoCs, I just forgot to
enable CSC clock.

> >> Maybe dw-hdmi can call a dw-hdmi glue driver callback to get the
> >> preferred
> >> output format order?
> >>
> >> On a side note but related issue, the dw-hdmi format negotiation code
> >> should probably also filter modes on tmds rate, something like [1].
> >> It is needed to filter out deep color modes that is not supported by the
> >> sink or hdmi spec.
> >
> > Ah, you mean on TMDS rates supported by the sink. Could this avoid Samsung
> > deep color issues? If so, we don't need blacklist then.
>
> I do not think so, the initial issue was reported with intel graphics but
> looking at the edid and comparing to the manual of supported modes they do
> not fully match. See manual info at [2] and edid when UHD mode is off at
> [3] and when on at [4].

Which part doesn't match? I don't see any issue.

Best regards,
Jernej

>
> [2]
> https://gitlab.freedesktop.org/drm/intel/uploads/36630c8a727bf8c66a53fd7470
> c88383/MU7000_manual_-_UHD_colour_modes.PNG [3] http://ix.io/1H25
> [4] http://ix.io/1H2e
>
> > Best regards,
> > Jernej
> >
> >> [1]
> >> https://github.com/Kwiboo/linux-rockchip/commit/fc3df6903384e764ab6ac5987
> >> 9c
> >> 489cbef55fcbe
> >>
> >> Best regards,
> >> Jonas
> >>
> >>> Best regards,
> >>> Jernej
> >>>
> >>>> + }
> >>>> +
> >>>> + /*
> >>>> + * Order bus formats from 16bit to 8bit and from YUV422 to RGB
> >>>> + * if supported. In any case the default RGB888 format is added
> >>>> + */
> >>>> +
> >>>> + if (max_bpc >= 16 && info->bpc == 16) {
> >>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> >>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> >>>> +
> >>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> >>>> + }
> >>>> +
> >>>> + if (max_bpc >= 12 && info->bpc >= 12) {
> >>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> >>>> + output_fmts[i++] =
> >
> > MEDIA_BUS_FMT_UYVY12_1X24;
> >
> >>>> +
> >>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> >>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> >>>> +
> >>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> >>>> + }
> >>>> +
> >>>> + if (max_bpc >= 10 && info->bpc >= 10) {
> >>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> >>>> + output_fmts[i++] =
> >
> > MEDIA_BUS_FMT_UYVY10_1X20;
> >
> >>>> +
> >>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> >>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> >>>> +
> >>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> >>>> + }
> >>>> +
> >>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> >>>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> >>>> +
> >>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> >>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> >>>> +
> >>>> + /* Default 8bit RGB fallback */
> >>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> >>>> +
> >>>> + *num_output_fmts = i;
> >>>> +
> >>>> + return output_fmts;
> >>>> +}
> >>>> +
> >>>> +/*
> >>>> + * Possible input formats :
> >>>> + * - MEDIA_BUS_FMT_RGB888_1X24
> >>>> + * - MEDIA_BUS_FMT_YUV8_1X24
> >>>> + * - MEDIA_BUS_FMT_UYVY8_1X16
> >>>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
> >>>> + * - MEDIA_BUS_FMT_RGB101010_1X30
> >>>> + * - MEDIA_BUS_FMT_YUV10_1X30
> >>>> + * - MEDIA_BUS_FMT_UYVY10_1X20
> >>>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
> >>>> + * - MEDIA_BUS_FMT_RGB121212_1X36
> >>>> + * - MEDIA_BUS_FMT_YUV12_1X36
> >>>> + * - MEDIA_BUS_FMT_UYVY12_1X24
> >>>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
> >>>> + * - MEDIA_BUS_FMT_RGB161616_1X48
> >>>> + * - MEDIA_BUS_FMT_YUV16_1X48
> >>>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
> >>>> + */
> >>>> +
> >>>> +/* Can return a maximum of 4 possible input formats for an output
> >>>> format
> >>>> */ +#define MAX_INPUT_SEL_FORMATS 4
> >>>> +
> >>>> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge
> >>>> *bridge, + struct
> >>>
> >>> drm_bridge_state *bridge_state,
> >>>
> >>>> + struct drm_crtc_state
> >>>
> >>> *crtc_state,
> >>>
> >>>> + struct
> >>>
> >>> drm_connector_state *conn_state,
> >>>
> >>>> + u32 output_fmt,
> >>>> + unsigned int
> >>>
> >>> *num_input_fmts)
> >>>
> >>>> +{
> >>>> + u32 *input_fmts;
> >>>> + int i = 0;
> >>>> +
> >>>> + *num_input_fmts = 0;
> >>>> +
> >>>> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
> >>>> + GFP_KERNEL);
> >>>> + if (!input_fmts)
> >>>> + return NULL;
> >>>> +
> >>>> + switch (output_fmt) {
> >>>> + /* 8bit */
> >>>> + case MEDIA_BUS_FMT_RGB888_1X24:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> >>>> + break;
> >>>> + case MEDIA_BUS_FMT_YUV8_1X24:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> >>>> + break;
> >>>> + case MEDIA_BUS_FMT_UYVY8_1X16:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> >>>> + break;
> >>>> +
> >>>> + /* 10bit */
> >>>> + case MEDIA_BUS_FMT_RGB101010_1X30:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> >>>> + break;
> >>>> + case MEDIA_BUS_FMT_YUV10_1X30:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> >>>> + break;
> >>>> + case MEDIA_BUS_FMT_UYVY10_1X20:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> >>>> + break;
> >>>> +
> >>>> + /* 12bit */
> >>>> + case MEDIA_BUS_FMT_RGB121212_1X36:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> >>>> + break;
> >>>> + case MEDIA_BUS_FMT_YUV12_1X36:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> >>>> + break;
> >>>> + case MEDIA_BUS_FMT_UYVY12_1X24:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> >>>> + break;
> >>>> +
> >>>> + /* 16bit */
> >>>> + case MEDIA_BUS_FMT_RGB161616_1X48:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> >>>> + break;
> >>>> + case MEDIA_BUS_FMT_YUV16_1X48:
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> >>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> >>>> + break;
> >>>> +
> >>>> + /* 420 */
> >>>> + 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:
> >>>> + input_fmts[i++] = output_fmt;
> >>>> + break;
> >>>> + }
> >>>> +
> >>>> + *num_input_fmts = i;
> >>>> +
> >>>> + if (*num_input_fmts == 0) {
> >>>> + kfree(input_fmts);
> >>>> + input_fmts = NULL;
> >>>> + }
> >>>> +
> >>>> + return input_fmts;
> >>>> +}
> >>>> +
> >>>> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
> >>>> + struct drm_bridge_state
> >>>
> >>> *bridge_state,
> >>>
> >>>> + struct drm_crtc_state
> >>>
> >>> *crtc_state,
> >>>
> >>>> + struct drm_connector_state
> >>>
> >>> *conn_state)
> >>>
> >>>> +{
> >>>> + struct dw_hdmi *hdmi = bridge->driver_private;
> >>>> +
> >>>> + dev_dbg(hdmi->dev, "selected output format %x\n",
> >>>> + bridge_state->output_bus_cfg.format);
> >>>> +
> >>>> + hdmi->hdmi_data.enc_out_bus_format =
> >>>> + bridge_state->output_bus_cfg.format;
> >>>> +
> >>>> + dev_dbg(hdmi->dev, "selected input format %x\n",
> >>>> + bridge_state->input_bus_cfg.format);
> >>>> +
> >>>> + hdmi->hdmi_data.enc_in_bus_format =
> >>>> + bridge_state->input_bus_cfg.format;
> >>>> +
> >>>> + return 0;
> >>>> +}
> >>>> +
> >>>>
> >>>> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
> >>>> {
> >>>>
> >>>> struct dw_hdmi *hdmi = bridge->driver_private;
> >>>>
> >>>> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs
> >>>> dw_hdmi_bridge_funcs = { .atomic_reset =
> >>>> drm_atomic_helper_bridge_reset,
> >>>>
> >>>> .attach = dw_hdmi_bridge_attach,
> >>>> .detach = dw_hdmi_bridge_detach,
> >>>>
> >>>> + .atomic_check = dw_hdmi_bridge_atomic_check,
> >>>> + .atomic_get_output_bus_fmts =
> >>>
> >>> dw_hdmi_bridge_atomic_get_output_bus_fmts,
> >>>
> >>>> + .atomic_get_input_bus_fmts =
> >>>
> >>> dw_hdmi_bridge_atomic_get_input_bus_fmts,
> >>>
> >>>> .enable = dw_hdmi_bridge_enable,
> >>>> .disable = dw_hdmi_bridge_disable,
> >>>> .mode_set = dw_hdmi_bridge_mode_set,
> >>>>
> >>>> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
> >>>>
> >>>> hdmi->bridge.driver_private = hdmi;
> >>>> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
> >>>>
> >>>> +
> >>>>
> >>>> #ifdef CONFIG_OF
> >>>>
> >>>> hdmi->bridge.of_node = pdev->dev.of_node;
> >>>>
> >>>> #endif




2020-03-02 08:05:02

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

On 29/02/2020 08:42, Jernej Škrabec wrote:
> Hi Neil!
>
> Dne četrtek, 06. februar 2020 ob 20:18:27 CET je Neil Armstrong napisal(a):
>> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to negociate
>> the possible output and input formats for the current mode and monitor,
>> and use the negotiated formats in a basic atomic_check callback.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
>> 1 file changed, 268 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
>> fec4a4bcd1fe..15048ad694bc 100644
>> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> @@ -2095,11 +2095,10 @@ 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)
>> hdmi->hdmi_data.enc_in_bus_format =
>> hdmi->plat_data->input_bus_format;
>> - else
>> + else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
>> hdmi->hdmi_data.enc_in_bus_format =
> MEDIA_BUS_FMT_RGB888_1X24;
>>
>> /* TOFIX: Get input encoding from plat data or fallback to none */
>> @@ -2109,8 +2108,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct
>> drm_display_mode *mode) 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 (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
>> + hdmi->hdmi_data.enc_out_bus_format =
> MEDIA_BUS_FMT_RGB888_1X24;
>>
>> hdmi->hdmi_data.pix_repet_factor = 0;
>> hdmi->hdmi_data.hdcp_enable = 0;
>> @@ -2388,6 +2387,267 @@ static const struct drm_connector_helper_funcs
>> dw_hdmi_connector_helper_funcs = .atomic_check =
>> dw_hdmi_connector_atomic_check,
>> };
>>
>> +/*
>> + * Possible output formats :
>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
>> + * - MEDIA_BUS_FMT_YUV16_1X48,
>> + * - MEDIA_BUS_FMT_RGB161616_1X48,
>> + * - MEDIA_BUS_FMT_UYVY12_1X24,
>> + * - MEDIA_BUS_FMT_YUV12_1X36,
>> + * - MEDIA_BUS_FMT_RGB121212_1X36,
>> + * - MEDIA_BUS_FMT_UYVY10_1X20,
>> + * - MEDIA_BUS_FMT_YUV10_1X30,
>> + * - MEDIA_BUS_FMT_RGB101010_1X30,
>> + * - MEDIA_BUS_FMT_UYVY8_1X16,
>> + * - MEDIA_BUS_FMT_YUV8_1X24,
>> + * - MEDIA_BUS_FMT_RGB888_1X24,
>> + */
>> +
>> +/* Can return a maximum of 12 possible output formats for a mode/connector
>> */ +#define MAX_OUTPUT_SEL_FORMATS 12
>> +
>> +static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge
>> *bridge, + struct
> drm_bridge_state *bridge_state,
>> + struct drm_crtc_state
> *crtc_state,
>> + struct
> drm_connector_state *conn_state,
>> + unsigned int
> *num_output_fmts)
>> +{
>> + struct drm_connector *conn = conn_state->connector;
>> + struct drm_display_info *info = &conn->display_info;
>> + struct drm_display_mode *mode = &crtc_state->mode;
>> + u8 max_bpc = conn_state->max_requested_bpc;
>> + bool is_hdmi2_sink = info->hdmi.scdc.supported ||
>> + (info->color_formats &
> DRM_COLOR_FORMAT_YCRCB420);
>> + u32 *output_fmts;
>> + int i = 0;
>> +
>> + *num_output_fmts = 0;
>> +
>> + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts),
>> + GFP_KERNEL);
>> + if (!output_fmts)
>> + return NULL;
>> +
>> + /*
>> + * If the current mode enforces 4:2:0, force the output but format
>> + * to 4:2:0 and do not add the YUV422/444/RGB formats
>> + */
>> + if (conn->ycbcr_420_allowed &&
>> + (drm_mode_is_420_only(info, mode) ||
>> + ())) {
>> +
>> + /* Order bus formats from 16bit to 8bit if supported */
>> + if (max_bpc >= 16 && info->bpc == 16 &&
>> + (info->hdmi.y420_dc_modes &
> DRM_EDID_YCBCR420_DC_48))
>> + output_fmts[i++] =
> MEDIA_BUS_FMT_UYYVYY16_0_5X48;
>> +
>> + if (max_bpc >= 12 && info->bpc >= 12 &&
>> + (info->hdmi.y420_dc_modes &
> DRM_EDID_YCBCR420_DC_36))
>> + output_fmts[i++] =
> MEDIA_BUS_FMT_UYYVYY12_0_5X36;
>> +
>> + if (max_bpc >= 10 && info->bpc >= 10 &&
>> + (info->hdmi.y420_dc_modes &
> DRM_EDID_YCBCR420_DC_30))
>> + output_fmts[i++] =
> MEDIA_BUS_FMT_UYYVYY10_0_5X30;
>> +
>> + /* Default 8bit fallback */
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
>> +
>> + *num_output_fmts = i;
>> +
>> + return output_fmts;
>
> Driver shouldn't return just yet for case "is_hdmi2_sink &&
> drm_mode_is_420_also(info, mode)", because monitor/TV also supports YCbCr
> 4:4:4 in that case. IMO YCbCr 4:4:4 should be even prefered. What do you
> think?

The code is right.

If drm_mode_is_420_only(info, mode), then the selected mode is 4:2:0 only, so only
4:2:0 bus formats will be allowed in this case.

In this 4:2:0 only mode, the TV only support 4:2:0 4k60 modes and doesn't support
SCDC and high tmds frequency pattern & co.

Neil

>
> Best regards,
> Jernej
>
>> + }
>> +
>> + /*
>> + * Order bus formats from 16bit to 8bit and from YUV422 to RGB
>> + * if supported. In any case the default RGB888 format is added
>> + */
>> +
>> + if (max_bpc >= 16 && info->bpc == 16) {
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> +
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + }
>> +
>> + if (max_bpc >= 12 && info->bpc >= 12) {
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> +
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + }
>> +
>> + if (max_bpc >= 10 && info->bpc >= 10) {
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> +
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + }
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> +
>> + /* Default 8bit RGB fallback */
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> +
>> + *num_output_fmts = i;
>> +
>> + return output_fmts;
>> +}
>> +
>> +/*
>> + * Possible input formats :
>> + * - MEDIA_BUS_FMT_RGB888_1X24
>> + * - MEDIA_BUS_FMT_YUV8_1X24
>> + * - MEDIA_BUS_FMT_UYVY8_1X16
>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
>> + * - MEDIA_BUS_FMT_RGB101010_1X30
>> + * - MEDIA_BUS_FMT_YUV10_1X30
>> + * - MEDIA_BUS_FMT_UYVY10_1X20
>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
>> + * - MEDIA_BUS_FMT_RGB121212_1X36
>> + * - MEDIA_BUS_FMT_YUV12_1X36
>> + * - MEDIA_BUS_FMT_UYVY12_1X24
>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
>> + * - MEDIA_BUS_FMT_RGB161616_1X48
>> + * - MEDIA_BUS_FMT_YUV16_1X48
>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
>> + */
>> +
>> +/* Can return a maximum of 4 possible input formats for an output format */
>> +#define MAX_INPUT_SEL_FORMATS 4
>> +
>> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge
>> *bridge, + struct
> drm_bridge_state *bridge_state,
>> + struct drm_crtc_state
> *crtc_state,
>> + struct
> drm_connector_state *conn_state,
>> + u32 output_fmt,
>> + unsigned int
> *num_input_fmts)
>> +{
>> + u32 *input_fmts;
>> + int i = 0;
>> +
>> + *num_input_fmts = 0;
>> +
>> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
>> + GFP_KERNEL);
>> + if (!input_fmts)
>> + return NULL;
>> +
>> + switch (output_fmt) {
>> + /* 8bit */
>> + case MEDIA_BUS_FMT_RGB888_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + break;
>> + case MEDIA_BUS_FMT_YUV8_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY8_1X16:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + break;
>> +
>> + /* 10bit */
>> + case MEDIA_BUS_FMT_RGB101010_1X30:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + break;
>> + case MEDIA_BUS_FMT_YUV10_1X30:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY10_1X20:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + break;
>> +
>> + /* 12bit */
>> + case MEDIA_BUS_FMT_RGB121212_1X36:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + break;
>> + case MEDIA_BUS_FMT_YUV12_1X36:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY12_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + break;
>> +
>> + /* 16bit */
>> + case MEDIA_BUS_FMT_RGB161616_1X48:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> + break;
>> + case MEDIA_BUS_FMT_YUV16_1X48:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + break;
>> +
>> + /* 420 */
>> + 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:
>> + input_fmts[i++] = output_fmt;
>> + break;
>> + }
>> +
>> + *num_input_fmts = i;
>> +
>> + if (*num_input_fmts == 0) {
>> + kfree(input_fmts);
>> + input_fmts = NULL;
>> + }
>> +
>> + return input_fmts;
>> +}
>> +
>> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
>> + struct drm_bridge_state
> *bridge_state,
>> + struct drm_crtc_state
> *crtc_state,
>> + struct drm_connector_state
> *conn_state)
>> +{
>> + struct dw_hdmi *hdmi = bridge->driver_private;
>> +
>> + dev_dbg(hdmi->dev, "selected output format %x\n",
>> + bridge_state->output_bus_cfg.format);
>> +
>> + hdmi->hdmi_data.enc_out_bus_format =
>> + bridge_state->output_bus_cfg.format;
>> +
>> + dev_dbg(hdmi->dev, "selected input format %x\n",
>> + bridge_state->input_bus_cfg.format);
>> +
>> + hdmi->hdmi_data.enc_in_bus_format =
>> + bridge_state->input_bus_cfg.format;
>> +
>> + return 0;
>> +}
>> +
>> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
>> {
>> struct dw_hdmi *hdmi = bridge->driver_private;
>> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs
>> dw_hdmi_bridge_funcs = { .atomic_reset = drm_atomic_helper_bridge_reset,
>> .attach = dw_hdmi_bridge_attach,
>> .detach = dw_hdmi_bridge_detach,
>> + .atomic_check = dw_hdmi_bridge_atomic_check,
>> + .atomic_get_output_bus_fmts =
> dw_hdmi_bridge_atomic_get_output_bus_fmts,
>> + .atomic_get_input_bus_fmts =
> dw_hdmi_bridge_atomic_get_input_bus_fmts,
>> .enable = dw_hdmi_bridge_enable,
>> .disable = dw_hdmi_bridge_disable,
>> .mode_set = dw_hdmi_bridge_mode_set,
>> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
>>
>> hdmi->bridge.driver_private = hdmi;
>> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
>> +
>> #ifdef CONFIG_OF
>> hdmi->bridge.of_node = pdev->dev.of_node;
>> #endif
>
>
>
>

2020-03-02 08:09:14

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

On 29/02/2020 11:09, Jonas Karlman wrote:
> Hi Jernej,
>
> On 2020-02-29 08:42, Jernej Škrabec wrote:
>> Hi Neil!
>>
>> Dne četrtek, 06. februar 2020 ob 20:18:27 CET je Neil Armstrong napisal(a):
>>> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to negociate
>>> the possible output and input formats for the current mode and monitor,
>>> and use the negotiated formats in a basic atomic_check callback.
>>>
>>> Signed-off-by: Neil Armstrong <[email protected]>
>>> ---
>>> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
>>> 1 file changed, 268 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>>> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
>>> fec4a4bcd1fe..15048ad694bc 100644
>>> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>>> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>>> @@ -2095,11 +2095,10 @@ 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)
>>> hdmi->hdmi_data.enc_in_bus_format =
>>> hdmi->plat_data->input_bus_format;
>>> - else
>>> + else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
>>> hdmi->hdmi_data.enc_in_bus_format =
>> MEDIA_BUS_FMT_RGB888_1X24;
>>>
>>> /* TOFIX: Get input encoding from plat data or fallback to none */
>>> @@ -2109,8 +2108,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct
>>> drm_display_mode *mode) 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 (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
>>> + hdmi->hdmi_data.enc_out_bus_format =
>> MEDIA_BUS_FMT_RGB888_1X24;
>>>
>>> hdmi->hdmi_data.pix_repet_factor = 0;
>>> hdmi->hdmi_data.hdcp_enable = 0;
>>> @@ -2388,6 +2387,267 @@ static const struct drm_connector_helper_funcs
>>> dw_hdmi_connector_helper_funcs = .atomic_check =
>>> dw_hdmi_connector_atomic_check,
>>> };
>>>
>>> +/*
>>> + * Possible output formats :
>>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
>>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
>>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
>>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
>>> + * - MEDIA_BUS_FMT_YUV16_1X48,
>>> + * - MEDIA_BUS_FMT_RGB161616_1X48,
>>> + * - MEDIA_BUS_FMT_UYVY12_1X24,
>>> + * - MEDIA_BUS_FMT_YUV12_1X36,
>>> + * - MEDIA_BUS_FMT_RGB121212_1X36,
>>> + * - MEDIA_BUS_FMT_UYVY10_1X20,
>>> + * - MEDIA_BUS_FMT_YUV10_1X30,
>>> + * - MEDIA_BUS_FMT_RGB101010_1X30,
>>> + * - MEDIA_BUS_FMT_UYVY8_1X16,
>>> + * - MEDIA_BUS_FMT_YUV8_1X24,
>>> + * - MEDIA_BUS_FMT_RGB888_1X24,
>>> + */
>>> +
>>> +/* Can return a maximum of 12 possible output formats for a mode/connector
>>> */ +#define MAX_OUTPUT_SEL_FORMATS 12
>>> +
>>> +static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge
>>> *bridge, + struct
>> drm_bridge_state *bridge_state,
>>> + struct drm_crtc_state
>> *crtc_state,
>>> + struct
>> drm_connector_state *conn_state,
>>> + unsigned int
>> *num_output_fmts)
>>> +{
>>> + struct drm_connector *conn = conn_state->connector;
>>> + struct drm_display_info *info = &conn->display_info;
>>> + struct drm_display_mode *mode = &crtc_state->mode;
>>> + u8 max_bpc = conn_state->max_requested_bpc;
>>> + bool is_hdmi2_sink = info->hdmi.scdc.supported ||
>>> + (info->color_formats &
>> DRM_COLOR_FORMAT_YCRCB420);
>>> + u32 *output_fmts;
>>> + int i = 0;
>>> +
>>> + *num_output_fmts = 0;
>>> +
>>> + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts),
>>> + GFP_KERNEL);
>>> + if (!output_fmts)
>>> + return NULL;
>>> +
>>> + /*
>>> + * If the current mode enforces 4:2:0, force the output but format
>>> + * to 4:2:0 and do not add the YUV422/444/RGB formats
>>> + */
>>> + if (conn->ycbcr_420_allowed &&
>>> + (drm_mode_is_420_only(info, mode) ||
>>> + ())) {
>>> +
>>> + /* Order bus formats from 16bit to 8bit if supported */
>>> + if (max_bpc >= 16 && info->bpc == 16 &&
>>> + (info->hdmi.y420_dc_modes &
>> DRM_EDID_YCBCR420_DC_48))
>>> + output_fmts[i++] =
>> MEDIA_BUS_FMT_UYYVYY16_0_5X48;
>>> +
>>> + if (max_bpc >= 12 && info->bpc >= 12 &&
>>> + (info->hdmi.y420_dc_modes &
>> DRM_EDID_YCBCR420_DC_36))
>>> + output_fmts[i++] =
>> MEDIA_BUS_FMT_UYYVYY12_0_5X36;
>>> +
>>> + if (max_bpc >= 10 && info->bpc >= 10 &&
>>> + (info->hdmi.y420_dc_modes &
>> DRM_EDID_YCBCR420_DC_30))
>>> + output_fmts[i++] =
>> MEDIA_BUS_FMT_UYYVYY10_0_5X30;
>>> +
>>> + /* Default 8bit fallback */
>>> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
>>> +
>>> + *num_output_fmts = i;
>>> +
>>> + return output_fmts;
>>
>> Driver shouldn't return just yet for case "is_hdmi2_sink &&
>> drm_mode_is_420_also(info, mode)", because monitor/TV also supports YCbCr
>> 4:4:4 in that case. IMO YCbCr 4:4:4 should be even prefered. What do you
>> think?
>
> I think we need to have some way for controller driver and userspace to control what
> hdmi output format gets selected. I know for a fact that some Samsung TV have issues
> with 444 YCbCr modes at 4k 50/60hz but have no problems with 420 modes.
> The Samsung TV edid lie and/or the TV is not fully following HDMI specs.

In this case this must be handled in the EDID quirks, by disabling SCDC & co for this TV.

>
> From a personal and mediaplayer userspace perspective I would like to prefer
> 420/444 YCbCr mode as soon as any yuv drm plane is active and rgb 444 anytime else.
>
> On Rockchip SoCs the display controller cannot output yuv422 to dw-hdmi block,
> the optimal output format selection in such case should put yuv422 last.

I think 4:2:2 should be moved after 4:4:4 indeed, but on another side, 10bit cannot be
done in 4k60 4:4:4, so only 4:2:2 is possible when the tv is not 4:2:0 capable.

>
> Maybe dw-hdmi can call a dw-hdmi glue driver callback to get the preferred output format order?

This seems complex to handle...

>
> On a side note but related issue, the dw-hdmi format negotiation code should
> probably also filter modes on tmds rate, something like [1].
> It is needed to filter out deep color modes that is not supported by the sink or hdmi spec.

Indeed.

>
> [1] https://github.com/Kwiboo/linux-rockchip/commit/fc3df6903384e764ab6ac59879c489cbef55fcbe
>
> Best regards,
> Jonas

Neil

>
>>
>> Best regards,
>> Jernej
>>
>>> + }
>>> +
>>> + /*
>>> + * Order bus formats from 16bit to 8bit and from YUV422 to RGB
>>> + * if supported. In any case the default RGB888 format is added
>>> + */
>>> +
>>> + if (max_bpc >= 16 && info->bpc == 16) {
>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>>> +
>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>>> + }
>>> +
>>> + if (max_bpc >= 12 && info->bpc >= 12) {
>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>>> +
>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>>> +
>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>>> + }
>>> +
>>> + if (max_bpc >= 10 && info->bpc >= 10) {
>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>>> +
>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>>> +
>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>>> + }
>>> +
>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>>> +
>>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>>> +
>>> + /* Default 8bit RGB fallback */
>>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>>> +
>>> + *num_output_fmts = i;
>>> +
>>> + return output_fmts;
>>> +}
>>> +
>>> +/*
>>> + * Possible input formats :
>>> + * - MEDIA_BUS_FMT_RGB888_1X24
>>> + * - MEDIA_BUS_FMT_YUV8_1X24
>>> + * - MEDIA_BUS_FMT_UYVY8_1X16
>>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
>>> + * - MEDIA_BUS_FMT_RGB101010_1X30
>>> + * - MEDIA_BUS_FMT_YUV10_1X30
>>> + * - MEDIA_BUS_FMT_UYVY10_1X20
>>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
>>> + * - MEDIA_BUS_FMT_RGB121212_1X36
>>> + * - MEDIA_BUS_FMT_YUV12_1X36
>>> + * - MEDIA_BUS_FMT_UYVY12_1X24
>>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
>>> + * - MEDIA_BUS_FMT_RGB161616_1X48
>>> + * - MEDIA_BUS_FMT_YUV16_1X48
>>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
>>> + */
>>> +
>>> +/* Can return a maximum of 4 possible input formats for an output format */
>>> +#define MAX_INPUT_SEL_FORMATS 4
>>> +
>>> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge
>>> *bridge, + struct
>> drm_bridge_state *bridge_state,
>>> + struct drm_crtc_state
>> *crtc_state,
>>> + struct
>> drm_connector_state *conn_state,
>>> + u32 output_fmt,
>>> + unsigned int
>> *num_input_fmts)
>>> +{
>>> + u32 *input_fmts;
>>> + int i = 0;
>>> +
>>> + *num_input_fmts = 0;
>>> +
>>> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
>>> + GFP_KERNEL);
>>> + if (!input_fmts)
>>> + return NULL;
>>> +
>>> + switch (output_fmt) {
>>> + /* 8bit */
>>> + case MEDIA_BUS_FMT_RGB888_1X24:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>>> + break;
>>> + case MEDIA_BUS_FMT_YUV8_1X24:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>>> + break;
>>> + case MEDIA_BUS_FMT_UYVY8_1X16:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>>> + break;
>>> +
>>> + /* 10bit */
>>> + case MEDIA_BUS_FMT_RGB101010_1X30:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>>> + break;
>>> + case MEDIA_BUS_FMT_YUV10_1X30:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>>> + break;
>>> + case MEDIA_BUS_FMT_UYVY10_1X20:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>>> + break;
>>> +
>>> + /* 12bit */
>>> + case MEDIA_BUS_FMT_RGB121212_1X36:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>>> + break;
>>> + case MEDIA_BUS_FMT_YUV12_1X36:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>>> + break;
>>> + case MEDIA_BUS_FMT_UYVY12_1X24:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>>> + break;
>>> +
>>> + /* 16bit */
>>> + case MEDIA_BUS_FMT_RGB161616_1X48:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>>> + break;
>>> + case MEDIA_BUS_FMT_YUV16_1X48:
>>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>>> + break;
>>> +
>>> + /* 420 */
>>> + 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:
>>> + input_fmts[i++] = output_fmt;
>>> + break;
>>> + }
>>> +
>>> + *num_input_fmts = i;
>>> +
>>> + if (*num_input_fmts == 0) {
>>> + kfree(input_fmts);
>>> + input_fmts = NULL;
>>> + }
>>> +
>>> + return input_fmts;
>>> +}
>>> +
>>> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
>>> + struct drm_bridge_state
>> *bridge_state,
>>> + struct drm_crtc_state
>> *crtc_state,
>>> + struct drm_connector_state
>> *conn_state)
>>> +{
>>> + struct dw_hdmi *hdmi = bridge->driver_private;
>>> +
>>> + dev_dbg(hdmi->dev, "selected output format %x\n",
>>> + bridge_state->output_bus_cfg.format);
>>> +
>>> + hdmi->hdmi_data.enc_out_bus_format =
>>> + bridge_state->output_bus_cfg.format;
>>> +
>>> + dev_dbg(hdmi->dev, "selected input format %x\n",
>>> + bridge_state->input_bus_cfg.format);
>>> +
>>> + hdmi->hdmi_data.enc_in_bus_format =
>>> + bridge_state->input_bus_cfg.format;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
>>> {
>>> struct dw_hdmi *hdmi = bridge->driver_private;
>>> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs
>>> dw_hdmi_bridge_funcs = { .atomic_reset = drm_atomic_helper_bridge_reset,
>>> .attach = dw_hdmi_bridge_attach,
>>> .detach = dw_hdmi_bridge_detach,
>>> + .atomic_check = dw_hdmi_bridge_atomic_check,
>>> + .atomic_get_output_bus_fmts =
>> dw_hdmi_bridge_atomic_get_output_bus_fmts,
>>> + .atomic_get_input_bus_fmts =
>> dw_hdmi_bridge_atomic_get_input_bus_fmts,
>>> .enable = dw_hdmi_bridge_enable,
>>> .disable = dw_hdmi_bridge_disable,
>>> .mode_set = dw_hdmi_bridge_mode_set,
>>> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
>>>
>>> hdmi->bridge.driver_private = hdmi;
>>> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
>>> +
>>> #ifdef CONFIG_OF
>>> hdmi->bridge.of_node = pdev->dev.of_node;
>>> #endif
>>
>>
>>
>>

2020-03-02 09:20:09

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 02/11] drm/bridge: dw-hdmi: add max bpc connector property

Hi Neil and Jonas,

(CC'ing Daniel for a framework question)

Thank you for the patch.

On Fri, Feb 21, 2020 at 09:50:18AM +0100, Neil Armstrong wrote:
> On 17/02/2020 07:38, Jernej Škrabec wrote:
> > Dne četrtek, 06. februar 2020 ob 20:18:25 CET je Neil Armstrong napisal(a):
> >> From: Jonas Karlman <[email protected]>
> >>
> >> Add the max_bpc property to the dw-hdmi connector to prepare support
> >> for 10, 12 & 16bit output support.
> >>
> >> Signed-off-by: Jonas Karlman <[email protected]>
> >> Signed-off-by: Neil Armstrong <[email protected]>
> >> ---
> >> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 4 ++++
> >> 1 file changed, 4 insertions(+)
> >>
> >> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> >> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
> >> 9e0927d22db6..051001f77dd4 100644
> >> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> >> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> >> @@ -2406,6 +2406,10 @@ static int dw_hdmi_bridge_attach(struct drm_bridge
> >> *bridge) DRM_MODE_CONNECTOR_HDMIA,
> >> hdmi->ddc);
> >>
> >> + drm_atomic_helper_connector_reset(connector);
> >
> > Why is this reset needed?
>
> I assume it's to allocate a new connector state to attach a the bpc propery.
>
> But indeed, this helper is never used here, but only as callback to the drm_connector_funcs->reset.
>
> But, amdgpu calls :
> /*
> * Some of the properties below require access to state, like bpc.
> * Allocate some default initial connector state with our reset helper.
> */
> if (aconnector->base.funcs->reset)
> aconnector->base.funcs->reset(&aconnector->base);
>
> which is the same.

A comment would be useful:

/*
* drm_connector_attach_max_bpc_property() requires the
* connector to have a state.
*/
drm_atomic_helper_connector_reset(connector);

drm_connector_attach_max_bpc_property(connector, 8, 16);

I don't like this much though, it feels like the initial reset performed
by drm_mode_config_reset() should set default values for all state
members that are related to properties. Daniel, what's the rationale
behind the current implementation ?

This is a DRM core issue that shouldn't block this patch though, so

Reviewed-by: Laurent Pinchart <[email protected]>

> >> +
> >> + drm_connector_attach_max_bpc_property(connector, 8, 16);
> >> +
> >> if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
> >> drm_object_attach_property(&connector->base,
> >> connector->dev-
> >> mode_config.hdr_output_metadata_property, 0);

--
Regards,

Laurent Pinchart

2020-03-02 09:57:43

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 03/11] drm/bridge: dw-hdmi: Plug atomic state hooks to the default implementation

Hi Neil,

Thank you for the patch.

On Thu, Feb 06, 2020 at 08:18:26PM +0100, Neil Armstrong wrote:
> Signed-off-by: Neil Armstrong <[email protected]>

How about adding a commit message ?

Reviewed-by: Laurent Pinchart <[email protected]>

> ---
> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> index 051001f77dd4..fec4a4bcd1fe 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -2494,6 +2494,9 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
> }
>
> static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
> + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
> + .atomic_reset = drm_atomic_helper_bridge_reset,
> .attach = dw_hdmi_bridge_attach,
> .detach = dw_hdmi_bridge_detach,
> .enable = dw_hdmi_bridge_enable,

--
Regards,

Laurent Pinchart

2020-03-02 10:01:05

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 06/11] drm/meson: venc: make drm_display_mode const

Hi Neil,

Thank you for the patch.

On Thu, Feb 06, 2020 at 08:18:29PM +0100, Neil Armstrong wrote:
> Before switching to bridge funcs, make sure drm_display_mode is passed
> as const to the venc functions.
>
> Signed-off-by: Neil Armstrong <[email protected]>

Acked-by: Laurent Pinchart <[email protected]>

> ---
> drivers/gpu/drm/meson/meson_venc.c | 2 +-
> drivers/gpu/drm/meson/meson_venc.h | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c
> index 4efd7864d5bf..a9ab78970bfe 100644
> --- a/drivers/gpu/drm/meson/meson_venc.c
> +++ b/drivers/gpu/drm/meson/meson_venc.c
> @@ -946,7 +946,7 @@ bool meson_venc_hdmi_venc_repeat(int vic)
> EXPORT_SYMBOL_GPL(meson_venc_hdmi_venc_repeat);
>
> void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
> - struct drm_display_mode *mode)
> + const struct drm_display_mode *mode)
> {
> union meson_hdmi_venc_mode *vmode = NULL;
> union meson_hdmi_venc_mode vmode_dmt;
> diff --git a/drivers/gpu/drm/meson/meson_venc.h b/drivers/gpu/drm/meson/meson_venc.h
> index 576768bdd08d..1abdcbdf51c0 100644
> --- a/drivers/gpu/drm/meson/meson_venc.h
> +++ b/drivers/gpu/drm/meson/meson_venc.h
> @@ -60,7 +60,7 @@ extern struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc;
> void meson_venci_cvbs_mode_set(struct meson_drm *priv,
> struct meson_cvbs_enci_mode *mode);
> void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic,
> - struct drm_display_mode *mode);
> + const struct drm_display_mode *mode);
> unsigned int meson_venci_get_field(struct meson_drm *priv);
>
> void meson_venc_enable_vsync(struct meson_drm *priv);

--
Regards,

Laurent Pinchart

2020-03-02 10:03:54

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 05/11] drm/bridge: synopsys: dw-hdmi: allow ycbcr420 modes for >= 0x200a

Hi Neil,

Thank you for the patch.

On Thu, Feb 06, 2020 at 08:18:28PM +0100, Neil Armstrong wrote:
> Now the DW-HDMI Controller supports the HDMI2.0 modes, enable support
> for these modes in the connector if the platform supports them.
> We limit these modes to DW-HDMI IP version >= 0x200a which
> are designed to support HDMI2.0 display modes.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 6 ++++++
> include/drm/bridge/dw_hdmi.h | 1 +
> 2 files changed, 7 insertions(+)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> index 15048ad694bc..4b35ea1427df 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -3231,6 +3231,12 @@ __dw_hdmi_probe(struct platform_device *pdev,
> hdmi->bridge.of_node = pdev->dev.of_node;
> #endif
>
> + if (hdmi->version >= 0x200a)
> + hdmi->connector.ycbcr_420_allowed =
> + hdmi->plat_data->ycbcr_420_allowed;
> + else
> + hdmi->connector.ycbcr_420_allowed = false;
> +

The hdmi structure being allocated with kzalloc, you don't need the
second branch of the if, but I'm fine if you prefer keeping it. Any
either case,

Reviewed-by: Laurent Pinchart <[email protected]>

> memset(&pdevinfo, 0, sizeof(pdevinfo));
> pdevinfo.parent = dev;
> pdevinfo.id = PLATFORM_DEVID_AUTO;
> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
> index 9d4d5cc47969..0b34a12c4a1c 100644
> --- a/include/drm/bridge/dw_hdmi.h
> +++ b/include/drm/bridge/dw_hdmi.h
> @@ -129,6 +129,7 @@ struct dw_hdmi_plat_data {
> unsigned long input_bus_format;
> unsigned long input_bus_encoding;
> bool use_drm_infoframe;
> + bool ycbcr_420_allowed;
>
> /* Vendor PHY support */
> const struct dw_hdmi_phy_ops *phy_ops;

--
Regards,

Laurent Pinchart

2020-03-02 10:20:16

by Laurent Pinchart

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

Hi Neil,

Thank you for the patch.

On Thu, Feb 06, 2020 at 08:18:27PM +0100, Neil Armstrong wrote:
> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to negociate
> the possible output and input formats for the current mode and monitor,
> and use the negotiated formats in a basic atomic_check callback.
>
> Signed-off-by: Neil Armstrong <[email protected]>
> ---
> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
> 1 file changed, 268 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> index fec4a4bcd1fe..15048ad694bc 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -2095,11 +2095,10 @@ 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)
> hdmi->hdmi_data.enc_in_bus_format =
> hdmi->plat_data->input_bus_format;
> - else
> + else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
> hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
>
> /* TOFIX: Get input encoding from plat data or fallback to none */
> @@ -2109,8 +2108,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
> 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 (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
> + hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
>
> hdmi->hdmi_data.pix_repet_factor = 0;
> hdmi->hdmi_data.hdcp_enable = 0;
> @@ -2388,6 +2387,267 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
> .atomic_check = dw_hdmi_connector_atomic_check,
> };
>
> +/*
> + * Possible output formats :
> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
> + * - MEDIA_BUS_FMT_YUV16_1X48,
> + * - MEDIA_BUS_FMT_RGB161616_1X48,
> + * - MEDIA_BUS_FMT_UYVY12_1X24,
> + * - MEDIA_BUS_FMT_YUV12_1X36,
> + * - MEDIA_BUS_FMT_RGB121212_1X36,
> + * - MEDIA_BUS_FMT_UYVY10_1X20,
> + * - MEDIA_BUS_FMT_YUV10_1X30,
> + * - MEDIA_BUS_FMT_RGB101010_1X30,
> + * - MEDIA_BUS_FMT_UYVY8_1X16,
> + * - MEDIA_BUS_FMT_YUV8_1X24,
> + * - MEDIA_BUS_FMT_RGB888_1X24,
> + */

I'd drop this comment as I don't think it brings much, except for a risk
of getting out of sync with the implementation below :-)

> +
> +/* Can return a maximum of 12 possible output formats for a mode/connector */
> +#define MAX_OUTPUT_SEL_FORMATS 12

I count 11 below.

> +
> +static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state,
> + unsigned int *num_output_fmts)
> +{
> + struct drm_connector *conn = conn_state->connector;
> + struct drm_display_info *info = &conn->display_info;
> + struct drm_display_mode *mode = &crtc_state->mode;
> + u8 max_bpc = conn_state->max_requested_bpc;
> + bool is_hdmi2_sink = info->hdmi.scdc.supported ||
> + (info->color_formats & DRM_COLOR_FORMAT_YCRCB420);
> + u32 *output_fmts;
> + int i = 0;

i is never negative, you can make it an unsigned int.

> +
> + *num_output_fmts = 0;
> +
> + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts),
> + GFP_KERNEL);
> + if (!output_fmts)
> + return NULL;
> +
> + /*
> + * If the current mode enforces 4:2:0, force the output but format
> + * to 4:2:0 and do not add the YUV422/444/RGB formats
> + */
> + if (conn->ycbcr_420_allowed &&
> + (drm_mode_is_420_only(info, mode) ||
> + (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) {
> +
> + /* Order bus formats from 16bit to 8bit if supported */
> + if (max_bpc >= 16 && info->bpc == 16 &&
> + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48))
> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48;
> +
> + if (max_bpc >= 12 && info->bpc >= 12 &&
> + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36))
> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
> +
> + if (max_bpc >= 10 && info->bpc >= 10 &&
> + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30))
> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
> +
> + /* Default 8bit fallback */
> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
> +
> + *num_output_fmts = i;
> +
> + return output_fmts;
> + }
> +
> + /*
> + * Order bus formats from 16bit to 8bit and from YUV422 to RGB
> + * if supported. In any case the default RGB888 format is added
> + */
> +
> + if (max_bpc >= 16 && info->bpc == 16) {
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> +
> + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> + }
> +
> + if (max_bpc >= 12 && info->bpc >= 12) {
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> +
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> +
> + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + }
> +
> + if (max_bpc >= 10 && info->bpc >= 10) {
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> +
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> +
> + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + }
> +
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> +
> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
> + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> +
> + /* Default 8bit RGB fallback */
> + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> +
> + *num_output_fmts = i;
> +
> + return output_fmts;
> +}
> +
> +/*
> + * Possible input formats :
> + * - MEDIA_BUS_FMT_RGB888_1X24
> + * - MEDIA_BUS_FMT_YUV8_1X24
> + * - MEDIA_BUS_FMT_UYVY8_1X16
> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
> + * - MEDIA_BUS_FMT_RGB101010_1X30
> + * - MEDIA_BUS_FMT_YUV10_1X30
> + * - MEDIA_BUS_FMT_UYVY10_1X20
> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
> + * - MEDIA_BUS_FMT_RGB121212_1X36
> + * - MEDIA_BUS_FMT_YUV12_1X36
> + * - MEDIA_BUS_FMT_UYVY12_1X24
> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
> + * - MEDIA_BUS_FMT_RGB161616_1X48
> + * - MEDIA_BUS_FMT_YUV16_1X48
> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
> + */

Same here.

> +
> +/* Can return a maximum of 4 possible input formats for an output format */
> +#define MAX_INPUT_SEL_FORMATS 4

As Boris pointed out, that should be 3.

> +
> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state,
> + u32 output_fmt,
> + unsigned int *num_input_fmts)
> +{
> + u32 *input_fmts;
> + int i = 0;

i is never negative, you can make it an unsigned int.

> +
> + *num_input_fmts = 0;
> +
> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
> + GFP_KERNEL);
> + if (!input_fmts)
> + return NULL;
> +
> + switch (output_fmt) {
> + /* 8bit */
> + case MEDIA_BUS_FMT_RGB888_1X24:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> + break;
> + case MEDIA_BUS_FMT_YUV8_1X24:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> + break;
> + case MEDIA_BUS_FMT_UYVY8_1X16:
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
> + break;
> +
> + /* 10bit */
> + case MEDIA_BUS_FMT_RGB101010_1X30:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> + break;
> + case MEDIA_BUS_FMT_YUV10_1X30:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + break;
> + case MEDIA_BUS_FMT_UYVY10_1X20:
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
> + break;
> +
> + /* 12bit */
> + case MEDIA_BUS_FMT_RGB121212_1X36:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> + break;
> + case MEDIA_BUS_FMT_YUV12_1X36:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + break;
> + case MEDIA_BUS_FMT_UYVY12_1X24:
> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
> + break;
> +
> + /* 16bit */
> + case MEDIA_BUS_FMT_RGB161616_1X48:
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> + break;
> + case MEDIA_BUS_FMT_YUV16_1X48:
> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
> + break;
> +
> + /* 420 */

s/420/YUV 4:2:0/ ?

> + 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:
> + input_fmts[i++] = output_fmt;
> + break;
> + }
> +
> + *num_input_fmts = i;
> +
> + if (*num_input_fmts == 0) {
> + kfree(input_fmts);
> + input_fmts = NULL;
> + }
> +
> + return input_fmts;
> +}
> +
> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state)
> +{
> + struct dw_hdmi *hdmi = bridge->driver_private;
> +
> + dev_dbg(hdmi->dev, "selected output format %x\n",

s/%x/0x%04x/

> + bridge_state->output_bus_cfg.format);

Misalignment ?

> +
> + hdmi->hdmi_data.enc_out_bus_format =
> + bridge_state->output_bus_cfg.format;
> +
> + dev_dbg(hdmi->dev, "selected input format %x\n",
> + bridge_state->input_bus_cfg.format);

Same here. I would combine both messages:

dev_dbg(hdmi->dev, "input format: 0x%04x, output format: 0x04x\n",
bridge_state->input_bus_cfg.format,
bridge_state->output_bus_cfg.format);

> +
> + hdmi->hdmi_data.enc_in_bus_format =
> + bridge_state->input_bus_cfg.format;
> +
> + return 0;
> +}
> +
> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
> {
> struct dw_hdmi *hdmi = bridge->driver_private;
> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
> .atomic_reset = drm_atomic_helper_bridge_reset,
> .attach = dw_hdmi_bridge_attach,
> .detach = dw_hdmi_bridge_detach,
> + .atomic_check = dw_hdmi_bridge_atomic_check,
> + .atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts,
> + .atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts,
> .enable = dw_hdmi_bridge_enable,
> .disable = dw_hdmi_bridge_disable,
> .mode_set = dw_hdmi_bridge_mode_set,
> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
>
> hdmi->bridge.driver_private = hdmi;
> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
> +

This seems unrelated.

> #ifdef CONFIG_OF
> hdmi->bridge.of_node = pdev->dev.of_node;
> #endif

--
Regards,

Laurent Pinchart

2020-03-02 15:56:09

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 02/11] drm/bridge: dw-hdmi: add max bpc connector property

On 02/03/2020 10:18, Laurent Pinchart wrote:
> Hi Neil and Jonas,
>
> (CC'ing Daniel for a framework question)
>
> Thank you for the patch.
>
> On Fri, Feb 21, 2020 at 09:50:18AM +0100, Neil Armstrong wrote:
>> On 17/02/2020 07:38, Jernej Škrabec wrote:
>>> Dne četrtek, 06. februar 2020 ob 20:18:25 CET je Neil Armstrong napisal(a):
>>>> From: Jonas Karlman <[email protected]>
>>>>
>>>> Add the max_bpc property to the dw-hdmi connector to prepare support
>>>> for 10, 12 & 16bit output support.
>>>>
>>>> Signed-off-by: Jonas Karlman <[email protected]>
>>>> Signed-off-by: Neil Armstrong <[email protected]>
>>>> ---
>>>> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 4 ++++
>>>> 1 file changed, 4 insertions(+)
>>>>
>>>> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>>>> b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index
>>>> 9e0927d22db6..051001f77dd4 100644
>>>> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>>>> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>>>> @@ -2406,6 +2406,10 @@ static int dw_hdmi_bridge_attach(struct drm_bridge
>>>> *bridge) DRM_MODE_CONNECTOR_HDMIA,
>>>> hdmi->ddc);
>>>>
>>>> + drm_atomic_helper_connector_reset(connector);
>>>
>>> Why is this reset needed?
>>
>> I assume it's to allocate a new connector state to attach a the bpc propery.
>>
>> But indeed, this helper is never used here, but only as callback to the drm_connector_funcs->reset.
>>
>> But, amdgpu calls :
>> /*
>> * Some of the properties below require access to state, like bpc.
>> * Allocate some default initial connector state with our reset helper.
>> */
>> if (aconnector->base.funcs->reset)
>> aconnector->base.funcs->reset(&aconnector->base);
>>
>> which is the same.
>
> A comment would be useful:
>
> /*
> * drm_connector_attach_max_bpc_property() requires the
> * connector to have a state.
> */
> drm_atomic_helper_connector_reset(connector);
>
> drm_connector_attach_max_bpc_property(connector, 8, 16);
>

Done

> I don't like this much though, it feels like the initial reset performed
> by drm_mode_config_reset() should set default values for all state
> members that are related to properties. Daniel, what's the rationale
> behind the current implementation ?
>
> This is a DRM core issue that shouldn't block this patch though, so

I'll investigate why, but I haven't found out how the intel driver got the
connector state initialized since they don't use the atomic helpers....

>
> Reviewed-by: Laurent Pinchart <[email protected]>
>
>>>> +
>>>> + drm_connector_attach_max_bpc_property(connector, 8, 16);
>>>> +
>>>> if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
>>>> drm_object_attach_property(&connector->base,
>>>> connector->dev-
>>>> mode_config.hdr_output_metadata_property, 0);
>

2020-03-02 15:56:24

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 03/11] drm/bridge: dw-hdmi: Plug atomic state hooks to the default implementation

On 02/03/2020 10:56, Laurent Pinchart wrote:
> Hi Neil,
>
> Thank you for the patch.
>
> On Thu, Feb 06, 2020 at 08:18:26PM +0100, Neil Armstrong wrote:
>> Signed-off-by: Neil Armstrong <[email protected]>
>
> How about adding a commit message ?

Done, thanks

>
> Reviewed-by: Laurent Pinchart <[email protected]>
>
>> ---
>> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 3 +++
>> 1 file changed, 3 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> index 051001f77dd4..fec4a4bcd1fe 100644
>> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> @@ -2494,6 +2494,9 @@ static void dw_hdmi_bridge_enable(struct drm_bridge *bridge)
>> }
>>
>> static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
>> + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
>> + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
>> + .atomic_reset = drm_atomic_helper_bridge_reset,
>> .attach = dw_hdmi_bridge_attach,
>> .detach = dw_hdmi_bridge_detach,
>> .enable = dw_hdmi_bridge_enable,
>

2020-03-02 15:59:24

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 05/11] drm/bridge: synopsys: dw-hdmi: allow ycbcr420 modes for >= 0x200a

On 02/03/2020 11:03, Laurent Pinchart wrote:
> Hi Neil,
>
> Thank you for the patch.
>
> On Thu, Feb 06, 2020 at 08:18:28PM +0100, Neil Armstrong wrote:
>> Now the DW-HDMI Controller supports the HDMI2.0 modes, enable support
>> for these modes in the connector if the platform supports them.
>> We limit these modes to DW-HDMI IP version >= 0x200a which
>> are designed to support HDMI2.0 display modes.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 6 ++++++
>> include/drm/bridge/dw_hdmi.h | 1 +
>> 2 files changed, 7 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> index 15048ad694bc..4b35ea1427df 100644
>> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> @@ -3231,6 +3231,12 @@ __dw_hdmi_probe(struct platform_device *pdev,
>> hdmi->bridge.of_node = pdev->dev.of_node;
>> #endif
>>
>> + if (hdmi->version >= 0x200a)
>> + hdmi->connector.ycbcr_420_allowed =
>> + hdmi->plat_data->ycbcr_420_allowed;
>> + else
>> + hdmi->connector.ycbcr_420_allowed = false;
>> +
>
> The hdmi structure being allocated with kzalloc, you don't need the
> second branch of the if, but I'm fine if you prefer keeping it. Any
> either case,

Yep Andrzej said the same, but I prefer it to be explicit.

>
> Reviewed-by: Laurent Pinchart <[email protected]>

Thanks,
Neil

>
>> memset(&pdevinfo, 0, sizeof(pdevinfo));
>> pdevinfo.parent = dev;
>> pdevinfo.id = PLATFORM_DEVID_AUTO;
>> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
>> index 9d4d5cc47969..0b34a12c4a1c 100644
>> --- a/include/drm/bridge/dw_hdmi.h
>> +++ b/include/drm/bridge/dw_hdmi.h
>> @@ -129,6 +129,7 @@ struct dw_hdmi_plat_data {
>> unsigned long input_bus_format;
>> unsigned long input_bus_encoding;
>> bool use_drm_infoframe;
>> + bool ycbcr_420_allowed;
>>
>> /* Vendor PHY support */
>> const struct dw_hdmi_phy_ops *phy_ops;
>

2020-03-02 15:59:48

by Neil Armstrong

[permalink] [raw]
Subject: Re: [PATCH v4 04/11] drm/bridge: synopsys: dw-hdmi: add bus format negociation

On 02/03/2020 11:18, Laurent Pinchart wrote:
> Hi Neil,
>
> Thank you for the patch.
>
> On Thu, Feb 06, 2020 at 08:18:27PM +0100, Neil Armstrong wrote:
>> Add the atomic_get_output_bus_fmts, atomic_get_input_bus_fmts to negociate
>> the possible output and input formats for the current mode and monitor,
>> and use the negotiated formats in a basic atomic_check callback.
>>
>> Signed-off-by: Neil Armstrong <[email protected]>
>> ---
>> drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 272 +++++++++++++++++++++-
>> 1 file changed, 268 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> index fec4a4bcd1fe..15048ad694bc 100644
>> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
>> @@ -2095,11 +2095,10 @@ 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)
>> hdmi->hdmi_data.enc_in_bus_format =
>> hdmi->plat_data->input_bus_format;
>> - else
>> + else if (hdmi->hdmi_data.enc_in_bus_format == MEDIA_BUS_FMT_FIXED)
>> hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
>>
>> /* TOFIX: Get input encoding from plat data or fallback to none */
>> @@ -2109,8 +2108,8 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
>> 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 (hdmi->hdmi_data.enc_out_bus_format == MEDIA_BUS_FMT_FIXED)
>> + hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24;
>>
>> hdmi->hdmi_data.pix_repet_factor = 0;
>> hdmi->hdmi_data.hdcp_enable = 0;
>> @@ -2388,6 +2387,267 @@ static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs =
>> .atomic_check = dw_hdmi_connector_atomic_check,
>> };
>>
>> +/*
>> + * Possible output formats :
>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48,
>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36,
>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30,
>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24,
>> + * - MEDIA_BUS_FMT_YUV16_1X48,
>> + * - MEDIA_BUS_FMT_RGB161616_1X48,
>> + * - MEDIA_BUS_FMT_UYVY12_1X24,
>> + * - MEDIA_BUS_FMT_YUV12_1X36,
>> + * - MEDIA_BUS_FMT_RGB121212_1X36,
>> + * - MEDIA_BUS_FMT_UYVY10_1X20,
>> + * - MEDIA_BUS_FMT_YUV10_1X30,
>> + * - MEDIA_BUS_FMT_RGB101010_1X30,
>> + * - MEDIA_BUS_FMT_UYVY8_1X16,
>> + * - MEDIA_BUS_FMT_YUV8_1X24,
>> + * - MEDIA_BUS_FMT_RGB888_1X24,
>> + */
>
> I'd drop this comment as I don't think it brings much, except for a risk
> of getting out of sync with the implementation below :-)

I know it's redundant, but I'll prefer having it here to clarify which bus formats
are handled.

>
>> +
>> +/* Can return a maximum of 12 possible output formats for a mode/connector */
>> +#define MAX_OUTPUT_SEL_FORMATS 12
>
> I count 11 below.

Exact !

>
>> +
>> +static u32 *dw_hdmi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
>> + struct drm_bridge_state *bridge_state,
>> + struct drm_crtc_state *crtc_state,
>> + struct drm_connector_state *conn_state,
>> + unsigned int *num_output_fmts)
>> +{
>> + struct drm_connector *conn = conn_state->connector;
>> + struct drm_display_info *info = &conn->display_info;
>> + struct drm_display_mode *mode = &crtc_state->mode;
>> + u8 max_bpc = conn_state->max_requested_bpc;
>> + bool is_hdmi2_sink = info->hdmi.scdc.supported ||
>> + (info->color_formats & DRM_COLOR_FORMAT_YCRCB420);
>> + u32 *output_fmts;
>> + int i = 0;
>
> i is never negative, you can make it an unsigned int.
ok

>
>> +
>> + *num_output_fmts = 0;
>> +
>> + output_fmts = kcalloc(MAX_OUTPUT_SEL_FORMATS, sizeof(*output_fmts),
>> + GFP_KERNEL);
>> + if (!output_fmts)
>> + return NULL;
>> +
>> + /*
>> + * If the current mode enforces 4:2:0, force the output but format
>> + * to 4:2:0 and do not add the YUV422/444/RGB formats
>> + */
>> + if (conn->ycbcr_420_allowed &&
>> + (drm_mode_is_420_only(info, mode) ||
>> + (is_hdmi2_sink && drm_mode_is_420_also(info, mode)))) {
>> +
>> + /* Order bus formats from 16bit to 8bit if supported */
>> + if (max_bpc >= 16 && info->bpc == 16 &&
>> + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48))
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY16_0_5X48;
>> +
>> + if (max_bpc >= 12 && info->bpc >= 12 &&
>> + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36))
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36;
>> +
>> + if (max_bpc >= 10 && info->bpc >= 10 &&
>> + (info->hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30))
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30;
>> +
>> + /* Default 8bit fallback */
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24;
>> +
>> + *num_output_fmts = i;
>> +
>> + return output_fmts;
>> + }
>> +
>> + /*
>> + * Order bus formats from 16bit to 8bit and from YUV422 to RGB
>> + * if supported. In any case the default RGB888 format is added
>> + */
>> +
>> + if (max_bpc >= 16 && info->bpc == 16) {
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> +
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + }
>> +
>> + if (max_bpc >= 12 && info->bpc >= 12) {
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> +
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + }
>> +
>> + if (max_bpc >= 10 && info->bpc >= 10) {
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> +
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + }
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB422)
>> + output_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> +
>> + if (info->color_formats & DRM_COLOR_FORMAT_YCRCB444)
>> + output_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> +
>> + /* Default 8bit RGB fallback */
>> + output_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> +
>> + *num_output_fmts = i;
>> +
>> + return output_fmts;
>> +}
>> +
>> +/*
>> + * Possible input formats :
>> + * - MEDIA_BUS_FMT_RGB888_1X24
>> + * - MEDIA_BUS_FMT_YUV8_1X24
>> + * - MEDIA_BUS_FMT_UYVY8_1X16
>> + * - MEDIA_BUS_FMT_UYYVYY8_0_5X24
>> + * - MEDIA_BUS_FMT_RGB101010_1X30
>> + * - MEDIA_BUS_FMT_YUV10_1X30
>> + * - MEDIA_BUS_FMT_UYVY10_1X20
>> + * - MEDIA_BUS_FMT_UYYVYY10_0_5X30
>> + * - MEDIA_BUS_FMT_RGB121212_1X36
>> + * - MEDIA_BUS_FMT_YUV12_1X36
>> + * - MEDIA_BUS_FMT_UYVY12_1X24
>> + * - MEDIA_BUS_FMT_UYYVYY12_0_5X36
>> + * - MEDIA_BUS_FMT_RGB161616_1X48
>> + * - MEDIA_BUS_FMT_YUV16_1X48
>> + * - MEDIA_BUS_FMT_UYYVYY16_0_5X48
>> + */
>
> Same here.

I'll prefer keeping these aswell

>
>> +
>> +/* Can return a maximum of 4 possible input formats for an output format */
>> +#define MAX_INPUT_SEL_FORMATS 4
>
> As Boris pointed out, that should be 3.

Yep

>
>> +
>> +static u32 *dw_hdmi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
>> + struct drm_bridge_state *bridge_state,
>> + struct drm_crtc_state *crtc_state,
>> + struct drm_connector_state *conn_state,
>> + u32 output_fmt,
>> + unsigned int *num_input_fmts)
>> +{
>> + u32 *input_fmts;
>> + int i = 0;
>
> i is never negative, you can make it an unsigned int.
ok

>
>> +
>> + *num_input_fmts = 0;
>> +
>> + input_fmts = kcalloc(MAX_INPUT_SEL_FORMATS, sizeof(*input_fmts),
>> + GFP_KERNEL);
>> + if (!input_fmts)
>> + return NULL;
>> +
>> + switch (output_fmt) {
>> + /* 8bit */
>> + case MEDIA_BUS_FMT_RGB888_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + break;
>> + case MEDIA_BUS_FMT_YUV8_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY8_1X16:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY8_1X16;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV8_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB888_1X24;
>> + break;
>> +
>> + /* 10bit */
>> + case MEDIA_BUS_FMT_RGB101010_1X30:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + break;
>> + case MEDIA_BUS_FMT_YUV10_1X30:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY10_1X20:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY10_1X20;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV10_1X30;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB101010_1X30;
>> + break;
>> +
>> + /* 12bit */
>> + case MEDIA_BUS_FMT_RGB121212_1X36:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + break;
>> + case MEDIA_BUS_FMT_YUV12_1X36:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + break;
>> + case MEDIA_BUS_FMT_UYVY12_1X24:
>> + input_fmts[i++] = MEDIA_BUS_FMT_UYVY12_1X24;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV12_1X36;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB121212_1X36;
>> + break;
>> +
>> + /* 16bit */
>> + case MEDIA_BUS_FMT_RGB161616_1X48:
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> + break;
>> + case MEDIA_BUS_FMT_YUV16_1X48:
>> + input_fmts[i++] = MEDIA_BUS_FMT_YUV16_1X48;
>> + input_fmts[i++] = MEDIA_BUS_FMT_RGB161616_1X48;
>> + break;
>> +
>> + /* 420 */
>
> s/420/YUV 4:2:0/ ?

Looks better

>
>> + 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:
>> + input_fmts[i++] = output_fmt;
>> + break;
>> + }
>> +
>> + *num_input_fmts = i;
>> +
>> + if (*num_input_fmts == 0) {
>> + kfree(input_fmts);
>> + input_fmts = NULL;
>> + }
>> +
>> + return input_fmts;
>> +}
>> +
>> +static int dw_hdmi_bridge_atomic_check(struct drm_bridge *bridge,
>> + struct drm_bridge_state *bridge_state,
>> + struct drm_crtc_state *crtc_state,
>> + struct drm_connector_state *conn_state)
>> +{
>> + struct dw_hdmi *hdmi = bridge->driver_private;
>> +
>> + dev_dbg(hdmi->dev, "selected output format %x\n",
>
> s/%x/0x%04x/
>
>> + bridge_state->output_bus_cfg.format);
>
> Misalignment ?

Fixed

>
>> +
>> + hdmi->hdmi_data.enc_out_bus_format =
>> + bridge_state->output_bus_cfg.format;
>> +
>> + dev_dbg(hdmi->dev, "selected input format %x\n",
>> + bridge_state->input_bus_cfg.format);
>
> Same here. I would combine both messages:
>
> dev_dbg(hdmi->dev, "input format: 0x%04x, output format: 0x04x\n",
> bridge_state->input_bus_cfg.format,
> bridge_state->output_bus_cfg.format);

Looks better

>
>> +
>> + hdmi->hdmi_data.enc_in_bus_format =
>> + bridge_state->input_bus_cfg.format;
>> +
>> + return 0;
>> +}
>> +
>> static int dw_hdmi_bridge_attach(struct drm_bridge *bridge)
>> {
>> struct dw_hdmi *hdmi = bridge->driver_private;
>> @@ -2499,6 +2759,9 @@ static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
>> .atomic_reset = drm_atomic_helper_bridge_reset,
>> .attach = dw_hdmi_bridge_attach,
>> .detach = dw_hdmi_bridge_detach,
>> + .atomic_check = dw_hdmi_bridge_atomic_check,
>> + .atomic_get_output_bus_fmts = dw_hdmi_bridge_atomic_get_output_bus_fmts,
>> + .atomic_get_input_bus_fmts = dw_hdmi_bridge_atomic_get_input_bus_fmts,
>> .enable = dw_hdmi_bridge_enable,
>> .disable = dw_hdmi_bridge_disable,
>> .mode_set = dw_hdmi_bridge_mode_set,
>> @@ -2963,6 +3226,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
>>
>> hdmi->bridge.driver_private = hdmi;
>> hdmi->bridge.funcs = &dw_hdmi_bridge_funcs;
>> +
>
> This seems unrelated.

Yep

>
>> #ifdef CONFIG_OF
>> hdmi->bridge.of_node = pdev->dev.of_node;
>> #endif
>

Thanks,
Neil