Commit 65548c8f authored by Maxime Ripard's avatar Maxime Ripard

drm/rockchip: inno_hdmi: Switch to HDMI connector

The new HDMI connector infrastructure allows to remove some boilerplate,
especially to generate infoframes. Let's switch to it.
Reviewed-by: default avatarHeiko Stuebner <heiko@sntech.de>
Acked-by: default avatarHeiko Stuebner <heiko@sntech.de>
Acked-by: default avatarAndy Yan <andyshrk@163.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240527-kms-hdmi-connector-state-v15-28-c5af16c3aae2@kernel.orgSigned-off-by: default avatarMaxime Ripard <mripard@kernel.org>
parent b3bf1955
...@@ -74,6 +74,9 @@ config ROCKCHIP_DW_MIPI_DSI ...@@ -74,6 +74,9 @@ config ROCKCHIP_DW_MIPI_DSI
config ROCKCHIP_INNO_HDMI config ROCKCHIP_INNO_HDMI
bool "Rockchip specific extensions for Innosilicon HDMI" bool "Rockchip specific extensions for Innosilicon HDMI"
select DRM_DISPLAY_HDMI_HELPER
select DRM_DISPLAY_HDMI_STATE_HELPER
select DRM_DISPLAY_HELPER
help help
This selects support for Rockchip SoC specific extensions This selects support for Rockchip SoC specific extensions
for the Innosilicon HDMI driver. If you want to enable for the Innosilicon HDMI driver. If you want to enable
......
...@@ -22,6 +22,9 @@ ...@@ -22,6 +22,9 @@
#include <drm/drm_probe_helper.h> #include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h> #include <drm/drm_simple_kms_helper.h>
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
#include "rockchip_drm_drv.h" #include "rockchip_drm_drv.h"
#include "inno_hdmi.h" #include "inno_hdmi.h"
...@@ -67,9 +70,7 @@ struct inno_hdmi { ...@@ -67,9 +70,7 @@ struct inno_hdmi {
struct inno_hdmi_connector_state { struct inno_hdmi_connector_state {
struct drm_connector_state base; struct drm_connector_state base;
unsigned int enc_out_format;
unsigned int colorimetry; unsigned int colorimetry;
bool rgb_limited_range;
}; };
static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder) static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
...@@ -257,26 +258,29 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi) ...@@ -257,26 +258,29 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
inno_hdmi_standby(hdmi); inno_hdmi_standby(hdmi);
} }
static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi, static int inno_hdmi_disable_frame(struct drm_connector *connector,
enum hdmi_infoframe_type type) enum hdmi_infoframe_type type)
{ {
struct drm_connector *connector = &hdmi->connector; struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
if (type != HDMI_INFOFRAME_TYPE_AVI) { if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev, drm_err(connector->dev,
"Unsupported infoframe type: %u\n", type); "Unsupported infoframe type: %u\n", type);
return; return 0;
} }
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI); hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);
return 0;
} }
static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, static int inno_hdmi_upload_frame(struct drm_connector *connector,
union hdmi_infoframe *frame, enum hdmi_infoframe_type type) enum hdmi_infoframe_type type,
const u8 *buffer, size_t len)
{ {
struct drm_connector *connector = &hdmi->connector; struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector);
u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE]; u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
ssize_t rc, i; ssize_t i;
if (type != HDMI_INFOFRAME_TYPE_AVI) { if (type != HDMI_INFOFRAME_TYPE_AVI) {
drm_err(connector->dev, drm_err(connector->dev,
...@@ -284,59 +288,19 @@ static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi, ...@@ -284,59 +288,19 @@ static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
return 0; return 0;
} }
inno_hdmi_disable_frame(hdmi, type); inno_hdmi_disable_frame(connector, type);
rc = hdmi_infoframe_pack(frame, packed_frame, for (i = 0; i < len; i++)
sizeof(packed_frame));
if (rc < 0)
return rc;
for (i = 0; i < rc; i++)
hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i,
packed_frame[i]); packed_frame[i]);
return 0; return 0;
} }
static int inno_hdmi_config_video_avi(struct inno_hdmi *hdmi, static const struct drm_connector_hdmi_funcs inno_hdmi_hdmi_connector_funcs = {
struct drm_display_mode *mode) .clear_infoframe = inno_hdmi_disable_frame,
{ .write_infoframe = inno_hdmi_upload_frame,
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;
rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
&hdmi->connector,
mode);
if (rc) {
inno_hdmi_disable_frame(hdmi, HDMI_INFOFRAME_TYPE_AVI);
return rc;
}
if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444)
frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
else if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV422)
frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
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);
}
static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi) static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
{ {
...@@ -361,8 +325,8 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi) ...@@ -361,8 +325,8 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
v_VIDEO_INPUT_CSP(0); v_VIDEO_INPUT_CSP(0);
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value); hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);
if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) { if (conn_state->hdmi.output_format == HDMI_COLORSPACE_RGB) {
if (inno_conn_state->rgb_limited_range) { if (conn_state->hdmi.is_limited_range) {
csc_mode = CSC_RGB_0_255_TO_RGB_16_235_8BIT; csc_mode = CSC_RGB_0_255_TO_RGB_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE; auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE; c0_c2_change = C0_C2_CHANGE_DISABLE;
...@@ -380,14 +344,14 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi) ...@@ -380,14 +344,14 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
} }
} else { } else {
if (inno_conn_state->colorimetry == HDMI_COLORIMETRY_ITU_601) { if (inno_conn_state->colorimetry == HDMI_COLORIMETRY_ITU_601) {
if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) { if (conn_state->hdmi.output_format == HDMI_COLORSPACE_YUV444) {
csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT; csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE; auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE; c0_c2_change = C0_C2_CHANGE_DISABLE;
csc_enable = v_CSC_ENABLE; csc_enable = v_CSC_ENABLE;
} }
} else { } else {
if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_YUV444) { if (conn_state->hdmi.output_format == HDMI_COLORSPACE_YUV444) {
csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT; csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
auto_csc = AUTO_CSC_DISABLE; auto_csc = AUTO_CSC_DISABLE;
c0_c2_change = C0_C2_CHANGE_DISABLE; c0_c2_change = C0_C2_CHANGE_DISABLE;
...@@ -462,10 +426,20 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi, ...@@ -462,10 +426,20 @@ static int inno_hdmi_config_video_timing(struct inno_hdmi *hdmi,
} }
static int inno_hdmi_setup(struct inno_hdmi *hdmi, static int inno_hdmi_setup(struct inno_hdmi *hdmi,
struct drm_display_mode *mode) struct drm_atomic_state *state)
{ {
struct drm_display_info *display = &hdmi->connector.display_info; struct drm_connector *connector = &hdmi->connector;
unsigned long mpixelclock = mode->clock * 1000; struct drm_display_info *display = &connector->display_info;
struct drm_connector_state *new_conn_state;
struct drm_crtc_state *new_crtc_state;
new_conn_state = drm_atomic_get_new_connector_state(state, connector);
if (WARN_ON(!new_conn_state))
return -EINVAL;
new_crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
if (WARN_ON(!new_crtc_state))
return -EINVAL;
/* Mute video and audio output */ /* Mute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK, hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
...@@ -475,12 +449,11 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi, ...@@ -475,12 +449,11 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
hdmi_writeb(hdmi, HDMI_HDCP_CTRL, hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
v_HDMI_DVI(display->is_hdmi)); v_HDMI_DVI(display->is_hdmi));
inno_hdmi_config_video_timing(hdmi, mode); inno_hdmi_config_video_timing(hdmi, &new_crtc_state->adjusted_mode);
inno_hdmi_config_video_csc(hdmi); inno_hdmi_config_video_csc(hdmi);
if (display->is_hdmi) drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
inno_hdmi_config_video_avi(hdmi, mode);
/* /*
* When IP controller have configured to an accurate video * When IP controller have configured to an accurate video
...@@ -488,13 +461,13 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi, ...@@ -488,13 +461,13 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
* DCLK_LCDC, so we need to init the TMDS rate to mode pixel * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
* clock rate, and reconfigure the DDC clock. * clock rate, and reconfigure the DDC clock.
*/ */
inno_hdmi_i2c_init(hdmi, mpixelclock); inno_hdmi_i2c_init(hdmi, new_conn_state->hdmi.tmds_char_rate);
/* Unmute video and audio output */ /* Unmute video and audio output */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK, hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0)); v_AUDIO_MUTE(0) | v_VIDEO_MUTE(0));
inno_hdmi_power_up(hdmi, mpixelclock); inno_hdmi_power_up(hdmi, new_conn_state->hdmi.tmds_char_rate);
return 0; return 0;
} }
...@@ -535,18 +508,8 @@ static void inno_hdmi_encoder_enable(struct drm_encoder *encoder, ...@@ -535,18 +508,8 @@ static void inno_hdmi_encoder_enable(struct drm_encoder *encoder,
struct drm_atomic_state *state) struct drm_atomic_state *state)
{ {
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder); 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); inno_hdmi_setup(hdmi, state);
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);
} }
static void inno_hdmi_encoder_disable(struct drm_encoder *encoder, static void inno_hdmi_encoder_disable(struct drm_encoder *encoder,
...@@ -563,7 +526,6 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder, ...@@ -563,7 +526,6 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_connector_state *conn_state) struct drm_connector_state *conn_state)
{ {
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder);
struct drm_display_mode *mode = &crtc_state->adjusted_mode; struct drm_display_mode *mode = &crtc_state->adjusted_mode;
u8 vic = drm_match_cea_mode(mode); u8 vic = drm_match_cea_mode(mode);
struct inno_hdmi_connector_state *inno_conn_state = struct inno_hdmi_connector_state *inno_conn_state =
...@@ -580,12 +542,7 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder, ...@@ -580,12 +542,7 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
else else
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709; inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
inno_conn_state->enc_out_format = HDMI_COLORSPACE_RGB; return 0;
inno_conn_state->rgb_limited_range =
drm_default_rgb_quant_range(mode) == HDMI_QUANTIZATION_RANGE_LIMITED;
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 = { static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
...@@ -629,12 +586,6 @@ inno_hdmi_connector_mode_valid(struct drm_connector *connector, ...@@ -629,12 +586,6 @@ inno_hdmi_connector_mode_valid(struct drm_connector *connector,
return inno_hdmi_display_mode_valid(hdmi, mode); return inno_hdmi_display_mode_valid(hdmi, mode);
} }
static void inno_hdmi_connector_destroy(struct drm_connector *connector)
{
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
}
static void static void
inno_hdmi_connector_destroy_state(struct drm_connector *connector, inno_hdmi_connector_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state) struct drm_connector_state *state)
...@@ -660,10 +611,9 @@ static void inno_hdmi_connector_reset(struct drm_connector *connector) ...@@ -660,10 +611,9 @@ static void inno_hdmi_connector_reset(struct drm_connector *connector)
return; return;
__drm_atomic_helper_connector_reset(connector, &inno_conn_state->base); __drm_atomic_helper_connector_reset(connector, &inno_conn_state->base);
__drm_atomic_helper_connector_hdmi_reset(connector, connector->state);
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709; inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;
inno_conn_state->enc_out_format = HDMI_COLORSPACE_RGB;
inno_conn_state->rgb_limited_range = false;
} }
static struct drm_connector_state * static struct drm_connector_state *
...@@ -689,13 +639,13 @@ inno_hdmi_connector_duplicate_state(struct drm_connector *connector) ...@@ -689,13 +639,13 @@ inno_hdmi_connector_duplicate_state(struct drm_connector *connector)
static const struct drm_connector_funcs inno_hdmi_connector_funcs = { static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.detect = inno_hdmi_connector_detect, .detect = inno_hdmi_connector_detect,
.destroy = inno_hdmi_connector_destroy,
.reset = inno_hdmi_connector_reset, .reset = inno_hdmi_connector_reset,
.atomic_duplicate_state = inno_hdmi_connector_duplicate_state, .atomic_duplicate_state = inno_hdmi_connector_duplicate_state,
.atomic_destroy_state = inno_hdmi_connector_destroy_state, .atomic_destroy_state = inno_hdmi_connector_destroy_state,
}; };
static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = { static struct drm_connector_helper_funcs inno_hdmi_connector_helper_funcs = {
.atomic_check = drm_atomic_helper_connector_hdmi_check,
.get_modes = inno_hdmi_connector_get_modes, .get_modes = inno_hdmi_connector_get_modes,
.mode_valid = inno_hdmi_connector_mode_valid, .mode_valid = inno_hdmi_connector_mode_valid,
}; };
...@@ -723,10 +673,14 @@ static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi) ...@@ -723,10 +673,14 @@ static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)
drm_connector_helper_add(&hdmi->connector, drm_connector_helper_add(&hdmi->connector,
&inno_hdmi_connector_helper_funcs); &inno_hdmi_connector_helper_funcs);
drm_connector_init_with_ddc(drm, &hdmi->connector, drmm_connector_hdmi_init(drm, &hdmi->connector,
"Rockchip", "Inno HDMI",
&inno_hdmi_connector_funcs, &inno_hdmi_connector_funcs,
&inno_hdmi_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA, DRM_MODE_CONNECTOR_HDMIA,
hdmi->ddc); hdmi->ddc,
BIT(HDMI_COLORSPACE_RGB),
8);
drm_connector_attach_encoder(&hdmi->connector, encoder); drm_connector_attach_encoder(&hdmi->connector, encoder);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment