Switch between requested color formats by setting the right bus formats,
configuring the VO GRF registers, and setting the right output mode.

To do this, the encoder's atomic_check queries the bus format of the
first bridge, which was determined by the bridge chain recursive format
selection. Pick the input format if it's !FIXED, otherwise, pick the
output format.

The previously unused GRF register color format defines are redone as
well. Both RK3588 and RK3576 use the same defines; it didn't look like
this as there was a typo in the previously (unused) definition.

Signed-off-by: Nicolas Frattaroli <[email protected]>
---
 drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c | 107 ++++++++++++++++++++++---
 1 file changed, 98 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c 
b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
index 1a09bcc96c3e..d39a4284b92c 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -11,6 +11,7 @@
 #include <linux/gpio/consumer.h>
 #include <linux/hw_bitfield.h>
 #include <linux/mfd/syscon.h>
+#include <linux/media-bus-format.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
@@ -43,10 +44,6 @@
 #define RK3576_8BPC                    0x0
 #define RK3576_10BPC                   0x6
 #define RK3576_COLOR_FORMAT_MASK       GENMASK(7, 4)
-#define RK3576_RGB                     0x9
-#define RK3576_YUV422                  0x1
-#define RK3576_YUV444                  0x2
-#define RK3576_YUV420                  0x3
 #define RK3576_CECIN_MASK              BIT(3)
 
 #define RK3576_VO0_GRF_SOC_CON14       0x0038
@@ -74,8 +71,6 @@
 #define RK3588_8BPC                    0x0
 #define RK3588_10BPC                   0x6
 #define RK3588_COLOR_FORMAT_MASK       GENMASK(3, 0)
-#define RK3588_RGB                     0x0
-#define RK3588_YUV420                  0x3
 #define RK3588_SCLIN_MASK              BIT(9)
 #define RK3588_SDAIN_MASK              BIT(10)
 #define RK3588_MODE_MASK               BIT(11)
@@ -87,6 +82,11 @@
 #define HOTPLUG_DEBOUNCE_MS            150
 #define MAX_HDMI_PORT_NUM              2
 
+#define RK_COLOR_FMT_RGB               0x0
+#define RK_COLOR_FMT_YUV422            0x1
+#define RK_COLOR_FMT_YUV444            0x2
+#define RK_COLOR_FMT_YUV420            0x3
+
 struct rockchip_hdmi_qp {
        struct device *dev;
        struct regmap *regmap;
@@ -115,6 +115,33 @@ static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct 
drm_encoder *encoder)
        return container_of(rkencoder, struct rockchip_hdmi_qp, encoder);
 }
 
+/**
+ * dw_hdmi_qp_rockchip_bus_fmt_to_reg - converts a bus format to a GRF reg 
value
+ * @bus_fmt: One of the MEDIA_BUS_FMT_s allowed by this driver's atomic_check
+ *
+ * Returns: an unshifted value to be written to the COLOR_FORMAT GRF register
+ * on success, or %-EINVAL if the bus format is not supported.
+ */
+static int __pure dw_hdmi_qp_rockchip_bus_fmt_to_reg(u32 bus_fmt)
+{
+       switch (bus_fmt) {
+       case MEDIA_BUS_FMT_RGB888_1X24:
+       case MEDIA_BUS_FMT_RGB101010_1X30:
+               return RK_COLOR_FMT_RGB;
+       case MEDIA_BUS_FMT_UYVY8_1X16:
+       case MEDIA_BUS_FMT_UYVY10_1X20:
+               return RK_COLOR_FMT_YUV422;
+       case MEDIA_BUS_FMT_YUV8_1X24:
+       case MEDIA_BUS_FMT_YUV10_1X30:
+               return RK_COLOR_FMT_YUV444;
+       case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+       case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+               return RK_COLOR_FMT_YUV420;
+       }
+
+       return -EINVAL;
+}
+
 static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
 {
        struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
@@ -130,29 +157,83 @@ static void dw_hdmi_qp_rockchip_encoder_enable(struct 
drm_encoder *encoder)
                hdmi->ctrl_ops->enc_init(hdmi, 
to_rockchip_crtc_state(crtc->state));
 }
 
