2023-12-16 16:27:34

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 00/27] Add HDMI support for RK3128

This is version 2 of my series that aims to add support for the display
controller (VOP) and the HDMI controller block of RK3128 (which is very
similar to the one found in RK3036).

The VOP part is very simple - everything we need for HDMI support is
already there. I only needed to split the output selection registers from
RK3036. The VOP has an IOMMU attached, but it has a serious silicon bug:
Registers can only be written, but not be read. As it's not possible to use
it with the IOMMU driver in it's current state I'm not adding it here and
we have to live with CMA for now - which works fine also. I got response
from the vendor, that there is no possibility to read the registers and an
workaround must be implemented in software in order to use it.

The inno-hdmi driver currently gets a lot of attention [0-2] and I'm
hooking in now also. As requested I incorporated some of Maxime's series
[0] (and tested them).
I have intentionally not removed any code dealing with output format
conversion in this series. In contrast to the input format, which is always
RGB on this platform and certainly can be dropped, that can be implemented
later. And secondly I need the conversion for RGB full range to RGB limited
range for this series.

I did also some smaller driver cleanups from my side and implemented a
custom connector state which now holds the data that belongs there and it
is not longer in the device structure and, of course, addressed the
feedback from v1 [3].
Please see individual patches for changelog.

Note: Patches are based and tested on next-20231213.

[0] https://lore.kernel.org/all/[email protected]
[1] https://lore.kernel.org/all/[email protected]
[2] https://lore.kernel.org/all/[email protected]
[3] https://lore.kernel.org/all/[email protected]/

Alex Bee (16):
dt-bindings: display: rockchip,inno-hdmi: Document RK3128 compatible
drm/rockchip: vop: Add output selection registers for RK312x
drm/rockchip: inno_hdmi: Fix video timing
drm/rockchip: inno_hdmi: Remove YUV-based csc coefficents
drm/rockchip: inno_hdmi: Drop irq struct member
drm/rockchip: inno_hdmi: Remove useless include
drm/rockchip: inno_hdmi: Subclass connector state
drm/rockchip: inno_hdmi: Move tmds rate to connector state subclass
drm/rockchip: inno_hdmi: Correctly setup HDMI quantization range
drm/rockchip: inno_hdmi: Add variant support
drm/rockchip: inno_hdmi: Add RK3128 support
drm/rockchip: inno_hdmi: Add basic mode validation
drm/rockchip: inno_hdmi: Drop custom fill_modes hook
ARM: dts: rockchip: Add display subsystem for RK3128
ARM: dts: rockchip: Add HDMI node for RK3128
ARM: dts: rockchip: Enable HDMI output for XPI-3128

Maxime Ripard (11):
drm/rockchip: inno_hdmi: Remove useless mode_fixup
drm/rockchip: inno_hdmi: Remove useless copy of drm_display_mode
drm/rockchip: inno_hdmi: Switch encoder hooks to atomic
drm/rockchip: inno_hdmi: Get rid of mode_set
drm/rockchip: inno_hdmi: no need to store vic
drm/rockchip: inno_hdmi: Remove unneeded has audio flag
drm/rockchip: inno_hdmi: Remove useless input format
drm/rockchip: inno_hdmi: Drop HDMI Vendor Infoframe support
drm/rockchip: inno_hdmi: Move infoframe disable to separate function
drm/rockchip: inno_hdmi: Switch to infoframe type
drm/rockchip: inno_hdmi: Remove unused drm device pointer

.../display/rockchip/rockchip,inno-hdmi.yaml | 40 +-
.../arm/boot/dts/rockchip/rk3128-xpi-3128.dts | 29 ++
arch/arm/boot/dts/rockchip/rk3128.dtsi | 60 +++
drivers/gpu/drm/rockchip/inno_hdmi.c | 482 +++++++++++-------
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 13 +-
drivers/gpu/drm/rockchip/rockchip_vop_reg.h | 3 +
6 files changed, 448 insertions(+), 179 deletions(-)


base-commit: 48e8992e33abf054bcc0bb2e77b2d43bb899212e
--
2.43.0



2023-12-16 16:27:41

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 02/27] drm/rockchip: vop: Add output selection registers for RK312x

In contrast to RK3036, RK312x SoCs have multiple output channels such as
RGB (i.e. LVDS TTL), LVDS, DSI and HDMI.

In order to support that, this splits output from RK3036 and defines an
separate one for RK3126 with the registers required to enable the
appropriate output and setup the correct polarity.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- rephrase commit message

drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 13 ++++++++++++-
drivers/gpu/drm/rockchip/rockchip_vop_reg.h | 3 +++
2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
index c51ca82320cb..b9ee02061d5b 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c
@@ -227,11 +227,22 @@ static const struct vop_win_data rk3126_vop_win_data[] = {
.type = DRM_PLANE_TYPE_CURSOR },
};

+static const struct vop_output rk3126_output = {
+ .pin_pol = VOP_REG(RK3036_DSP_CTRL0, 0xf, 4),
+ .hdmi_pin_pol = VOP_REG(RK3126_INT_SCALER, 0x7, 4),
+ .hdmi_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 22),
+ .hdmi_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 23),
+ .rgb_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 24),
+ .rgb_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 25),
+ .mipi_en = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 28),
+ .mipi_dclk_pol = VOP_REG(RK3036_AXI_BUS_CTRL, 0x1, 29),
+};
+
static const struct vop_data rk3126_vop = {
.intr = &rk3036_intr,
.common = &rk3036_common,
.modeset = &rk3036_modeset,
- .output = &rk3036_output,
+ .output = &rk3126_output,
.win = rk3126_vop_win_data,
.win_size = ARRAY_SIZE(rk3126_vop_win_data),
.max_output = { 1920, 1080 },
diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
index 406e981c75bd..fbf1bcc68625 100644
--- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
+++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
@@ -872,6 +872,9 @@
/* rk3036 register definition end */

/* rk3126 register definition */
+#define RK3126_INT_SCALER 0x0c
+
+/* win1 register */
#define RK3126_WIN1_MST 0x4c
#define RK3126_WIN1_DSP_INFO 0x50
#define RK3126_WIN1_DSP_ST 0x54
--
2.43.0


2023-12-16 16:28:19

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 04/27] drm/rockchip: inno_hdmi: Remove useless mode_fixup

From: Maxime Ripard <[email protected]>

The mode_fixup implementation doesn't do anything, so we can simply
remove it.

Signed-off-by: Maxime Ripard <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 8 --------
1 file changed, 8 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 345253e033c5..0b1740b38c7b 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -517,13 +517,6 @@ static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
inno_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
}

-static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adj_mode)
-{
- return true;
-}
-
static int
inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
@@ -540,7 +533,6 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
.enable = inno_hdmi_encoder_enable,
.disable = inno_hdmi_encoder_disable,
- .mode_fixup = inno_hdmi_encoder_mode_fixup,
.mode_set = inno_hdmi_encoder_mode_set,
.atomic_check = inno_hdmi_encoder_atomic_check,
};
--
2.43.0


2023-12-16 16:28:41

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 05/27] drm/rockchip: inno_hdmi: Remove useless copy of drm_display_mode

From: Maxime Ripard <[email protected]>

The driver maintains a copy of the adjusted mode but doesn't use it
anywhere. Remove it.

Signed-off-by: Maxime Ripard <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 4 ----
1 file changed, 4 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 0b1740b38c7b..14d2ba92a606 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -62,7 +62,6 @@ struct inno_hdmi {
unsigned int tmds_rate;

struct hdmi_data_info hdmi_data;
- struct drm_display_mode previous_mode;
};

static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
@@ -498,9 +497,6 @@ static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);

inno_hdmi_setup(hdmi, adj_mode);
-
- /* Store the display mode for plugin/DPMS poweron events */
- drm_mode_copy(&hdmi->previous_mode, adj_mode);
}

static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
--
2.43.0


2023-12-16 16:28:55

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 07/27] drm/rockchip: inno_hdmi: Get rid of mode_set

From: Maxime Ripard <[email protected]>

We're not doing anything special in atomic_mode_set so we can simply
merge it into atomic_enable.

Signed-off-by: Maxime Ripard <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 48c4f010b260..299770e481b7 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -490,21 +490,22 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
return 0;
}

-static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- struct drm_display_mode *adj_mode = &crtc_state->adjusted_mode;
- struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
-
- inno_hdmi_setup(hdmi, adj_mode);
-}
-
static void inno_hdmi_encoder_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
+ struct drm_connector_state *conn_state;
+ struct drm_crtc_state *crtc_state;
+
+ conn_state = drm_atomic_get_new_connector_state(state, &hdmi->connector);
+ if (WARN_ON(!conn_state))
+ return;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+ if (WARN_ON(!crtc_state))
+ return;

+ inno_hdmi_setup(hdmi, &crtc_state->adjusted_mode);
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}

@@ -533,7 +534,6 @@ static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
.atomic_check = inno_hdmi_encoder_atomic_check,
.atomic_enable = inno_hdmi_encoder_enable,
.atomic_disable = inno_hdmi_encoder_disable,
- .atomic_mode_set = inno_hdmi_encoder_mode_set,
};

static enum drm_connector_status
--
2.43.0


2023-12-16 16:29:15

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 03/27] drm/rockchip: inno_hdmi: Fix video timing

The controller wants the difference between *total and *sync_start in the
HDMI_VIDEO_EXT_*DELAY registers. Otherwise the signal is very unstable for
certain non-VIC modes. See downstream commit [0].

[0] https://github.com/rockchip-linux/kernel/commit/8eb559f2502c

Fixes: 412d4ae6b7a5 ("drm/rockchip: hdmi: add Innosilicon HDMI support")
Co-developed-by: Zheng Yang <[email protected]>
Signed-off-by: Zheng Yang <[email protected]>
Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- none

