Skip to content

Commit

Permalink
drm/rockchip: inno_hdmi: Switch to HDMI connector
Browse files Browse the repository at this point in the history
The new HDMI connector infrastructure allows to remove some boilerplate,
especially to generate infoframes. Let's switch to it.

Reviewed-by: Heiko Stuebner <heiko@sntech.de>
Acked-by: Heiko Stuebner <heiko@sntech.de>
Acked-by: Andy Yan <andyshrk@163.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240527-kms-hdmi-connector-state-v15-28-c5af16c3aae2@kernel.org
Signed-off-by: Maxime Ripard <mripard@kernel.org>
  • Loading branch information
Maxime Ripard committed May 28, 2024
1 parent b3bf195 commit 65548c8
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 99 deletions.
3 changes: 3 additions & 0 deletions drivers/gpu/drm/rockchip/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ config ROCKCHIP_DW_MIPI_DSI

config ROCKCHIP_INNO_HDMI
bool "Rockchip specific extensions for Innosilicon HDMI"
select DRM_DISPLAY_HDMI_HELPER
select DRM_DISPLAY_HDMI_STATE_HELPER
select DRM_DISPLAY_HELPER
help
This selects support for Rockchip SoC specific extensions
for the Innosilicon HDMI driver. If you want to enable
Expand Down
152 changes: 53 additions & 99 deletions drivers/gpu/drm/rockchip/inno_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
#include <drm/drm_probe_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 "inno_hdmi.h"
Expand Down Expand Up @@ -67,9 +70,7 @@ struct inno_hdmi {

struct inno_hdmi_connector_state {
struct drm_connector_state base;
unsigned int enc_out_format;
unsigned int colorimetry;
bool rgb_limited_range;
};

static struct inno_hdmi *encoder_to_inno_hdmi(struct drm_encoder *encoder)
Expand Down Expand Up @@ -257,86 +258,49 @@ static void inno_hdmi_reset(struct inno_hdmi *hdmi)
inno_hdmi_standby(hdmi);
}

static void inno_hdmi_disable_frame(struct inno_hdmi *hdmi,
enum hdmi_infoframe_type type)
static int inno_hdmi_disable_frame(struct drm_connector *connector,
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) {
drm_err(connector->dev,
"Unsupported infoframe type: %u\n", type);
return;
return 0;
}

hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);

return 0;
}

static int inno_hdmi_upload_frame(struct inno_hdmi *hdmi,
union hdmi_infoframe *frame, enum hdmi_infoframe_type type)
static int inno_hdmi_upload_frame(struct drm_connector *connector,
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];
ssize_t rc, i;
ssize_t i;

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

inno_hdmi_disable_frame(hdmi, type);

rc = hdmi_infoframe_pack(frame, packed_frame,
sizeof(packed_frame));
if (rc < 0)
return rc;
inno_hdmi_disable_frame(connector, type);

for (i = 0; i < rc; i++)
for (i = 0; i < len; 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,
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;

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 const struct drm_connector_hdmi_funcs inno_hdmi_hdmi_connector_funcs = {
.clear_infoframe = inno_hdmi_disable_frame,
.write_infoframe = inno_hdmi_upload_frame,
};

static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
{
Expand All @@ -361,8 +325,8 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
v_VIDEO_INPUT_CSP(0);
hdmi_writeb(hdmi, HDMI_VIDEO_CONTRL2, value);

if (inno_conn_state->enc_out_format == HDMI_COLORSPACE_RGB) {
if (inno_conn_state->rgb_limited_range) {
if (conn_state->hdmi.output_format == HDMI_COLORSPACE_RGB) {
if (conn_state->hdmi.is_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;
Expand All @@ -380,14 +344,14 @@ static int inno_hdmi_config_video_csc(struct inno_hdmi *hdmi)
}
} else {
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;
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) {
if (conn_state->hdmi.output_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;
Expand Down Expand Up @@ -462,10 +426,20 @@ static int inno_hdmi_config_video_timing(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;
unsigned long mpixelclock = mode->clock * 1000;
struct drm_connector *connector = &hdmi->connector;
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 */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
Expand All @@ -475,26 +449,25 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi,
hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
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);

if (display->is_hdmi)
inno_hdmi_config_video_avi(hdmi, mode);
drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);

/*
* When IP controller have configured to an accurate video
* timing, then the TMDS clock source would be switched to
* DCLK_LCDC, so we need to init the TMDS rate to mode pixel
* 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 */
hdmi_modb(hdmi, HDMI_AV_MUTE, m_AUDIO_MUTE | m_VIDEO_BLACK,
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;
}
Expand Down Expand Up @@ -535,18 +508,8 @@ 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_setup(hdmi, state);
}

static void inno_hdmi_encoder_disable(struct drm_encoder *encoder,
Expand All @@ -563,7 +526,6 @@ 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);
struct drm_display_mode *mode = &crtc_state->adjusted_mode;
u8 vic = drm_match_cea_mode(mode);
struct inno_hdmi_connector_state *inno_conn_state =
Expand All @@ -580,12 +542,7 @@ inno_hdmi_encoder_atomic_check(struct drm_encoder *encoder,
else
inno_conn_state->colorimetry = HDMI_COLORIMETRY_ITU_709;

inno_conn_state->enc_out_format = HDMI_COLORSPACE_RGB;
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;
return 0;
}

static struct drm_encoder_helper_funcs inno_hdmi_encoder_helper_funcs = {
Expand Down Expand Up @@ -629,12 +586,6 @@ inno_hdmi_connector_mode_valid(struct drm_connector *connector,
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
inno_hdmi_connector_destroy_state(struct drm_connector *connector,
struct drm_connector_state *state)
Expand All @@ -660,10 +611,9 @@ static void inno_hdmi_connector_reset(struct drm_connector *connector)
return;

__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->enc_out_format = HDMI_COLORSPACE_RGB;
inno_conn_state->rgb_limited_range = false;
}

static struct drm_connector_state *
Expand All @@ -689,13 +639,13 @@ inno_hdmi_connector_duplicate_state(struct drm_connector *connector)
static const struct drm_connector_funcs inno_hdmi_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = inno_hdmi_connector_detect,
.destroy = inno_hdmi_connector_destroy,
.reset = inno_hdmi_connector_reset,
.atomic_duplicate_state = inno_hdmi_connector_duplicate_state,
.atomic_destroy_state = inno_hdmi_connector_destroy_state,
};

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,
.mode_valid = inno_hdmi_connector_mode_valid,
};
Expand Down Expand Up @@ -723,10 +673,14 @@ static int inno_hdmi_register(struct drm_device *drm, struct inno_hdmi *hdmi)

drm_connector_helper_add(&hdmi->connector,
&inno_hdmi_connector_helper_funcs);
drm_connector_init_with_ddc(drm, &hdmi->connector,
&inno_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA,
hdmi->ddc);
drmm_connector_hdmi_init(drm, &hdmi->connector,
"Rockchip", "Inno HDMI",
&inno_hdmi_connector_funcs,
&inno_hdmi_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA,
hdmi->ddc,
BIT(HDMI_COLORSPACE_RGB),
8);

drm_connector_attach_encoder(&hdmi->connector, encoder);

Expand Down

0 comments on commit 65548c8

Please sign in to comment.