diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index d38519e3923e..ca6a72a4cf80 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -401,8 +401,7 @@ drm_bridge_connector_tmds_char_rate_valid(const struct drm_connector *connector, return MODE_OK; } -static int drm_bridge_connector_clear_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type) +static int drm_bridge_connector_clear_avi_infoframe(struct drm_connector *connector) { struct drm_bridge_connector *bridge_connector = to_drm_bridge_connector(connector); @@ -412,35 +411,70 @@ static int drm_bridge_connector_clear_infoframe(struct drm_connector *connector, if (!bridge) return -EINVAL; - switch (type) { - case HDMI_INFOFRAME_TYPE_AVI: - /* required */ - return bridge->funcs->hdmi_clear_avi_infoframe(bridge); - case HDMI_INFOFRAME_TYPE_VENDOR: - /* required */ - return bridge->funcs->hdmi_clear_hdmi_infoframe(bridge); - case HDMI_INFOFRAME_TYPE_AUDIO: - if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) - return bridge->funcs->hdmi_clear_audio_infoframe(bridge); - break; - case HDMI_INFOFRAME_TYPE_DRM: - if (bridge->ops & DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME) - return bridge->funcs->hdmi_clear_hdr_drm_infoframe(bridge); - break; - case HDMI_INFOFRAME_TYPE_SPD: - if (bridge->ops & DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME) - return bridge->funcs->hdmi_clear_spd_infoframe(bridge); - break; - } + return bridge->funcs->hdmi_clear_avi_infoframe(bridge); +} - drm_dbg_driver(connector->dev, "Unsupported HDMI InfoFrame %x\n", type); +static int drm_bridge_connector_write_avi_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi; + if (!bridge) + return -EINVAL; + + return bridge->funcs->hdmi_write_avi_infoframe(bridge, buffer, len); +} + +static int drm_bridge_connector_clear_hdmi_infoframe(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi; + if (!bridge) + return -EINVAL; + + return bridge->funcs->hdmi_clear_hdmi_infoframe(bridge); +} + +static int drm_bridge_connector_write_hdmi_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi; + if (!bridge) + return -EINVAL; + + return bridge->funcs->hdmi_write_hdmi_infoframe(bridge, buffer, len); +} + +static int drm_bridge_connector_clear_audio_infoframe(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi; + if (!bridge) + return -EINVAL; + + if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) + return bridge->funcs->hdmi_clear_audio_infoframe(bridge); + + drm_dbg_driver(connector->dev, "Unsupported HDMI Audio InfoFrame\n"); return 0; } -static int drm_bridge_connector_write_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type, - const u8 *buffer, size_t len) +static int drm_bridge_connector_write_audio_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) { struct drm_bridge_connector *bridge_connector = to_drm_bridge_connector(connector); @@ -450,28 +484,84 @@ static int drm_bridge_connector_write_infoframe(struct drm_connector *connector, if (!bridge) return -EINVAL; - switch (type) { - case HDMI_INFOFRAME_TYPE_AVI: - /* required */ - return bridge->funcs->hdmi_write_avi_infoframe(bridge, buffer, len); - case HDMI_INFOFRAME_TYPE_VENDOR: - /* required */ - return bridge->funcs->hdmi_write_hdmi_infoframe(bridge, buffer, len); - case HDMI_INFOFRAME_TYPE_AUDIO: - if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) - return bridge->funcs->hdmi_write_audio_infoframe(bridge, buffer, len); - break; - case HDMI_INFOFRAME_TYPE_DRM: - if (bridge->ops & DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME) - return bridge->funcs->hdmi_write_hdr_drm_infoframe(bridge, buffer, len); - break; - case HDMI_INFOFRAME_TYPE_SPD: - if (bridge->ops & DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME) - return bridge->funcs->hdmi_write_spd_infoframe(bridge, buffer, len); - break; - } + if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) + return bridge->funcs->hdmi_write_audio_infoframe(bridge, buffer, len); - drm_dbg_driver(connector->dev, "Unsupported HDMI InfoFrame %x\n", type); + drm_dbg_driver(connector->dev, "Unsupported HDMI Audio InfoFrame\n"); + + return 0; +} + +static int drm_bridge_connector_clear_hdr_drm_infoframe(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi; + if (!bridge) + return -EINVAL; + + if (bridge->ops & DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME) + return bridge->funcs->hdmi_clear_hdr_drm_infoframe(bridge); + + drm_dbg_driver(connector->dev, "Unsupported HDMI HDR DRM InfoFrame\n"); + + return 0; +} + +static int drm_bridge_connector_write_hdr_drm_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi; + if (!bridge) + return -EINVAL; + + if (bridge->ops & DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME) + return bridge->funcs->hdmi_write_hdr_drm_infoframe(bridge, buffer, len); + + drm_dbg_driver(connector->dev, "Unsupported HDMI HDR DRM InfoFrame\n"); + + return 0; +} + +static int drm_bridge_connector_clear_spd_infoframe(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi; + if (!bridge) + return -EINVAL; + + if (bridge->ops & DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME) + return bridge->funcs->hdmi_clear_spd_infoframe(bridge); + + drm_dbg_driver(connector->dev, "Unsupported HDMI SPD InfoFrame\n"); + + return 0; +} + +static int drm_bridge_connector_write_spd_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi; + if (!bridge) + return -EINVAL; + + if (bridge->ops & DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME) + return bridge->funcs->hdmi_write_spd_infoframe(bridge, buffer, len); + + drm_dbg_driver(connector->dev, "Unsupported HDMI SPD InfoFrame\n"); return 0; } @@ -492,9 +582,27 @@ drm_bridge_connector_read_edid(struct drm_connector *connector) static const struct drm_connector_hdmi_funcs drm_bridge_connector_hdmi_funcs = { .tmds_char_rate_valid = drm_bridge_connector_tmds_char_rate_valid, - .clear_infoframe = drm_bridge_connector_clear_infoframe, - .write_infoframe = drm_bridge_connector_write_infoframe, .read_edid = drm_bridge_connector_read_edid, + .avi = { + .clear_infoframe = drm_bridge_connector_clear_avi_infoframe, + .write_infoframe = drm_bridge_connector_write_avi_infoframe, + }, + .hdmi = { + .clear_infoframe = drm_bridge_connector_clear_hdmi_infoframe, + .write_infoframe = drm_bridge_connector_write_hdmi_infoframe, + }, + .audio = { + .clear_infoframe = drm_bridge_connector_clear_audio_infoframe, + .write_infoframe = drm_bridge_connector_write_audio_infoframe, + }, + .hdr_drm = { + .clear_infoframe = drm_bridge_connector_clear_hdr_drm_infoframe, + .write_infoframe = drm_bridge_connector_write_hdr_drm_infoframe, + }, + .spd = { + .clear_infoframe = drm_bridge_connector_clear_spd_infoframe, + .write_infoframe = drm_bridge_connector_write_spd_infoframe, + }, }; static int drm_bridge_connector_audio_startup(struct drm_connector *connector) diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index a561f124be99..5a3817271d91 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -891,62 +891,21 @@ drm_hdmi_connector_mode_valid(struct drm_connector *connector, } EXPORT_SYMBOL(drm_hdmi_connector_mode_valid); -static int clear_device_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type) +static int clear_infoframe(struct drm_connector *connector, + const struct drm_connector_infoframe_funcs *funcs, + const char *type) { - const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs; struct drm_device *dev = connector->dev; int ret; - drm_dbg_kms(dev, "Clearing infoframe type 0x%x\n", type); + drm_dbg_kms(dev, "Clearing %s InfoFrame\n", type); - if (!funcs || !funcs->clear_infoframe) { + if (!funcs->clear_infoframe) { drm_dbg_kms(dev, "Function not implemented, bailing.\n"); return 0; } - ret = funcs->clear_infoframe(connector, type); - if (ret) { - drm_dbg_kms(dev, "Call failed: %d\n", ret); - return ret; - } - - return 0; -} - -static int clear_infoframe(struct drm_connector *connector, - struct drm_connector_hdmi_infoframe *old_frame) -{ - int ret; - - ret = clear_device_infoframe(connector, old_frame->data.any.type); - if (ret) - return ret; - - return 0; -} - -static int write_device_infoframe(struct drm_connector *connector, - union hdmi_infoframe *frame) -{ - const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs; - struct drm_device *dev = connector->dev; - u8 buffer[HDMI_INFOFRAME_SIZE(MAX)]; - int ret; - int len; - - drm_dbg_kms(dev, "Writing infoframe type %x\n", frame->any.type); - - if (!funcs || !funcs->write_infoframe) { - drm_dbg_kms(dev, "Function not implemented, bailing.\n"); - return -EINVAL; - } - - len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer)); - if (len < 0) - return len; - - ret = funcs->write_infoframe(connector, frame->any.type, buffer, len); + ret = funcs->clear_infoframe(connector); if (ret) { drm_dbg_kms(dev, "Call failed: %d\n", ret); return ret; @@ -956,26 +915,46 @@ static int write_device_infoframe(struct drm_connector *connector, } static int write_infoframe(struct drm_connector *connector, + const struct drm_connector_infoframe_funcs *funcs, + const char *type, struct drm_connector_hdmi_infoframe *new_frame) { + struct drm_device *dev = connector->dev; + u8 buffer[HDMI_INFOFRAME_SIZE(MAX)]; int ret; + int len; - ret = write_device_infoframe(connector, &new_frame->data); - if (ret) + drm_dbg_kms(dev, "Writing %s InfoFrame\n", type); + + if (!funcs->write_infoframe) { + drm_dbg_kms(dev, "Function not implemented, bailing.\n"); + return 0; /* XXX: temporal until we stop generating unsupported frames */ + } + + len = hdmi_infoframe_pack(&new_frame->data, buffer, sizeof(buffer)); + if (len < 0) + return len; + + ret = funcs->write_infoframe(connector, buffer, len); + if (ret) { + drm_dbg_kms(dev, "Call failed: %d\n", ret); return ret; + } return 0; } static int write_or_clear_infoframe(struct drm_connector *connector, + const struct drm_connector_infoframe_funcs *funcs, + const char *type, struct drm_connector_hdmi_infoframe *old_frame, struct drm_connector_hdmi_infoframe *new_frame) { if (new_frame->set) - return write_infoframe(connector, new_frame); + return write_infoframe(connector, funcs, type, new_frame); if (old_frame->set && !new_frame->set) - return clear_infoframe(connector, old_frame); + return clear_infoframe(connector, funcs, type); return 0; } @@ -995,6 +974,7 @@ static int write_or_clear_infoframe(struct drm_connector *connector, int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector, struct drm_atomic_state *state) { + const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs; struct drm_connector_state *old_conn_state = drm_atomic_get_old_connector_state(state, connector); struct drm_connector_state *new_conn_state = @@ -1005,9 +985,15 @@ int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *con if (!info->is_hdmi) return 0; + if (!funcs) { + drm_dbg_kms(connector->dev, "Function not implemented, bailing.\n"); + return -EINVAL; + } + mutex_lock(&connector->hdmi.infoframes.lock); ret = write_or_clear_infoframe(connector, + &funcs->avi, "AVI", &old_conn_state->hdmi.infoframes.avi, &new_conn_state->hdmi.infoframes.avi); if (ret) @@ -1015,18 +1001,21 @@ int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *con if (connector->hdmi.infoframes.audio.set) { ret = write_infoframe(connector, + &funcs->audio, "Audio", &connector->hdmi.infoframes.audio); if (ret) goto out; } ret = write_or_clear_infoframe(connector, + &funcs->hdr_drm, "HDR DRM", &old_conn_state->hdmi.infoframes.hdr_drm, &new_conn_state->hdmi.infoframes.hdr_drm); if (ret) goto out; ret = write_or_clear_infoframe(connector, + &funcs->spd, "SPD", &old_conn_state->hdmi.infoframes.spd, &new_conn_state->hdmi.infoframes.spd); if (ret) @@ -1034,6 +1023,7 @@ int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *con if (info->has_hdmi_infoframe) { ret = write_or_clear_infoframe(connector, + &funcs->hdmi, "HDMI-VS", &old_conn_state->hdmi.infoframes.hdmi, &new_conn_state->hdmi.infoframes.hdmi); if (ret) @@ -1062,6 +1052,7 @@ int drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *connector, struct hdmi_audio_infoframe *frame) { + const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs; struct drm_connector_hdmi_infoframe *infoframe = &connector->hdmi.infoframes.audio; struct drm_display_info *info = &connector->display_info; @@ -1070,12 +1061,17 @@ drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector *co if (!info->is_hdmi) return 0; + if (!funcs) { + drm_dbg_kms(connector->dev, "Function not implemented, bailing.\n"); + return -EINVAL; + } + mutex_lock(&connector->hdmi.infoframes.lock); memcpy(&infoframe->data, frame, sizeof(infoframe->data)); infoframe->set = true; - ret = write_infoframe(connector, infoframe); + ret = write_infoframe(connector, &funcs->audio, "Audio", infoframe); mutex_unlock(&connector->hdmi.infoframes.lock); @@ -1097,6 +1093,7 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_update_audio_infoframe); int drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *connector) { + const struct drm_connector_hdmi_funcs *funcs = connector->hdmi.funcs; struct drm_connector_hdmi_infoframe *infoframe = &connector->hdmi.infoframes.audio; struct drm_display_info *info = &connector->display_info; @@ -1105,11 +1102,16 @@ drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *con if (!info->is_hdmi) return 0; + if (!funcs) { + drm_dbg_kms(connector->dev, "Function not implemented, bailing.\n"); + return -EINVAL; + } + mutex_lock(&connector->hdmi.infoframes.lock); infoframe->set = false; - ret = clear_infoframe(connector, infoframe); + ret = clear_infoframe(connector, &funcs->audio, "Audio"); memset(&infoframe->data, 0, sizeof(infoframe->data)); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 40e025712c9b..4f5b27fab475 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -600,8 +600,10 @@ int drmm_connector_hdmi_init(struct drm_device *dev, if (!(max_bpc == 8 || max_bpc == 10 || max_bpc == 12)) return -EINVAL; - if (!hdmi_funcs->clear_infoframe || - !hdmi_funcs->write_infoframe) + if (!hdmi_funcs->avi.clear_infoframe || + !hdmi_funcs->avi.write_infoframe || + !hdmi_funcs->hdmi.clear_infoframe || + !hdmi_funcs->hdmi.write_infoframe) return -EINVAL; ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc); diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index 6263ee15880a..a50f260c73e4 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -40,27 +40,19 @@ #define drm_connector_to_sun4i_hdmi(c) \ container_of_const(c, struct sun4i_hdmi, connector) -static int sun4i_hdmi_clear_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type) +static int sun4i_hdmi_clear_avi_infoframe(struct drm_connector *connector) { drm_warn_once(connector->dev, "clearing of AVI infoframe is not implemented\n"); return 0; } -static int sun4i_hdmi_write_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type, - const u8 *buffer, size_t len) +static int sun4i_hdmi_write_avi_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) { struct sun4i_hdmi *hdmi = drm_connector_to_sun4i_hdmi(connector); int i; - if (type != HDMI_INFOFRAME_TYPE_AVI) { - drm_err(connector->dev, - "Unsupported infoframe type: %u\n", type); - return 0; - } - for (i = 0; i < len; i++) writeb(buffer[i], hdmi->base + SUN4I_HDMI_AVI_INFOFRAME_REG(i)); @@ -68,6 +60,21 @@ static int sun4i_hdmi_write_infoframe(struct drm_connector *connector, } +static int sun4i_hdmi_clear_hdmi_infoframe(struct drm_connector *connector) +{ + drm_warn_once(connector->dev, "HDMI VSI not implemented\n"); + + return 0; +} + +static int sun4i_hdmi_write_hdmi_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + drm_warn_once(connector->dev, "HDMI VSI not implemented\n"); + + return 0; +} + static void sun4i_hdmi_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) { @@ -244,8 +251,14 @@ static struct i2c_adapter *sun4i_hdmi_get_ddc(struct device *dev) static const struct drm_connector_hdmi_funcs sun4i_hdmi_hdmi_connector_funcs = { .tmds_char_rate_valid = sun4i_hdmi_connector_clock_valid, - .clear_infoframe = sun4i_hdmi_clear_infoframe, - .write_infoframe = sun4i_hdmi_write_infoframe, + .avi = { + .clear_infoframe = sun4i_hdmi_clear_avi_infoframe, + .write_infoframe = sun4i_hdmi_write_avi_infoframe, + }, + .hdmi = { + .clear_infoframe = sun4i_hdmi_clear_hdmi_infoframe, + .write_infoframe = sun4i_hdmi_write_hdmi_infoframe, + }, }; static const struct drm_connector_helper_funcs sun4i_hdmi_connector_helper_funcs = { diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index f356ea695ae7..86860ad0861c 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -25,22 +25,26 @@ struct drm_connector_init_priv { struct i2c_adapter ddc; }; -static int accept_infoframe_clear_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type) +static int accept_infoframe_clear_infoframe(struct drm_connector *connector) { return 0; } static int accept_infoframe_write_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type, const u8 *buffer, size_t len) { return 0; } static const struct drm_connector_hdmi_funcs dummy_hdmi_funcs = { - .clear_infoframe = accept_infoframe_clear_infoframe, - .write_infoframe = accept_infoframe_write_infoframe, + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, }; static const struct drm_connector_funcs dummy_funcs = { diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c index cfa14a6eb97f..1c60947a13a1 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -78,22 +78,26 @@ static int set_connector_edid(struct kunit *test, struct drm_connector *connecto return ret; } -static int accept_infoframe_clear_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type) +static int accept_infoframe_clear_infoframe(struct drm_connector *connector) { return 0; } static int accept_infoframe_write_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type, const u8 *buffer, size_t len) { return 0; } static const struct drm_connector_hdmi_funcs dummy_connector_hdmi_funcs = { - .clear_infoframe = accept_infoframe_clear_infoframe, - .write_infoframe = accept_infoframe_write_infoframe, + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, }; static enum drm_mode_status @@ -106,8 +110,14 @@ reject_connector_tmds_char_rate_valid(const struct drm_connector *connector, static const struct drm_connector_hdmi_funcs reject_connector_hdmi_funcs = { .tmds_char_rate_valid = reject_connector_tmds_char_rate_valid, - .clear_infoframe = accept_infoframe_clear_infoframe, - .write_infoframe = accept_infoframe_write_infoframe, + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, }; static enum drm_mode_status @@ -120,8 +130,14 @@ reject_100mhz_connector_tmds_char_rate_valid(const struct drm_connector *connect static const struct drm_connector_hdmi_funcs reject_100mhz_connector_hdmi_funcs = { .tmds_char_rate_valid = reject_100mhz_connector_tmds_char_rate_valid, - .clear_infoframe = accept_infoframe_clear_infoframe, - .write_infoframe = accept_infoframe_write_infoframe, + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, }; static int dummy_connector_get_modes(struct drm_connector *connector) @@ -2449,19 +2465,21 @@ retry_crtc_state: drm_modeset_acquire_fini(&ctx); } -static int reject_avi_infoframe_write_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type, - const u8 *buffer, size_t len) +static int reject_infoframe_write_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) { - if (type == HDMI_INFOFRAME_TYPE_AVI) - return -EOPNOTSUPP; - - return 0; + return -EOPNOTSUPP; } static const struct drm_connector_hdmi_funcs reject_avi_infoframe_hdmi_funcs = { - .clear_infoframe = accept_infoframe_clear_infoframe, - .write_infoframe = reject_avi_infoframe_write_infoframe, + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = reject_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, }; /* @@ -2552,19 +2570,19 @@ retry_crtc_state: drm_modeset_acquire_fini(&ctx); } -static int reject_hdr_infoframe_write_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type, - const u8 *buffer, size_t len) -{ - if (type == HDMI_INFOFRAME_TYPE_DRM) - return -EOPNOTSUPP; - - return 0; -} - static const struct drm_connector_hdmi_funcs reject_hdr_infoframe_hdmi_funcs = { - .clear_infoframe = accept_infoframe_clear_infoframe, - .write_infoframe = reject_hdr_infoframe_write_infoframe, + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdr_drm = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = reject_infoframe_write_infoframe, + }, }; /* @@ -2800,19 +2818,19 @@ retry_conn_state: drm_modeset_acquire_fini(&ctx); } -static int reject_audio_infoframe_write_infoframe(struct drm_connector *connector, - enum hdmi_infoframe_type type, - const u8 *buffer, size_t len) -{ - if (type == HDMI_INFOFRAME_TYPE_AUDIO) - return -EOPNOTSUPP; - - return 0; -} - static const struct drm_connector_hdmi_funcs reject_audio_infoframe_hdmi_funcs = { - .clear_infoframe = accept_infoframe_clear_infoframe, - .write_infoframe = reject_audio_infoframe_write_infoframe, + .avi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .hdmi = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = accept_infoframe_write_infoframe, + }, + .audio = { + .clear_infoframe = accept_infoframe_clear_infoframe, + .write_infoframe = reject_infoframe_write_infoframe, + }, }; /* diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index 4cfb7ebc0c81..9fe605a42df7 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -727,6 +727,66 @@ out: return ret; } +static int vc4_hdmi_clear_avi_infoframe(struct drm_connector *connector) +{ + return vc4_hdmi_clear_infoframe(connector, HDMI_INFOFRAME_TYPE_AVI); +} + +static int vc4_hdmi_clear_hdmi_infoframe(struct drm_connector *connector) +{ + return vc4_hdmi_clear_infoframe(connector, HDMI_INFOFRAME_TYPE_VENDOR); +} + +static int vc4_hdmi_clear_audio_infoframe(struct drm_connector *connector) +{ + return vc4_hdmi_clear_infoframe(connector, HDMI_INFOFRAME_TYPE_AUDIO); +} + +static int vc4_hdmi_clear_hdr_drm_infoframe(struct drm_connector *connector) +{ + return vc4_hdmi_clear_infoframe(connector, HDMI_INFOFRAME_TYPE_DRM); +} + +static int vc4_hdmi_clear_spd_infoframe(struct drm_connector *connector) +{ + return vc4_hdmi_clear_infoframe(connector, HDMI_INFOFRAME_TYPE_SPD); +} + +static int vc4_hdmi_write_avi_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + return vc4_hdmi_write_infoframe(connector, HDMI_INFOFRAME_TYPE_AVI, + buffer, len); +} + +static int vc4_hdmi_write_hdmi_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + return vc4_hdmi_write_infoframe(connector, HDMI_INFOFRAME_TYPE_VENDOR, + buffer, len); +} + +static int vc4_hdmi_write_audio_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + return vc4_hdmi_write_infoframe(connector, HDMI_INFOFRAME_TYPE_AUDIO, + buffer, len); +} + +static int vc4_hdmi_write_hdr_drm_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + return vc4_hdmi_write_infoframe(connector, HDMI_INFOFRAME_TYPE_DRM, + buffer, len); +} + +static int vc4_hdmi_write_spd_infoframe(struct drm_connector *connector, + const u8 *buffer, size_t len) +{ + return vc4_hdmi_write_infoframe(connector, HDMI_INFOFRAME_TYPE_SPD, + buffer, len); +} + #define SCRAMBLING_POLLING_DELAY_MS 1000 static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder) @@ -1684,8 +1744,26 @@ vc4_hdmi_connector_clock_valid(const struct drm_connector *connector, static const struct drm_connector_hdmi_funcs vc4_hdmi_hdmi_connector_funcs = { .tmds_char_rate_valid = vc4_hdmi_connector_clock_valid, - .clear_infoframe = vc4_hdmi_clear_infoframe, - .write_infoframe = vc4_hdmi_write_infoframe, + .avi = { + .clear_infoframe = vc4_hdmi_clear_avi_infoframe, + .write_infoframe = vc4_hdmi_write_avi_infoframe, + }, + .hdmi = { + .clear_infoframe = vc4_hdmi_clear_hdmi_infoframe, + .write_infoframe = vc4_hdmi_write_hdmi_infoframe, + }, + .audio = { + .clear_infoframe = vc4_hdmi_clear_audio_infoframe, + .write_infoframe = vc4_hdmi_write_audio_infoframe, + }, + .hdr_drm = { + .clear_infoframe = vc4_hdmi_clear_hdr_drm_infoframe, + .write_infoframe = vc4_hdmi_write_hdr_drm_infoframe, + }, + .spd = { + .clear_infoframe = vc4_hdmi_clear_spd_infoframe, + .write_infoframe = vc4_hdmi_write_spd_infoframe, + }, }; #define WIFI_2_4GHz_CH1_MIN_FREQ 2400000000ULL diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 4543833acdec..7eaec37ae1c7 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1221,6 +1221,45 @@ struct drm_connector_cec_funcs { void (*phys_addr_set)(struct drm_connector *connector, u16 addr); }; +/** + * struct drm_connector_infoframe_funcs - InfoFrame-related functions + */ +struct drm_connector_infoframe_funcs { + /** + * @clear_infoframe: + * + * This callback is invoked through + * @drm_atomic_helper_connector_hdmi_update_infoframes during a + * commit to clear the infoframes into the hardware. It will be + * called once for each frame type to be disabled. + * + * The @clear_infoframe callback is mandatory for AVI and HDMI-VS + * InfoFrame types. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*clear_infoframe)(struct drm_connector *connector); + + /** + * @write_infoframe: + * + * This callback is invoked through + * @drm_atomic_helper_connector_hdmi_update_infoframes during a + * commit to program the infoframes into the hardware. It will + * be called for every updated infoframe type. + * + * The @write_infoframe callback is mandatory for AVI and HDMI-VS + * InfoFrame types. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*write_infoframe)(struct drm_connector *connector, + const u8 *buffer, size_t len); + +}; + /** * struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions */ @@ -1244,41 +1283,6 @@ struct drm_connector_hdmi_funcs { const struct drm_display_mode *mode, unsigned long long tmds_rate); - /** - * @clear_infoframe: - * - * This callback is invoked through - * @drm_atomic_helper_connector_hdmi_update_infoframes during a - * commit to clear the infoframes into the hardware. It will be - * called multiple times, once for every disabled infoframe - * type. - * - * The @clear_infoframe callback is mandatory. - * - * Returns: - * 0 on success, a negative error code otherwise - */ - int (*clear_infoframe)(struct drm_connector *connector, - enum hdmi_infoframe_type type); - - /** - * @write_infoframe: - * - * This callback is invoked through - * @drm_atomic_helper_connector_hdmi_update_infoframes during a - * commit to program the infoframes into the hardware. It will - * be called multiple times, once for every updated infoframe - * type. - * - * The @write_infoframe callback is mandatory. - * - * Returns: - * 0 on success, a negative error code otherwise - */ - int (*write_infoframe)(struct drm_connector *connector, - enum hdmi_infoframe_type type, - const u8 *buffer, size_t len); - /** * @read_edid: * @@ -1293,6 +1297,47 @@ struct drm_connector_hdmi_funcs { * Valid EDID on success, NULL in case of failure. */ const struct drm_edid *(*read_edid)(struct drm_connector *connector); + + /** + * @avi: + * + * Set of callbacks for handling the AVI InfoFrame. These callbacks are + * mandatory. + */ + struct drm_connector_infoframe_funcs avi; + + /** + * @hdmi: + * + * Set of callbacks for handling the HDMI Vendor-Specific InfoFrame. + * These callbacks are mandatory. + */ + struct drm_connector_infoframe_funcs hdmi; + + /** + * @audio: + * + * Set of callbacks for handling the Audio InfoFrame. These callbacks + * are optional, but they are required for drivers which use + * drm_atomic_helper_connector_hdmi_update_audio_infoframe(). + */ + struct drm_connector_infoframe_funcs audio; + + /** + * @hdr_drm: + * + * Set of callbacks for handling the HDR DRM InfoFrame. These callbacks + * are mandatory if HDR output is to be supported. + */ + struct drm_connector_infoframe_funcs hdr_drm; + + /** + * @spd: + * + * Set of callbacks for handling the SPD InfoFrame. These callbacks are + * optional. + */ + struct drm_connector_infoframe_funcs spd; }; /**