drivers/gpu/drm/rockchip/inno_hdmi.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 6e5b922a121e..345253e033c5 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -412,7 +412,7 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);

- value = mode->hsync_start - mode->hdisplay;
+ value = mode->htotal - mode->hsync_start;
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);

@@ -427,7 +427,7 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
value = mode->vtotal - mode->vdisplay;
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);

- value = mode->vsync_start - mode->vdisplay;
+ value = mode->vtotal - mode->vsync_start;
hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);

value = mode->vsync_end - mode->vsync_start;
--
2.43.0


2023-12-16 16:29:16

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 06/27] drm/rockchip: inno_hdmi: Switch encoder hooks to atomic

From: Maxime Ripard <[email protected]>

The inno_hdmi encoder still uses the !atomic variants of enable, disable
and modeset. Convert to their atomic equivalents.

Signed-off-by: Maxime Ripard <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 19 +++++++++++--------
1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 14d2ba92a606..48c4f010b260 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -491,22 +491,25 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
}

static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adj_mode)
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
{
+ struct drm_display_mode *adj_mode = &crtc_state->adjusted_mode;
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);

inno_hdmi_setup(hdmi, adj_mode);
}

-static void inno_hdmi_encoder_enable(struct drm_encoder *encoder)
+static void inno_hdmi_encoder_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);

inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}

-static void inno_hdmi_encoder_disable(struct drm_encoder *encoder)
+static void inno_hdmi_encoder_disable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);

@@ -527,10 +530,10 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
}

static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
- .enable = inno_hdmi_encoder_enable,
- .disable = inno_hdmi_encoder_disable,
- .mode_set = inno_hdmi_encoder_mode_set,
- .atomic_check = inno_hdmi_encoder_atomic_check,
+ .atomic_check = inno_hdmi_encoder_atomic_check,
+ .atomic_enable = inno_hdmi_encoder_enable,
+ .atomic_disable = inno_hdmi_encoder_disable,
+ .atomic_mode_set = inno_hdmi_encoder_mode_set,
};

static enum drm_connector_status
--
2.43.0


2023-12-16 16:29:19

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 08/27] drm/rockchip: inno_hdmi: no need to store vic

From: Maxime Ripard <[email protected]>

The mode's VIC is only ever used in the inno_hdmi_setup() function so
there's no need to store it in the main structure.

Signed-off-by: Maxime Ripard <[email protected]>
[made checkpatch happy]
Signed-off-by: Alex Bee <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 299770e481b7..d99896f1a73a 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -28,7 +28,6 @@
#include "inno_hdmi.h"