+/**
+ * dw_hdmi_qp_rockchip_get_vop_format - get the bus format VOP should output
+ * @encoder: pointer to a &struct drm_encoder
+ * @conn_state: pointer to the current atomic &struct drm_connector_state
+ *
+ * Determines which bus format the Rockchip video processor should output as
+ * to feed into the bridge chain.
+ *
+ * Returns a MEDIA_BUS_FMT_* on success, or %0 on error.
+ */
+static u32 dw_hdmi_qp_rockchip_get_vop_format(struct drm_encoder *encoder,
+                                             struct drm_connector_state 
*conn_state)
+{
+       struct drm_bridge *bridge __free(drm_bridge_put) = NULL;
+       struct drm_bridge_state *bstate;
+
+       bridge = drm_bridge_chain_get_first_bridge(encoder);
+       if (!bridge)
+               return 0;
+
+       bstate = drm_atomic_get_bridge_state(conn_state->state, bridge);
+       if (!bstate)
+               return 0;
+
+       if (bstate->input_bus_cfg.format != MEDIA_BUS_FMT_FIXED)
+               return bstate->input_bus_cfg.format;
+
+       return bstate->output_bus_cfg.format;
+}
+
 static int
 dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
                                         struct drm_crtc_state *crtc_state,
                                         struct drm_connector_state *conn_state)
 {
-       struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
        struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+       struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
        union phy_configure_opts phy_cfg = {};
+       u32 ingest_fmt;
        int ret;
 
+       ingest_fmt = dw_hdmi_qp_rockchip_get_vop_format(encoder, conn_state);
+       if (!ingest_fmt)
+               return -EINVAL;
+
        if (hdmi->tmds_char_rate == conn_state->hdmi.tmds_char_rate &&
-           s->output_bpc == conn_state->hdmi.output_bpc)
+           s->output_bpc == conn_state->hdmi.output_bpc &&
+           s->bus_format == ingest_fmt)
                return 0;
 
+       switch (ingest_fmt) {
+       case MEDIA_BUS_FMT_RGB888_1X24:
+       case MEDIA_BUS_FMT_RGB101010_1X30:
+       case MEDIA_BUS_FMT_YUV8_1X24:
+       case MEDIA_BUS_FMT_YUV10_1X30:
+               s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+               break;
+       case MEDIA_BUS_FMT_UYVY8_1X16:
+               s->output_mode = ROCKCHIP_OUT_MODE_YUV422;
+               break;
+       case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+       case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+               s->output_mode = ROCKCHIP_OUT_MODE_YUV420;
+               break;
+       default:
+               return -EINVAL;
+       }
+
        phy_cfg.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate;
        phy_cfg.hdmi.bpc = conn_state->hdmi.output_bpc;
 
        ret = phy_configure(hdmi->phy, &phy_cfg);
        if (!ret) {
                hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate;
-               s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
                s->output_type = DRM_MODE_CONNECTOR_HDMIA;
                s->output_bpc = conn_state->hdmi.output_bpc;
+               s->bus_format = ingest_fmt;
        } else {
                dev_err(hdmi->dev, "Failed to configure phy: %d\n", ret);
        }
@@ -382,6 +463,7 @@ static void dw_hdmi_qp_rk3588_io_init(struct 
rockchip_hdmi_qp *hdmi)
 static void dw_hdmi_qp_rk3576_enc_init(struct rockchip_hdmi_qp *hdmi,
                                       struct rockchip_crtc_state *state)
 {
+       int color = dw_hdmi_qp_rockchip_bus_fmt_to_reg(state->bus_format);
        u32 val;
 
        if (state->output_bpc == 10)
@@ -389,12 +471,16 @@ static void dw_hdmi_qp_rk3576_enc_init(struct 
rockchip_hdmi_qp *hdmi,
        else
                val = FIELD_PREP_WM16(RK3576_COLOR_DEPTH_MASK, RK3576_8BPC);
 
+       if (likely(color > 0))
+               val |= FIELD_PREP_WM16(RK3576_COLOR_FORMAT_MASK, color);
+
        regmap_write(hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON8, val);
 }
 
 static void dw_hdmi_qp_rk3588_enc_init(struct rockchip_hdmi_qp *hdmi,
                                       struct rockchip_crtc_state *state)
 {
+       int color = dw_hdmi_qp_rockchip_bus_fmt_to_reg(state->bus_format);
        u32 val;
 
        if (state->output_bpc == 10)
@@ -402,6 +488,9 @@ static void dw_hdmi_qp_rk3588_enc_init(struct 
rockchip_hdmi_qp *hdmi,
        else
                val = FIELD_PREP_WM16(RK3588_COLOR_DEPTH_MASK, RK3588_8BPC);
 
+       if (likely(color > 0))
+               val |= FIELD_PREP_WM16(RK3588_COLOR_FORMAT_MASK, color);
+
        regmap_write(hdmi->vo_regmap,
                     hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3,
                     val);

-- 
2.52.0

Reply via email to