struct hdmi_data_info {
- int vic;
bool sink_has_audio;
unsigned int enc_in_format;
unsigned int enc_out_format;
@@ -443,16 +442,15 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
{
struct drm_display_info *display = &hdmi->connector.display_info;
-
- hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+ u8 vic = drm_match_cea_mode(mode);

hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;

- if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
- (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
- (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
- (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+ if (vic == 6 || vic == 7 ||
+ vic == 21 || vic == 22 ||
+ vic == 2 || vic == 3 ||
+ vic == 17 || vic == 18)
hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
else
hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
--
2.43.0


2023-12-16 16:29:43

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 09/27] drm/rockchip: inno_hdmi: Remove unneeded has audio flag

From: Maxime Ripard <[email protected]>

The sink_has_audio flag is not used anywhere in the driver so let's get
rid of it. It's redundant with drm_display_info.has_audio anyway.

Signed-off-by: Maxime Ripard <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 2 --
1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index d99896f1a73a..58aff7a9c09a 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -28,7 +28,6 @@
#include "inno_hdmi.h"

struct hdmi_data_info {
- bool sink_has_audio;
unsigned int enc_in_format;
unsigned int enc_out_format;
unsigned int colorimetry;
@@ -554,7 +553,6 @@ static int inno_hdmi_connector_get_modes(struct drm_connector *connector)

edid = drm_get_edid(connector, hdmi->ddc);
if (edid) {
- hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
drm_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
--
2.43.0


2023-12-16 16:30:11

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 10/27] drm/rockchip: inno_hdmi: Remove useless input format

From: Maxime Ripard <[email protected]>

The driver has a lot of logic to deal with multiple input formats, but
hardcodes it to RGB. This means that most of that code has been dead
code, so let's get rid of it.

Signed-off-by: Maxime Ripard <[email protected]>
[made checkpatch happy]
Signed-off-by: Alex Bee <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 41 ++++++++--------------------
1 file changed, 11 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 58aff7a9c09a..7c75feedacad 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -28,7 +28,6 @@
#include "inno_hdmi.h"

struct hdmi_data_info {
- unsigned int enc_in_format;
unsigned int enc_out_format;
unsigned int colorimetry;
};
@@ -328,47 +327,30 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
v_VIDEO_INPUT_CSP(0);
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);

- if (data->enc_in_format == data->enc_out_format) {
- if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
- (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
- value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
- hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
-
- hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
- m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
- v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
- v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
- return 0;
- }
+ if (data->enc_out_format == HDMI_COLORSPACE_RGB) {
+ value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+ hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+ hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+ m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+ v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+ v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+ return 0;
}

if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
- if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
- (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+ if (data->enc_out_format == HDMI_COLORSPACE_YUV444) {
csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
- } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
- (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
- csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
- auto_csc = AUTO_CSC_ENABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_DISABLE;
}
} else {
- if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
- (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+ if (data->enc_out_format == HDMI_COLORSPACE_YUV444) {
csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
- } else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
- (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
- csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
- auto_csc = AUTO_CSC_ENABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_DISABLE;
}
}

@@ -443,7 +425,6 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
struct drm_display_info *display = &hdmi->connector.display_info;
u8 vic = drm_match_cea_mode(mode);

- hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;

if (vic == 6 || vic == 7 ||
--
2.43.0


2023-12-16 16:30:35

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 11/27] drm/rockchip: inno_hdmi: Remove YUV-based csc coefficents

Now that the unneeded support for YUV based input formats is gone, the csc
coefficients for those formats can be dropped as well.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- new patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 37 ----------------------------
1 file changed, 37 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 7c75feedacad..04344ee1265d 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -74,49 +74,12 @@ static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
}

enum {
- CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
- CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
- CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
CSC_RGB_0_255_TO_RGB_16_235_8BIT,
};

static const char coeff_csc[][24] = {
- /*
- * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
- * R = 1.164*Y + 1.596*V - 204
- * G = 1.164*Y - 0.391*U - 0.813*V + 154
- * B = 1.164*Y + 2.018*U - 258
- */
- {
- 0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
- 0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
- 0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
- },
- /*
- * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
- * R = Y + 1.402*V - 248
- * G = Y - 0.344*U - 0.714*V + 135
- * B = Y + 1.772*U - 227
- */
- {
- 0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
- 0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
- 0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
- },
- /*
- * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
- * R = 1.164*Y + 1.793*V - 248
- * G = 1.164*Y - 0.213*U - 0.534*V + 77
- * B = 1.164*Y + 2.115*U - 289
- */
- {
- 0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
- 0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
- 0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
- },
-
/*
* RGB2YUV:601 SD mode:
* Cb = -0.291G - 0.148R + 0.439B + 128
--
2.43.0


2023-12-16 16:30:54

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 12/27] drm/rockchip: inno_hdmi: Drop HDMI Vendor Infoframe support

From: Maxime Ripard <[email protected]>

The HDMI vendor infoframe is only meant to be sent with 4k60 modes and
higher, but the controller doesn't support them. Let's drop them from
the kernel.

Suggested-by: Johan Jonker <[email protected]>
Signed-off-by: Maxime Ripard <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 35 ++++++++--------------------
1 file changed, 10 insertions(+), 25 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 04344ee1265d..1dd757845547 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -208,11 +208,15 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
}

static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
- union hdmi_infoframe *frame, u32 frame_index,
- u32 mask, u32 disable, u32 enable)
+ union hdmi_infoframe *frame, u32 frame_index)
{
- if (mask)
- hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
+ struct drm_connector *connector = &hdmi->connector;
+
+ if (frame_index != INFOFRAME_AVI) {
+ drm_err(connector->dev,
+ "Unsupported infoframe type: %u\n", frame_index);
+ return 0;
+ }

hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);

@@ -228,28 +232,11 @@ static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
for (i = 0; i < rc; i++)
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
packed_frame[i]);
-
- if (mask)
- hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
}

return setup_rc;
}

-static int inno_hdmi_config_video_vsi(struct inno_hdmi *hdmi,
- struct drm_display_mode *mode)
-{
- union hdmi_infoframe frame;
- int rc;
-
- rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
- &hdmi->connector,
- mode);
-
- return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_VSI,
- m_PACKET_VSI_EN, v_PACKET_VSI_EN(0), v_PACKET_VSI_EN(1));
-}
-
static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
{
@@ -267,7 +254,7 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
else
frame.avi.colorspace = HDMI_COLORSPACE_RGB;

- return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI, 0, 0, 0);
+ return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI);
}

static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
@@ -410,10 +397,8 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,

inno_hdmi_config_video_csc(hdmi);

- if (display->is_hdmi) {
+ if (display->is_hdmi)
inno_hdmi_config_video_avi(hdmi, mode);
- inno_hdmi_config_video_vsi(hdmi, mode);
- }

/*
* When IP controller have configured to an accurate video
--
2.43.0


2023-12-16 16:31:09

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 13/27] drm/rockchip: inno_hdmi: Move infoframe disable to separate function

From: Maxime Ripard <[email protected]>

The code to upload infoframes to the controller uses a weird construct
which, based on the previous function call return code, will either
disable or enable that infoframe.

In order to get rid of that argument, let's split the function to
disable the infoframe into a separate function and make it obvious what
we are doing in the error path.

Signed-off-by: Maxime Ripard <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 46 ++++++++++++++++++----------
1 file changed, 30 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 1dd757845547..6354949bfd8e 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -207,34 +207,44 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}

-static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, int setup_rc,
- union hdmi_infoframe *frame, u32 frame_index)
+static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi, u32 frame_index)
{
struct drm_connector *connector = &hdmi->connector;

if (frame_index != INFOFRAME_AVI) {
drm_err(connector->dev,
"Unsupported infoframe type: %u\n", frame_index);
- return 0;
+ return;
}

hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+}

- if (setup_rc >= 0) {
- u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
- ssize_t rc, i;
-
- rc = hdmi_infoframe_pack(frame, packed_frame,
- sizeof(packed_frame));
- if (rc < 0)
- return rc;
+static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
+ union hdmi_infoframe *frame, u32 frame_index)
+{
+ struct drm_connector *connector = &hdmi->connector;
+ u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
+ ssize_t rc, i;

- for (i = 0; i < rc; i++)
- hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
- packed_frame[i]);
+ if (frame_index != INFOFRAME_AVI) {
+ drm_err(connector->dev,
+ "Unsupported infoframe type: %u\n", frame_index);
+ return 0;
}

- return setup_rc;
+ inno_hdmi_disable_frame(hdmi, frame_index);
+
+ rc = hdmi_infoframe_pack(frame, packed_frame,
+ sizeof(packed_frame));
+ if (rc < 0)
+ return rc;
+
+ for (i = 0; i < rc; i++)
+ hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
+ packed_frame[i]);
+
+ return 0;
}

static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
@@ -246,6 +256,10 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
&hdmi->connector,
mode);
+ if (rc) {
+ inno_hdmi_disable_frame(hdmi, INFOFRAME_AVI);
+ return rc;
+ }

if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
@@ -254,7 +268,7 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
else
frame.avi.colorspace = HDMI_COLORSPACE_RGB;

- return inno_hdmi_upload_frame(hdmi, rc, &frame, INFOFRAME_AVI);
+ return inno_hdmi_upload_frame(hdmi, &frame, INFOFRAME_AVI);
}

static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
--
2.43.0


2023-12-16 16:31:27

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 14/27] drm/rockchip: inno_hdmi: Switch to infoframe type

From: Maxime Ripard <[email protected]>

The inno_hdmi driver relies on its own internal infoframe type matching
the hardware.

This works fine, but in order to make further reworks easier, let's
switch to the HDMI spec definition of those types.

Signed-off-by: Maxime Ripard <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 21 +++++++++++----------
1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 6354949bfd8e..b6b34f4b8cda 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -207,33 +207,34 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
inno_hdmi_set_pwr_mode(hdmi, NORMAL);
}

-static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi, u32 frame_index)
+static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi,
+ enum hdmi_infoframe_type type)
{
struct drm_connector *connector = &hdmi->connector;

- if (frame_index != INFOFRAME_AVI) {
+ if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev,
- "Unsupported infoframe type: %u\n", frame_index);
+ "Unsupported infoframe type: %u\n", type);
return;
}

- hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+ hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
}

static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
- union hdmi_infoframe *frame, u32 frame_index)
+ union hdmi_infoframe *frame, enum hdmi_infoframe_type type)
{
struct drm_connector *connector = &hdmi->connector;
u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
ssize_t rc, i;

- if (frame_index != INFOFRAME_AVI) {
+ if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev,
- "Unsupported infoframe type: %u\n", frame_index);
+ "Unsupported infoframe type: %u\n", type);
return 0;
}

- inno_hdmi_disable_frame(hdmi, frame_index);
+ inno_hdmi_disable_frame(hdmi, type);

rc = hdmi_infoframe_pack(frame, packed_frame,
sizeof(packed_frame));
@@ -257,7 +258,7 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
&hdmi->connector,
mode);
if (rc) {
- inno_hdmi_disable_frame(hdmi, INFOFRAME_AVI);
+ inno_hdmi_disable_frame(hdmi, HDMI_INFOFRAME_TYPE_AVI);
return rc;
}

@@ -268,7 +269,7 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
else
frame.avi.colorspace = HDMI_COLORSPACE_RGB;

- return inno_hdmi_upload_frame(hdmi, &frame, INFOFRAME_AVI);
+ return inno_hdmi_upload_frame(hdmi, &frame, HDMI_INFOFRAME_TYPE_AVI);
}

static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
--
2.43.0


2023-12-16 16:31:43

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 15/27] drm/rockchip: inno_hdmi: Remove unused drm device pointer

From: Maxime Ripard <[email protected]>

The drm_dev field in the inno_hdmi struct stores a pointer to the DRM
device but is never used anywhere in the driver. Let's remove it.

Signed-off-by: Maxime Ripard <[email protected]>
Tested-by: Alex Bee <[email protected]>
---
changes in v2:
- imported patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 2 --
1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index b6b34f4b8cda..e37023d8fa39 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -44,7 +44,6 @@ struct inno_hdmi_i2c {

struct inno_hdmi {
struct device *dev;
- struct drm_device *drm_dev;

int irq;
struct clk *pclk;
@@ -760,7 +759,6 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
return -ENOMEM;

hdmi->dev = dev;
- hdmi->drm_dev = drm;

hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hdmi->regs))
--
2.43.0


2023-12-16 16:32:02

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 16/27] drm/rockchip: inno_hdmi: Drop irq struct member

The struct member irq isn't used anywhere. Drop it.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- new patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index e37023d8fa39..d9eb8cdc0148 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -45,7 +45,6 @@ struct inno_hdmi_i2c {
struct inno_hdmi {
struct device *dev;

- int irq;
struct clk *pclk;
void __iomem *regs;

--
2.43.0


2023-12-16 16:32:12

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 17/27] drm/rockchip: inno_hdmi: Remove useless include

The inclusion syscon.h isn't used anywhere. Remove it.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- new patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 1 -
1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index d9eb8cdc0148..62c7e2275246 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -10,7 +10,6 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/hdmi.h>
-#include <linux/mfd/syscon.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
--
2.43.0


2023-12-16 16:32:36

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 18/27] drm/rockchip: inno_hdmi: Subclass connector state

The data which is currently hold in hdmi_data should not be part of device
itself but of the connector state.
Introduce a connector state subclass and move the data from hdmi_data in
there.

Suggested-by: Maxime Ripard <[email protected]>
Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- new patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 67 +++++++++++++++++++++-------
1 file changed, 50 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 62c7e2275246..f9bfae1e97a2 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -26,11 +26,6 @@

#include "inno_hdmi.h"

-struct hdmi_data_info {
- unsigned int enc_out_format;
- unsigned int colorimetry;
-};
-
struct inno_hdmi_i2c {
struct i2c_adapter adap;

@@ -54,8 +49,12 @@ struct inno_hdmi {
struct i2c_adapter *ddc;

unsigned int tmds_rate;
+};

- struct hdmi_data_info hdmi_data;
+struct inno_hdmi_connector_state {
+ struct drm_connector_state base;
+ unsigned int enc_out_format;
+ unsigned int colorimetry;
};

static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
@@ -70,6 +69,9 @@ static struct inno_hdmi *connector_to_inno_hdmi(struct drm_connector *connector)
return container_of(connector, struct inno_hdmi, connector);
}

+#define to_inno_hdmi_conn_state(_state) \
+ container_of_const(_state, struct inno_hdmi_connector_state, base)
+
enum {
CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
@@ -248,6 +250,10 @@ static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
struct drm_display_mode *mode)
{
+ struct drm_connector *connector = &hdmi->connector;
+ struct drm_connector_state *conn_state = connector->state;
+ struct inno_hdmi_connector_state *inno_conn_state =
+ to_inno_hdmi_conn_state(conn_state);
union hdmi_infoframe frame;
int rc;

@@ -259,9 +265,9 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
return rc;
}

- if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+ if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444)
frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
- else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
+ else if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV422)
frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
else
frame.avi.colorspace = HDMI_COLORSPACE_RGB;
@@ -271,7 +277,10 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,

static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
{
- struct hdmi_data_info *data = &hdmi->hdmi_data;
+ struct drm_connector *connector = &hdmi->connector;
+ struct drm_connector_state *conn_state = connector->state;
+ struct inno_hdmi_connector_state *inno_conn_state =
+ to_inno_hdmi_conn_state(conn_state);
int c0_c2_change = 0;
int csc_enable = 0;
int csc_mode = 0;
@@ -289,7 +298,7 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
v_VIDEO_INPUT_CSP(0);
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);

- if (data->enc_out_format == HDMI_COLORSPACE_RGB) {
+ if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) {
value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);

@@ -300,15 +309,15 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
return 0;
}

- if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
- if (data->enc_out_format == HDMI_COLORSPACE_YUV444) {
+ if (inno_conn_state->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+ if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
}
} else {
- if (data->enc_out_format == HDMI_COLORSPACE_YUV444) {
+ if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
@@ -386,16 +395,20 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
{
struct drm_display_info *display = &hdmi->connector.display_info;
u8 vic = drm_match_cea_mode(mode);
+ struct drm_connector *connector = &hdmi->connector;
+ struct drm_connector_state *conn_state = connector->state;
+ struct inno_hdmi_connector_state *inno_conn_state =
+ to_inno_hdmi_conn_state(conn_state);

- hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+ inno_conn_state->enc_out_format = HDMI_COLORSPACE_RGB;

if (vic == 6 || vic == 7 ||
vic == 21 || vic == 22 ||
vic == 2 || vic == 3 ||
vic == 17 || vic == 18)
- hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+ inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_601;
else
- hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+ inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;

/* Mute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
@@ -522,12 +535,32 @@ static void inno_hdmi_connector_destroy(struct drm_connector *connector)
drm_connector_cleanup(connector);
}

+static struct drm_connector_state *
+inno_hdmi_connector_duplicate_state(struct drm_connector *connector)
+{
+ struct inno_hdmi_connector_state *inno_conn_state;
+
+ if (WARN_ON(!connector->state))
+ return NULL;
+
+ inno_conn_state = kmemdup(to_inno_hdmi_conn_state(connector->state),
+ sizeof(*inno_conn_state), GFP_KERNEL);
+
+ if (!inno_conn_state)
+ return NULL;
+
+ __drm_atomic_helper_connector_duplicate_state(connector,
+ &inno_conn_state->base);
+
+ return &inno_conn_state->base;
+}
+
static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
.fill_modes = inno_hdmi_probe_single_connector_modes,
.detect = inno_hdmi_connector_detect,
.destroy = inno_hdmi_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_duplicate_state = inno_hdmi_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};

--
2.43.0


2023-12-16 16:32:52

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 19/27] drm/rockchip: inno_hdmi: Move tmds rate to connector state subclass

Similar to the othter members of inno_hdmi_connector_state the tmds_rate is
not a property of the device, but of the connector state. Move it to
inno_hdmi_connector_state and make it a long to comply with the clock
framework. To get arround the issue of not having the connector state when
inno_hdmi_i2c_init is called in the bind path, getting the tmds rate is
wrapped in function which returns the fallback rate if the connector
doesn't have a state yet.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- new patch

drivers/gpu/drm/rockchip/inno_hdmi.c | 36 +++++++++++++++++++---------
1 file changed, 25 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index f9bfae1e97a2..6799d24501b8 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -47,14 +47,13 @@ struct inno_hdmi {

struct inno_hdmi_i2c *i2c;
struct i2c_adapter *ddc;
-
- unsigned int tmds_rate;
};

struct inno_hdmi_connector_state {
struct drm_connector_state base;
unsigned int enc_out_format;
unsigned int colorimetry;
+ unsigned long tmds_rate;
};

static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
@@ -133,11 +132,33 @@ static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
hdmi_writeb(hdmi, offset, temp);
}

+static unsigned long inno_hdmi_tmds_rate(struct inno_hdmi *hdmi)
+{
+ struct drm_connector *connector = &hdmi->connector;
+ struct drm_connector_state *conn_state = connector->state;
+ struct inno_hdmi_connector_state *inno_conn_state;
+
+ if (conn_state) {
+ inno_conn_state = to_inno_hdmi_conn_state(conn_state);
+ return inno_conn_state->tmds_rate;
+ }
+
+ /*
+ * When IP controller haven't configured to an accurate video
+ * timing, then the TMDS clock source would be switched to
+ * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
+ * and reconfigure the DDC clock.
+ */
+
+ return clk_get_rate(hdmi->pclk);
+}
+
static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
{
int ddc_bus_freq;
+ unsigned long tmds_rate = inno_hdmi_tmds_rate(hdmi);

- ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+ ddc_bus_freq = (tmds_rate >> 2) / HDMI_SCL_RATE;

hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
@@ -431,7 +452,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
* DCLK_LCDC, so we need to init the TMDS rate to mode pixel
* clock rate, and reconfigure the DDC clock.
*/
- hdmi->tmds_rate = mode->clock * 1000;
+ inno_conn_state->tmds_rate = mode->clock * 1000;
inno_hdmi_i2c_init(hdmi);

/* Unmute video and audio output */
@@ -823,13 +844,6 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
goto err_disable_clk;
}

- /*
- * When IP controller haven't configured to an accurate video
- * timing, then the TMDS clock source would be switched to
- * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
- * and reconfigure the DDC clock.
- */
- hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
inno_hdmi_i2c_init(hdmi);

ret = inno_hdmi_register(drm, hdmi);
--
2.43.0


2023-12-16 16:33:09

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 20/27] drm/rockchip: inno_hdmi: Correctly setup HDMI quantization range

The display controller will always give full range RGB regardless of the
mode set, but HDMI requires certain modes to be transmitted in limited
range RGB. This is especially required for HDMI sinks which do not support
non-standard quantization ranges.

This enables color space conversion for those modes and sets the
quantization range accordingly in the AVI infoframe.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- made rgb_limited_range part of the new custom connector state

drivers/gpu/drm/rockchip/inno_hdmi.c | 60 +++++++++++++++++++---------
1 file changed, 42 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 6799d24501b8..9f27a5faf12d 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -54,6 +54,7 @@ struct inno_hdmi_connector_state {
unsigned int enc_out_format;
unsigned int colorimetry;
unsigned long tmds_rate;
+ bool rgb_limited_range;
};

static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
@@ -293,6 +294,18 @@ static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi,
else
frame.avi.colorspace = HDMI_COLORSPACE_RGB;

+ if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) {
+ drm_hdmi_avi_infoframe_quant_range(&frame.avi,
+ connector, mode,
+ inno_conn_state->rgb_limited_range ?
+ HDMI_QUANTIZATION_RANGE_LIMITED :
+ HDMI_QUANTIZATION_RANGE_FULL);
+ } else {
+ frame.avi.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
+ frame.avi.ycc_quantization_range =
+ HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
+ }
+
return inno_hdmi_upload_frame(hdmi, &frame, HDMI_INFOFRAME_TYPE_AVI);
}

@@ -320,29 +333,37 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);

if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) {
- value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
- hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
-
- hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
- m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
- v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
- v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
- return 0;
- }
-
- if (inno_conn_state->colorimetry == HDMI_COLORIMETRY_ITU_601) {
- if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
- csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+ if (inno_conn_state->rgb_limited_range) {
+ csc_mode = CSC_RGB_0_255_TO_RGB_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE;
+
+ } else {
+ value = v_SOF_DISABLE | v_COLOR_DEPTH_NOT_INDICATED(1);
+ hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL3, value);
+
+ hdmi_modb(hdmi, HDMI_VIDEO_CONTRL,
+ m_VIDEO_AUTO_CSC | m_VIDEO_C0_C2_SWAP,
+ v_VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+ v_VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+ return 0;
}
} else {
- if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
- csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
- auto_csc = AUTO_CSC_DISABLE;
- c0_c2_change = C0_C2_CHANGE_DISABLE;
- csc_enable = v_CSC_ENABLE;
+ if (inno_conn_state->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+ if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
+ csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+ auto_csc = AUTO_CSC_DISABLE;
+ c0_c2_change = C0_C2_CHANGE_DISABLE;
+ csc_enable = v_CSC_ENABLE;
+ }
+ } else {
+ if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) {
+ csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+ auto_csc = AUTO_CSC_DISABLE;
+ c0_c2_change = C0_C2_CHANGE_DISABLE;
+ csc_enable = v_CSC_ENABLE;
+ }
}
}

@@ -431,6 +452,9 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
else
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;

+ inno_conn_state->rgb_limited_range =
+ drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
+
/* Mute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
--
2.43.0


2023-12-16 16:33:26

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 21/27] drm/rockchip: inno_hdmi: Add variant support

In preparation to support RK3128's integration of the controller, this
patch adds a simple variant implementation. They mainly differ in the phy
configuration required, so those are part of the match_data. The values
have been taken from downstream. The pixelclocks in there are meant to be
max-inclusive.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- no changes

drivers/gpu/drm/rockchip/inno_hdmi.c | 69 +++++++++++++++++++++++++++-
1 file changed, 67 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 9f27a5faf12d..579baba6a61b 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -26,6 +26,17 @@

#include "inno_hdmi.h"

+struct inno_hdmi_phy_config {
+ unsigned long pixelclock;
+ u8 pre_emphasis;
+ u8 voltage_level_control;
+};
+
+struct inno_hdmi_variant {
+ struct inno_hdmi_phy_config *phy_configs;
+ struct inno_hdmi_phy_config *default_phy_config;
+};
+
struct inno_hdmi_i2c {
struct i2c_adapter adap;

@@ -47,6 +58,8 @@ struct inno_hdmi {

struct inno_hdmi_i2c *i2c;
struct i2c_adapter *ddc;
+
+ const struct inno_hdmi_variant *variant;
};

struct inno_hdmi_connector_state {
@@ -114,6 +127,30 @@ static const char coeff_csc[][24] = {
},
};

+static struct inno_hdmi_phy_config rk3036_hdmi_phy_configs[] = {
+ { 74250000, 0x3f, 0xbb },
+ { 165000000, 0x6f, 0xbb },
+ { ~0UL, 0x00, 0x00 }
+};
+
+static int inno_hdmi_find_phy_config(struct inno_hdmi *hdmi,
+ unsigned long pixelclk)
+{
+ const struct inno_hdmi_phy_config *phy_configs =
+ hdmi->variant->phy_configs;
+ int i;
+
+ for (i = 0; phy_configs[i].pixelclock != ~0UL; i++) {
+ if (pixelclk <= phy_configs[i].pixelclock)
+ return i;
+ }
+
+ DRM_DEV_DEBUG(hdmi->dev, "No phy configuration for pixelclock %lu\n",
+ pixelclk);
+
+ return -EINVAL;
+}
+
static inline u8 hdmi_readb(struct inno_hdmi *hdmi, u16 offset)
{
return readl_relaxed(hdmi->regs + (offset) * 0x04);
@@ -179,12 +216,26 @@ static void inno_hdmi_sys_power(struct inno_hdmi *hdmi, bool enable)

static void inno_hdmi_set_pwr_mode(struct inno_hdmi *hdmi, int mode)
{
+ struct inno_hdmi_phy_config *phy_config;
+ int ret;
+ unsigned long tmds_rate;
+
switch (mode) {
case NORMAL:
inno_hdmi_sys_power(hdmi, false);
+ tmds_rate = inno_hdmi_tmds_rate(hdmi);
+ ret = inno_hdmi_find_phy_config(hdmi, tmds_rate);
+ if (ret < 0) {
+ phy_config = hdmi->variant->default_phy_config;
+ DRM_DEV_ERROR(hdmi->dev,
+ "Using default phy configuration for TMDS rate %lu",
+ tmds_rate);
+ } else {
+ phy_config = &hdmi->variant->phy_configs[ret];
+ }

- hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x6f);
- hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0xbb);
+ hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, phy_config->pre_emphasis);
+ hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->voltage_level_control);

hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
@@ -827,6 +878,8 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = data;
struct inno_hdmi *hdmi;
+ const struct inno_hdmi_variant *variant;
+
int irq;
int ret;

@@ -836,6 +889,12 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,

hdmi->dev = dev;

+ variant = of_device_get_match_data(hdmi->dev);
+ if (!variant)
+ return -EINVAL;
+
+ hdmi->variant = variant;
+
hdmi->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(hdmi->regs))
return PTR_ERR(hdmi->regs);
@@ -923,8 +982,14 @@ static void inno_hdmi_remove(struct platform_device *pdev)
component_del(&pdev->dev, &inno_hdmi_ops);
}

+static const struct inno_hdmi_variant rk3036_inno_hdmi_variant = {
+ .phy_configs = rk3036_hdmi_phy_configs,
+ .default_phy_config = &rk3036_hdmi_phy_configs[1],
+};
+
static const struct of_device_id inno_hdmi_dt_ids[] = {
{ .compatible = "rockchip,rk3036-inno-hdmi",
+ .data = &rk3036_inno_hdmi_variant,
},
{},
};
--
2.43.0


2023-12-16 16:33:43

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 22/27] drm/rockchip: inno_hdmi: Add RK3128 support

This variant requires the phy reference clock to be enabled before the DDC
block can work and the (initial) DDC bus frequency is calculated based on
the rate of this clock. Besides the only difference is phy configuration
required to make the driver working for this variant as well.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- no changes

drivers/gpu/drm/rockchip/inno_hdmi.c | 46 +++++++++++++++++++++++++---
1 file changed, 41 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 579baba6a61b..792e5fad09bf 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -51,6 +51,7 @@ struct inno_hdmi {
struct device *dev;

struct clk *pclk;
+ struct clk *refclk;
void __iomem *regs;

struct drm_connector connector;
@@ -133,6 +134,12 @@ static struct inno_hdmi_phy_config rk3036_hdmi_phy_configs[] = {
{ ~0UL, 0x00, 0x00 }
};

+static struct inno_hdmi_phy_config rk3128_hdmi_phy_configs[] = {
+ { 74250000, 0x3f, 0xaa },
+ { 165000000, 0x5f, 0xaa },
+ { ~0UL, 0x00, 0x00 }
+};
+
static int inno_hdmi_find_phy_config(struct inno_hdmi *hdmi,
unsigned long pixelclk)
{
@@ -182,13 +189,17 @@ static unsigned long inno_hdmi_tmds_rate(struct inno_hdmi *hdmi)
}

/*
- * When IP controller haven't configured to an accurate video
- * timing, then the TMDS clock source would be switched to
- * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
- * and reconfigure the DDC clock.
+ * When IP controller isn't configured to an accurate
+ * video timing and there is no reference clock available,
+ * then the TMDS clock source would be switched to PCLK_HDMI,
+ * so we need to init the TMDS rate to PCLK rate, and
+ * reconfigure the DDC clock.
*/

- return clk_get_rate(hdmi->pclk);
+ if (hdmi->refclk)
+ return clk_get_rate(hdmi->refclk);
+ else
+ return clk_get_rate(hdmi->pclk);
}

static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
@@ -912,6 +923,20 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
return ret;
}

+ hdmi->refclk = devm_clk_get_optional(hdmi->dev, "ref");
+ if (IS_ERR(hdmi->refclk)) {
+ DRM_DEV_ERROR(hdmi->dev, "Unable to get HDMI reference clock\n");
+ ret = PTR_ERR(hdmi->refclk);
+ goto err_disable_pclk;
+ }
+
+ ret = clk_prepare_enable(hdmi->refclk);
+ if (ret) {
+ DRM_DEV_ERROR(hdmi->dev,
+ "Cannot enable HDMI reference clock: %d\n", ret);
+ goto err_disable_pclk;
+ }
+
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
@@ -951,6 +976,8 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
err_put_adapter:
i2c_put_adapter(hdmi->ddc);
err_disable_clk:
+ clk_disable_unprepare(hdmi->refclk);
+err_disable_pclk:
clk_disable_unprepare(hdmi->pclk);
return ret;
}
@@ -964,6 +991,7 @@ static void inno_hdmi_unbind(struct device *dev, struct device *master,
hdmi->encoder.encoder.funcs->destroy(&hdmi->encoder.encoder);

i2c_put_adapter(hdmi->ddc);
+ clk_disable_unprepare(hdmi->refclk);
clk_disable_unprepare(hdmi->pclk);
}

@@ -987,10 +1015,18 @@ static const struct inno_hdmi_variant rk3036_inno_hdmi_variant = {
.default_phy_config = &rk3036_hdmi_phy_configs[1],
};

+static const struct inno_hdmi_variant rk3128_inno_hdmi_variant = {
+ .phy_configs = rk3128_hdmi_phy_configs,
+ .default_phy_config = &rk3128_hdmi_phy_configs[1],
+};
+
static const struct of_device_id inno_hdmi_dt_ids[] = {
{ .compatible = "rockchip,rk3036-inno-hdmi",
.data = &rk3036_inno_hdmi_variant,
},
+ { .compatible = "rockchip,rk3128-inno-hdmi",
+ .data = &rk3128_inno_hdmi_variant,
+ },
{},
};
MODULE_DEVICE_TABLE(of, inno_hdmi_dt_ids);
--
2.43.0


2023-12-16 16:34:18

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 24/27] drm/rockchip: inno_hdmi: Drop custom fill_modes hook

Now that we have proper pixelclock-based mode validation we can drop the
custom fill_modes hook.
CRTC size validation for the display controller has been added with
Commit 8e140cb60270 ("drm/rockchip: vop: limit maximum resolution to
hardware capabilities")

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- no changes

drivers/gpu/drm/rockchip/inno_hdmi.c | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 14473ca96e0f..d477d2872195 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -667,13 +667,6 @@ inno_hdmi_connector_mode_valid(struct drm_connector *connector,
return inno_hdmi_display_mode_valid(hdmi, mode);
}

-static int
-inno_hdmi_probe_single_connector_modes(struct drm_connector *connector,
- uint32_t maxX, uint32_t maxY)
-{
- return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
-}
-
static void inno_hdmi_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
@@ -701,7 +694,7 @@ inno_hdmi_connector_duplicate_state(struct drm_connector *connector)
}

static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
- .fill_modes = inno_hdmi_probe_single_connector_modes,
+ .fill_modes = drm_helper_probe_single_connector_modes,
.detect = inno_hdmi_connector_detect,
.destroy = inno_hdmi_connector_destroy,
.reset = drm_atomic_helper_connector_reset,
--
2.43.0


2023-12-16 16:34:23

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 23/27] drm/rockchip: inno_hdmi: Add basic mode validation

As per TRM this controller supports pixelclocks starting from 25 MHz. The
maximum supported pixelclocks are defined by the phy configurations we
have. Also it can't support modes that require doubled clocks. If the
variant has a phy reference clock we can additionally validate against VESA
DMT'srecommendations.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- rename inno_mode_valid -> inno_hdmi_display_mode_valid
- fixed max_tolerance calculation
- use abs_diff() instead of abs()
- call in inno_hdmi_display_mode_valid in atomic_check

drivers/gpu/drm/rockchip/inno_hdmi.c | 42 ++++++++++++++++++++++++++--
1 file changed, 40 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index 792e5fad09bf..14473ca96e0f 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -26,6 +26,8 @@

#include "inno_hdmi.h"

+#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U
+
struct inno_hdmi_phy_config {
unsigned long pixelclock;
u8 pre_emphasis;
@@ -548,6 +550,38 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
return 0;
}

+static enum drm_mode_status inno_hdmi_display_mode_valid(struct inno_hdmi *hdmi,
+ struct drm_display_mode *mode)
+{
+ unsigned long mpixelclk, max_tolerance;
+ long rounded_refclk;
+
+ /* No support for double-clock modes */
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK)
+ return MODE_BAD;
+
+ mpixelclk = mode->clock * 1000;
+
+ if (mpixelclk < INNO_HDMI_MIN_TMDS_CLOCK)
+ return MODE_CLOCK_LOW;
+
+ if (inno_hdmi_find_phy_config(hdmi, mpixelclk) < 0)
+ return MODE_CLOCK_HIGH;
+
+ if (hdmi->refclk) {
+ rounded_refclk = clk_round_rate(hdmi->refclk, mpixelclk);
+ if (rounded_refclk < 0)
+ return MODE_BAD;
+
+ /* Vesa DMT standard mentions +/- 0.5% max tolerance */
+ max_tolerance = mpixelclk / 200;
+ if (abs_diff((unsigned long)rounded_refclk, mpixelclk) > max_tolerance)
+ return MODE_NOCLOCK;
+ }
+
+ return MODE_OK;
+}
+
static void inno_hdmi_encoder_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state)
{
@@ -581,11 +615,13 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_connector_state *conn_state)
{
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+ struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);

s->output_mode = ROCKCHIP_OUT_MODE_P888;
s->output_type = DRM_MODE_CONNECTOR_HDMIA;

- return 0;
+ return inno_hdmi_display_mode_valid(hdmi,
+ &crtc_state->adjusted_mode) == MODE_OK ? 0 : -EINVAL;
}

static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
@@ -626,7 +662,9 @@ static enum drm_mode_status
inno_hdmi_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
- return MODE_OK;
+ struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
+
+ return inno_hdmi_display_mode_valid(hdmi, mode);
}

static int
--
2.43.0


2023-12-16 16:34:38

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 25/27] ARM: dts: rockchip: Add display subsystem for RK3128

Add vop and display-subsystem nodes to RK3128's device tree.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- no changes

arch/arm/boot/dts/rockchip/rk3128.dtsi | 27 ++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/arch/arm/boot/dts/rockchip/rk3128.dtsi b/arch/arm/boot/dts/rockchip/rk3128.dtsi
index e2264c40b924..1a3bc8b2bc6e 100644
--- a/arch/arm/boot/dts/rockchip/rk3128.dtsi
+++ b/arch/arm/boot/dts/rockchip/rk3128.dtsi
@@ -115,6 +115,12 @@ opp-1200000000 {
};
};

+ display_subsystem: display-subsystem {
+ compatible = "rockchip,display-subsystem";
+ ports = <&vop_out>;
+ status = "disabled";
+ };
+
gpu_opp_table: opp-table-1 {
compatible = "operating-points-v2";

@@ -246,6 +252,27 @@ power-domain@RK3128_PD_GPU {
};
};

+ vop: vop@1010e000 {
+ compatible = "rockchip,rk3126-vop";
+ reg = <0x1010e000 0x300>;
+ interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru ACLK_LCDC0>, <&cru DCLK_VOP>,
+ <&cru HCLK_LCDC0>;
+ clock-names = "aclk_vop", "dclk_vop",
+ "hclk_vop";
+ resets = <&cru SRST_VOP_A>, <&cru SRST_VOP_H>,
+ <&cru SRST_VOP_D>;
+ reset-names = "axi", "ahb",
+ "dclk";
+ power-domains = <&power RK3128_PD_VIO>;
+ status = "disabled";
+
+ vop_out: port {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+
qos_gpu: qos@1012d000 {
compatible = "rockchip,rk3128-qos", "syscon";
reg = <0x1012d000 0x20>;
--
2.43.0


2023-12-16 16:34:51

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 26/27] ARM: dts: rockchip: Add HDMI node for RK3128

RK3128 has Innosilicon based HDMI TX controller similar to the one found in
RK3036.
Add it and the respective port nodes to the SoC device tree.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- no changes

arch/arm/boot/dts/rockchip/rk3128.dtsi | 33 ++++++++++++++++++++++++++
1 file changed, 33 insertions(+)

diff --git a/arch/arm/boot/dts/rockchip/rk3128.dtsi b/arch/arm/boot/dts/rockchip/rk3128.dtsi
index 1a3bc8b2bc6e..fb98873fd94e 100644
--- a/arch/arm/boot/dts/rockchip/rk3128.dtsi
+++ b/arch/arm/boot/dts/rockchip/rk3128.dtsi
@@ -270,6 +270,11 @@ vop: vop@1010e000 {
vop_out: port {
#address-cells = <1>;
#size-cells = <0>;
+
+ vop_out_hdmi: endpoint@0 {
+ reg = <0>;
+ remote-endpoint = <&hdmi_in_vop>;
+ };
};
};

@@ -463,6 +468,34 @@ usb2phy_otg: otg-port {
};
};

+ hdmi: hdmi@20034000 {
+ compatible = "rockchip,rk3128-inno-hdmi";
+ reg = <0x20034000 0x4000>;
+ interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&cru PCLK_HDMI>, <&cru DCLK_VOP>;
+ clock-names = "pclk", "ref";
+ pinctrl-names = "default";
+ pinctrl-0 = <&hdmii2c_xfer &hdmi_hpd &hdmi_cec>;
+ power-domains = <&power RK3128_PD_VIO>;
+ status = "disabled";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hdmi_in: port@0 {
+ reg = <0>;
+ hdmi_in_vop: endpoint {
+ remote-endpoint = <&vop_out_hdmi>;
+ };
+ };
+
+ hdmi_out: port@1 {
+ reg = <1>;
+ };
+ };
+ };
+
timer0: timer@20044000 {
compatible = "rockchip,rk3128-timer", "rockchip,rk3288-timer";
reg = <0x20044000 0x20>;
--
2.43.0


2023-12-16 16:35:23

by Alex Bee

[permalink] [raw]
Subject: [PATCH v2 27/27] ARM: dts: rockchip: Enable HDMI output for XPI-3128

Add an hdmi-connector node and enable the hdmi, display-subsystem and vop
nodes.

Signed-off-by: Alex Bee <[email protected]>
---
changes in v2:
- no changes

.../arm/boot/dts/rockchip/rk3128-xpi-3128.dts | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)

diff --git a/arch/arm/boot/dts/rockchip/rk3128-xpi-3128.dts b/arch/arm/boot/dts/rockchip/rk3128-xpi-3128.dts
index 03a97881519a..21c1678f4e91 100644
--- a/arch/arm/boot/dts/rockchip/rk3128-xpi-3128.dts
+++ b/arch/arm/boot/dts/rockchip/rk3128-xpi-3128.dts
@@ -47,6 +47,17 @@ dc_5v: dc-5v-regulator {
regulator-boot-on;
};

+ hdmi-connnector {
+ compatible = "hdmi-connector";
+ type = "a";
+
+ port {
+ hdmi_connector_in: endpoint {
+ remote-endpoint = <&hdmi_connector_out>;
+ };
+ };
+ };
+
/*
* This is a vbus-supply, which also supplies the GL852G usb hub,
* thus has to be always-on
@@ -239,6 +250,10 @@ &cpu0 {
cpu-supply = <&vdd_arm>;
};

+&display_subsystem {
+ status = "okay";
+};
+
&emmc {
bus-width = <8>;
vmmc-supply = <&vcc_io>;
@@ -328,6 +343,16 @@ &gpu {
status = "okay";
};

+&hdmi {
+ status = "okay";
+};
+
+&hdmi_out {
+ hdmi_connector_out: endpoint {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+};
+
&mdio {
phy0: ethernet-phy@1 {
compatible = "ethernet-phy-ieee802.3-c22";
@@ -423,3 +448,7 @@ &usb2phy_host {
&usb2phy_otg {
status = "okay";
};
+
+&vop {
+ status = "okay";
+};
--
2.43.0


2023-12-18 08:38:46

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 04/27] drm/rockchip: inno_hdmi: Remove useless mode_fixup

Hi,

On Sat, Dec 16, 2023 at 05:26:15PM +0100, Alex Bee wrote:
> From: Maxime Ripard <[email protected]>
>
> The mode_fixup implementation doesn't do anything, so we can simply
> remove it.
>
> Signed-off-by: Maxime Ripard <[email protected]>
> Tested-by: Alex Bee <[email protected]>

Generally speaking, when you're sending patches you should always have
your SoB. That is true for the commit you authored, but also for the
commits you applied and are sending on the author's behalf.

This applies to all the other patches

Maxime


Attachments:
(No filename) (557.00 B)
signature.asc (235.00 B)
Download all attachments

2023-12-18 09:00:38

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 16/27] drm/rockchip: inno_hdmi: Drop irq struct member

On Sat, 16 Dec 2023 17:26:27 +0100, Alex Bee wrote:
> The struct member irq isn't used anywhere. Drop it.
>
> Signed-off-by: Alex Bee <[email protected]>

Reviewed-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-12-18 09:00:56

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 17/27] drm/rockchip: inno_hdmi: Remove useless include

On Sat, 16 Dec 2023 17:26:28 +0100, Alex Bee wrote:
> The inclusion syscon.h isn't used anywhere. Remove it.
>
> Signed-off-by: Alex Bee <[email protected]>

Reviewed-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-12-18 09:02:37

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 18/27] drm/rockchip: inno_hdmi: Subclass connector state

Hi,

Thanks for working on that, it's much better now.

On Sat, Dec 16, 2023 at 05:26:29PM +0100, Alex Bee wrote:
> +static struct drm_connector_state *
> +inno_hdmi_connector_duplicate_state(struct drm_connector *connector)
> +{
> + struct inno_hdmi_connector_state *inno_conn_state;
> +
> + if (WARN_ON(!connector->state))
> + return NULL;
> +
> + inno_conn_state = kmemdup(to_inno_hdmi_conn_state(connector->state),
> + sizeof(*inno_conn_state), GFP_KERNEL);
> +
> + if (!inno_conn_state)
> + return NULL;
> +
> + __drm_atomic_helper_connector_duplicate_state(connector,
> + &inno_conn_state->base);
> +
> + return &inno_conn_state->base;
> +}
> +
> static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
> .fill_modes = inno_hdmi_probe_single_connector_modes,
> .detect = inno_hdmi_connector_detect,
> .destroy = inno_hdmi_connector_destroy,
> .reset = drm_atomic_helper_connector_reset,
> - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> + .atomic_duplicate_state = inno_hdmi_connector_duplicate_state,
> .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> };

You also need a custom reset and atomic_destroy_state implementations

Maxime


Attachments:
(No filename) (1.24 kB)
signature.asc (235.00 B)
Download all attachments

2023-12-18 09:05:18

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 20/27] drm/rockchip: inno_hdmi: Correctly setup HDMI quantization range

On Sat, Dec 16, 2023 at 05:26:31PM +0100, Alex Bee wrote:
> @@ -431,6 +452,9 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
> else
> inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
>
> + inno_conn_state->rgb_limited_range =
> + drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
> +
> /* Mute video and audio output */
> hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
> v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));

This needs to be done at atomic_check time: the expectation is that by
the time you commit the state, everything is prepared for it.

Maxime


Attachments:
(No filename) (629.00 B)
signature.asc (235.00 B)
Download all attachments

2023-12-18 09:06:37

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 23/27] drm/rockchip: inno_hdmi: Add basic mode validation

On Sat, 16 Dec 2023 17:26:34 +0100, Alex Bee wrote:
> As per TRM this controller supports pixelclocks starting from 25 MHz. The
> maximum supported pixelclocks are defined by the phy configurations we
> have. Also it can't support modes that require doubled clocks. If the
> variant has a phy reference clock we can additionally validate against VESA
> DMT'srecommendations.
>
> [ ... ]

Reviewed-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-12-18 09:06:59

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 24/27] drm/rockchip: inno_hdmi: Drop custom fill_modes hook

On Sat, 16 Dec 2023 17:26:35 +0100, Alex Bee wrote:
> Now that we have proper pixelclock-based mode validation we can drop the
> custom fill_modes hook.
> CRTC size validation for the display controller has been added with
> Commit 8e140cb60270 ("drm/rockchip: vop: limit maximum resolution to
> hardware capabilities")
>
> [ ... ]

Reviewed-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-12-18 10:06:51

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 19/27] drm/rockchip: inno_hdmi: Move tmds rate to connector state subclass

Hi,

On Sat, Dec 16, 2023 at 05:26:30PM +0100, Alex Bee wrote:
> Similar to the othter members of inno_hdmi_connector_state the tmds_rate is
> not a property of the device, but of the connector state. Move it to
> inno_hdmi_connector_state and make it a long to comply with the clock
> framework. To get arround the issue of not having the connector state when
> inno_hdmi_i2c_init is called in the bind path, getting the tmds rate is
> wrapped in function which returns the fallback rate if the connector
> doesn't have a state yet.
>
> Signed-off-by: Alex Bee <[email protected]>
> ---
> changes in v2:
> - new patch
>
> drivers/gpu/drm/rockchip/inno_hdmi.c | 36 +++++++++++++++++++---------
> 1 file changed, 25 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
> index f9bfae1e97a2..6799d24501b8 100644
> --- a/drivers/gpu/drm/rockchip/inno_hdmi.c
> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
> @@ -47,14 +47,13 @@ struct inno_hdmi {
>
> struct inno_hdmi_i2c *i2c;
> struct i2c_adapter *ddc;
> -
> - unsigned int tmds_rate;
> };
>
> struct inno_hdmi_connector_state {
> struct drm_connector_state base;
> unsigned int enc_out_format;
> unsigned int colorimetry;
> + unsigned long tmds_rate;
> };
>
> static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
> @@ -133,11 +132,33 @@ static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
> hdmi_writeb(hdmi, offset, temp);
> }
>
> +static unsigned long inno_hdmi_tmds_rate(struct inno_hdmi *hdmi)
> +{
> + struct drm_connector *connector = &hdmi->connector;
> + struct drm_connector_state *conn_state = connector->state;
> + struct inno_hdmi_connector_state *inno_conn_state;
> +
> + if (conn_state) {
> + inno_conn_state = to_inno_hdmi_conn_state(conn_state);
> + return inno_conn_state->tmds_rate;
> + }
> +
> + /*
> + * When IP controller haven't configured to an accurate video
> + * timing, then the TMDS clock source would be switched to
> + * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
> + * and reconfigure the DDC clock.
> + */
> +
> + return clk_get_rate(hdmi->pclk);
> +}
> +
> static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
> {
> int ddc_bus_freq;
> + unsigned long tmds_rate = inno_hdmi_tmds_rate(hdmi);
>
> - ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
> + ddc_bus_freq = (tmds_rate >> 2) / HDMI_SCL_RATE;
>
> hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
> hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
> @@ -431,7 +452,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
> * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
> * clock rate, and reconfigure the DDC clock.
> */
> - hdmi->tmds_rate = mode->clock * 1000;
> + inno_conn_state->tmds_rate = mode->clock * 1000;
> inno_hdmi_i2c_init(hdmi);
>
> /* Unmute video and audio output */
> @@ -823,13 +844,6 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
> goto err_disable_clk;
> }
>
> - /*
> - * When IP controller haven't configured to an accurate video
> - * timing, then the TMDS clock source would be switched to
> - * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
> - * and reconfigure the DDC clock.
> - */
> - hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
> inno_hdmi_i2c_init(hdmi);

I still think my patch is better there.

There's two places that use the inno_hdmi.tmds_rate field: the two
callers of inno_hdmi_i2c_init(). One is at bind time and needs to
initialise it with a sane default since we don't have a mode set yet,
the other is to update the internal clock rate while we have a mode set.

Since there's a single "modeset" user, there's no need to store it in
the state structure at all: it can be a local variable.

And in the bind function, you're not going to use the state structure
either since there's no state, and it's just a default that has no
relation to the modeset code at all.

Your function on the other end tries to reconcile and handle the two.
But there's no reason to, it just makes the code harder to follow. Just
pass the parent rate you want to init with as an argument and it's easy
to read and maintain.

Maxime


Attachments:
(No filename) (4.29 kB)
signature.asc (235.00 B)
Download all attachments

2023-12-18 12:33:09

by Alex Bee

[permalink] [raw]
Subject: Re: [PATCH v2 18/27] drm/rockchip: inno_hdmi: Subclass connector state

Hi,

Am 18.12.23 um 10:02 schrieb Maxime Ripard:
> Hi,
>
> Thanks for working on that, it's much better now.
>
> On Sat, Dec 16, 2023 at 05:26:29PM +0100, Alex Bee wrote:
>> +static struct drm_connector_state *
>> +inno_hdmi_connector_duplicate_state(struct drm_connector *connector)
>> +{
>> + struct inno_hdmi_connector_state *inno_conn_state;
>> +
>> + if (WARN_ON(!connector->state))
>> + return NULL;
>> +
>> + inno_conn_state = kmemdup(to_inno_hdmi_conn_state(connector->state),
>> + sizeof(*inno_conn_state), GFP_KERNEL);
>> +
>> + if (!inno_conn_state)
>> + return NULL;
>> +
>> + __drm_atomic_helper_connector_duplicate_state(connector,
>> + &inno_conn_state->base);
>> +
>> + return &inno_conn_state->base;
>> +}
>> +
>> static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
>> .fill_modes = inno_hdmi_probe_single_connector_modes,
>> .detect = inno_hdmi_connector_detect,
>> .destroy = inno_hdmi_connector_destroy,
>> .reset = drm_atomic_helper_connector_reset,
>> - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> + .atomic_duplicate_state = inno_hdmi_connector_duplicate_state,
>> .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> };
> You also need a custom reset and atomic_destroy_state implementations
Yea sure - thanks for the heads up.
I had this in multiple commits locally and thought I squashed them into
this one. Well, I obviously didn't :)

Alex
> Maxime

2023-12-18 12:37:59

by Alex Bee

[permalink] [raw]
Subject: Re: [PATCH v2 20/27] drm/rockchip: inno_hdmi: Correctly setup HDMI quantization range


Am 18.12.23 um 10:05 schrieb Maxime Ripard:
> On Sat, Dec 16, 2023 at 05:26:31PM +0100, Alex Bee wrote:
>> @@ -431,6 +452,9 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
>> else
>> inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
>>
>> + inno_conn_state->rgb_limited_range =
>> + drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
>> +
>> /* Mute video and audio output */
>> hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
>> v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
> This needs to be done at atomic_check time: the expectation is that by
> the time you commit the state, everything is prepared for it.
OK. I guess that also applies to the other members of
inno_hdmi_connector_state (former hdmi_data) and was wrong all the time.

Alex
> Maxime

2023-12-18 13:14:11

by Alex Bee

[permalink] [raw]
Subject: Re: [PATCH v2 19/27] drm/rockchip: inno_hdmi: Move tmds rate to connector state subclass


Am 18.12.23 um 11:06 schrieb Maxime Ripard:
> Hi,
>
> On Sat, Dec 16, 2023 at 05:26:30PM +0100, Alex Bee wrote:
>> Similar to the othter members of inno_hdmi_connector_state the tmds_rate is
>> not a property of the device, but of the connector state. Move it to
>> inno_hdmi_connector_state and make it a long to comply with the clock
>> framework. To get arround the issue of not having the connector state when
>> inno_hdmi_i2c_init is called in the bind path, getting the tmds rate is
>> wrapped in function which returns the fallback rate if the connector
>> doesn't have a state yet.
>>
>> Signed-off-by: Alex Bee <[email protected]>
>> ---
>> changes in v2:
>> - new patch
>>
>> drivers/gpu/drm/rockchip/inno_hdmi.c | 36 +++++++++++++++++++---------
>> 1 file changed, 25 insertions(+), 11 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
>> index f9bfae1e97a2..6799d24501b8 100644
>> --- a/drivers/gpu/drm/rockchip/inno_hdmi.c
>> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
>> @@ -47,14 +47,13 @@ struct inno_hdmi {
>>
>> struct inno_hdmi_i2c *i2c;
>> struct i2c_adapter *ddc;
>> -
>> - unsigned int tmds_rate;
>> };
>>
>> struct inno_hdmi_connector_state {
>> struct drm_connector_state base;
>> unsigned int enc_out_format;
>> unsigned int colorimetry;
>> + unsigned long tmds_rate;
>> };
>>
>> static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
>> @@ -133,11 +132,33 @@ static inline void hdmi_modb(struct inno_hdmi *hdmi, u16 offset,
>> hdmi_writeb(hdmi, offset, temp);
>> }
>>
>> +static unsigned long inno_hdmi_tmds_rate(struct inno_hdmi *hdmi)
>> +{
>> + struct drm_connector *connector = &hdmi->connector;
>> + struct drm_connector_state *conn_state = connector->state;
>> + struct inno_hdmi_connector_state *inno_conn_state;
>> +
>> + if (conn_state) {
>> + inno_conn_state = to_inno_hdmi_conn_state(conn_state);
>> + return inno_conn_state->tmds_rate;
>> + }
>> +
>> + /*
>> + * When IP controller haven't configured to an accurate video
>> + * timing, then the TMDS clock source would be switched to
>> + * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
>> + * and reconfigure the DDC clock.
>> + */
>> +
>> + return clk_get_rate(hdmi->pclk);
>> +}
>> +
>> static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
>> {
>> int ddc_bus_freq;
>> + unsigned long tmds_rate = inno_hdmi_tmds_rate(hdmi);
>>
>> - ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
>> + ddc_bus_freq = (tmds_rate >> 2) / HDMI_SCL_RATE;
>>
>> hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>> hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>> @@ -431,7 +452,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
>> * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
>> * clock rate, and reconfigure the DDC clock.
>> */
>> - hdmi->tmds_rate = mode->clock * 1000;
>> + inno_conn_state->tmds_rate = mode->clock * 1000;
>> inno_hdmi_i2c_init(hdmi);
>>
>> /* Unmute video and audio output */
>> @@ -823,13 +844,6 @@ static int inno_hdmi_bind(struct device *dev, struct device *master,
>> goto err_disable_clk;
>> }
>>
>> - /*
>> - * When IP controller haven't configured to an accurate video
>> - * timing, then the TMDS clock source would be switched to
>> - * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
>> - * and reconfigure the DDC clock.
>> - */
>> - hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
>> inno_hdmi_i2c_init(hdmi);
> I still think my patch is better there.
>
> There's two places that use the inno_hdmi.tmds_rate field: the two
> callers of inno_hdmi_i2c_init(). One is at bind time and needs to
> initialise it with a sane default since we don't have a mode set yet,
> the other is to update the internal clock rate while we have a mode set.
That’s, unfortunately, not fully true: inno_hdmi_set_pwr_mode not only
called at mode_set-time, but also in inno_hdmi_reset which is called in the
bind path (where we do not have a mode). That’s the point why I thought
extracting this in function makes sense. Otherwise I would have to pass the
tmds_rate to inno_hdmi_set_pwr_mode (also for the LOWER_PWR-case where I
don't need it) or do that whole fallback-if-no-mode thing in
inno_hdmi_set_pwr_mode directly. Neither would make the code easier to
follow. Being able to use it in inno_hdmi_i2c_init also is a nice gimmick.
I agree, having it in the custom connector state is not strictly required,
but I'd really like to keep the wrapping function.

Alex

> Since there's a single "modeset" user, there's no need to store it in
> the state structure at all: it can be a local variable.
>
> And in the bind function, you're not going to use the state structure
> either since there's no state, and it's just a default that has no
> relation to the modeset code at all.
>
> Your function on the other end tries to reconcile and handle the two.
> But there's no reason to, it just makes the code harder to follow. Just
> pass the parent rate you want to init with as an argument and it's easy
> to read and maintain.
>
> Maxime

2023-12-18 13:22:27

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 20/27] drm/rockchip: inno_hdmi: Correctly setup HDMI quantization range

On Mon, Dec 18, 2023 at 01:37:47PM +0100, Alex Bee wrote:
>
> Am 18.12.23 um 10:05 schrieb Maxime Ripard:
> > On Sat, Dec 16, 2023 at 05:26:31PM +0100, Alex Bee wrote:
> > > @@ -431,6 +452,9 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
> > > else
> > > inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
> > > + inno_conn_state->rgb_limited_range =
> > > + drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
> > > +
> > > /* Mute video and audio output */
> > > hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
> > > v_AUDIO_MUTE(1) | v_VIDEO_MUTE(1));
> > This needs to be done at atomic_check time: the expectation is that by
> > the time you commit the state, everything is prepared for it.
> OK. I guess that also applies to the other members of
> inno_hdmi_connector_state (former hdmi_data) and was wrong all the time.

Yeah, this will apply to all the members of inno_hdmi_connector_state indeed

Maxime


Attachments:
(No filename) (998.00 B)
signature.asc (235.00 B)
Download all attachments

2023-12-22 17:52:39

by Alex Bee

[permalink] [raw]
Subject: Re: [PATCH v2 19/27] drm/rockchip: inno_hdmi: Move tmds rate to connector state subclass

Hi Maxime,

Am 18.12.23 um 13:49 schrieb Alex Bee:
>
> Am 18.12.23 um 11:06 schrieb Maxime Ripard:
>> Hi,
>>
>> On Sat, Dec 16, 2023 at 05:26:30PM +0100, Alex Bee wrote:
>>> Similar to the othter members of inno_hdmi_connector_state the
>>> tmds_rate is
>>> not a property of the device, but of the connector state. Move it to
>>> inno_hdmi_connector_state and make it a long to comply with the clock
>>> framework. To get arround the issue of not having the connector
>>> state when
>>> inno_hdmi_i2c_init is called in the bind path, getting the tmds rate is
>>> wrapped in function which returns the fallback rate if the connector
>>> doesn't have a state yet.
>>>
>>> Signed-off-by: Alex Bee <[email protected]>
>>> ---
>>> changes in v2:
>>>   - new patch
>>>
>>>   drivers/gpu/drm/rockchip/inno_hdmi.c | 36
>>> +++++++++++++++++++---------
>>>   1 file changed, 25 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c
>>> b/drivers/gpu/drm/rockchip/inno_hdmi.c
>>> index f9bfae1e97a2..6799d24501b8 100644
>>> --- a/drivers/gpu/drm/rockchip/inno_hdmi.c
>>> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
>>> @@ -47,14 +47,13 @@ struct inno_hdmi {
>>>         struct inno_hdmi_i2c *i2c;
>>>       struct i2c_adapter *ddc;
>>> -
>>> -    unsigned int tmds_rate;
>>>   };
>>>     struct inno_hdmi_connector_state {
>>>       struct drm_connector_state    base;
>>>       unsigned int            enc_out_format;
>>>       unsigned int            colorimetry;
>>> +    unsigned long            tmds_rate;
>>>   };
>>>     static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder
>>> *encoder)
>>> @@ -133,11 +132,33 @@ static inline void hdmi_modb(struct inno_hdmi
>>> *hdmi, u16 offset,
>>>       hdmi_writeb(hdmi, offset, temp);
>>>   }
>>>   +static unsigned long inno_hdmi_tmds_rate(struct inno_hdmi *hdmi)
>>> +{
>>> +    struct drm_connector *connector = &hdmi->connector;
>>> +    struct drm_connector_state *conn_state = connector->state;
>>> +    struct inno_hdmi_connector_state *inno_conn_state;
>>> +
>>> +    if (conn_state) {
>>> +        inno_conn_state = to_inno_hdmi_conn_state(conn_state);
>>> +        return inno_conn_state->tmds_rate;
>>> +    }
>>> +
>>> +    /*
>>> +     * When IP controller haven't configured to an accurate video
>>> +     * timing, then the TMDS clock source would be switched to
>>> +     * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
>>> +     * and reconfigure the DDC clock.
>>> +     */
>>> +
>>> +    return clk_get_rate(hdmi->pclk);
>>> +}
>>> +
>>>   static void inno_hdmi_i2c_init(struct inno_hdmi *hdmi)
>>>   {
>>>       int ddc_bus_freq;
>>> +    unsigned long tmds_rate = inno_hdmi_tmds_rate(hdmi);
>>>   -    ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
>>> +    ddc_bus_freq = (tmds_rate >> 2) / HDMI_SCL_RATE;
>>>         hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
>>>       hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
>>> @@ -431,7 +452,7 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
>>>        * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
>>>        * clock rate, and reconfigure the DDC clock.
>>>        */
>>> -    hdmi->tmds_rate = mode->clock * 1000;
>>> +    inno_conn_state->tmds_rate = mode->clock * 1000;
>>>       inno_hdmi_i2c_init(hdmi);
>>>         /* Unmute video and audio output */
>>> @@ -823,13 +844,6 @@ static int inno_hdmi_bind(struct device *dev,
>>> struct device *master,
>>>           goto err_disable_clk;
>>>       }
>>>   -    /*
>>> -     * When IP controller haven't configured to an accurate video
>>> -     * timing, then the TMDS clock source would be switched to
>>> -     * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
>>> -     * and reconfigure the DDC clock.
>>> -     */
>>> -    hdmi->tmds_rate = clk_get_rate(hdmi->pclk);
>>>       inno_hdmi_i2c_init(hdmi);
>> I still think my patch is better there.
>>
I found a way now and added your patch which removes the tmds_rate: I
noticed powering up the phy initially isn't necessary at all (and makes no
sense). I refactored inno_hdmi_set_pwr_mode in two functions (in a similar
way you did for the infoframe), so that the tmds rate (pixelclock) is only
needed as parameter when a mode is set.

Alex

>> There's two places that use the inno_hdmi.tmds_rate field: the two
>> callers of inno_hdmi_i2c_init(). One is at bind time and needs to
>> initialise it with a sane default since we don't have a mode set yet,
>> the other is to update the internal clock rate while we have a mode set.
> That’s, unfortunately, not fully true: inno_hdmi_set_pwr_mode not only
> called at mode_set-time, but also in inno_hdmi_reset which is called
> in the
> bind path (where we do not have a mode). That’s the point why I thought
> extracting this in function makes sense. Otherwise I would have to
> pass the
> tmds_rate to inno_hdmi_set_pwr_mode (also for the LOWER_PWR-case where I
> don't need it) or do that whole fallback-if-no-mode thing in
> inno_hdmi_set_pwr_mode directly. Neither would make the code easier to
> follow. Being able to use it in inno_hdmi_i2c_init also is a nice
> gimmick.
> I agree, having it in the custom connector state is not strictly
> required,
> but I'd really like to keep the wrapping function.
>
> Alex
>
>> Since there's a single "modeset" user, there's no need to store it in
>> the state structure at all: it can be a local variable.
>>
>> And in the bind function, you're not going to use the state structure
>> either since there's no state, and it's just a default that has no
>> relation to the modeset code at all.
>>
>> Your function on the other end tries to reconcile and handle the two.
>> But there's no reason to, it just makes the code harder to follow. Just
>> pass the parent rate you want to init with as an argument and it's easy
>> to read and maintain.
>>
>> Maxime