Commit b626b1a1 authored by Dmitry Baryshkov's avatar Dmitry Baryshkov
Browse files

drm/bridge: refactor HDMI InfoFrame callbacks



Having only a single set of callbacks, hdmi_clear_infoframe and
hdmi_write_infoframe, bridge drivers don't have an easy way to signal to
the DRM framework, which InfoFrames are actually supported by the
hardware and by the driver and which are not. Also, it makes it
extremely easy for HDMI bridge drivers to skip implementing the
seemingly required InfoFrames (e.g. HDMI VSI). Last, but not least,
those callbacks take a single 'type' parameter, which makes it
impossible to implement support for multiple VSIs (which will be
required once we start working on HDMI Forum VSI).

Split the callbacks into a per-InfoFrame-kind pairs, letting the bridge
drivers actually signal supported features. The implementation follows
the overall drm_bridge design, where the bridge has a single
drm_bridge_funcs implementation and signals, which functions are to be
called using the drm_bridge->ops flags.

The AVI and HDMI VSI are assumed to be required for a normal HDMI
operation (with the drivers getting a drm_warn_once() stub
implementation if one is missing). The Audio InfoFrame is handled by the
existing DRM_BRIDGE_OP_HDMI_AUDIO, while the SPD and HDR DRM InfoFrames
got new drm_bridge_ops values.

Acked-by: default avatarMaxime Ripard <mripard@kernel.org>
Link: https://patch.msgid.link/20260107-limit-infoframes-2-v4-5-213d0d3bd490@oss.qualcomm.com


Signed-off-by: default avatarDmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
parent afc399f7
Loading
Loading
Loading
Loading
+105 −75
Original line number Diff line number Diff line
@@ -887,40 +887,47 @@ static const struct drm_edid *adv7511_bridge_edid_read(struct drm_bridge *bridge
	return adv7511_edid_read(adv, connector);
}

static int adv7511_bridge_hdmi_clear_infoframe(struct drm_bridge *bridge,
					       enum hdmi_infoframe_type type)
static int adv7511_bridge_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
{
	struct adv7511 *adv7511 = bridge_to_adv7511(bridge);

	switch (type) {
	case HDMI_INFOFRAME_TYPE_AUDIO:
	adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME);
		break;
	case HDMI_INFOFRAME_TYPE_AVI:

	return 0;
}

static int adv7511_bridge_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
{
	struct adv7511 *adv7511 = bridge_to_adv7511(bridge);

	adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
		break;
	case HDMI_INFOFRAME_TYPE_SPD:

	return 0;
}

static int adv7511_bridge_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
{
	struct adv7511 *adv7511 = bridge_to_adv7511(bridge);

	adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD);
		break;
	case HDMI_INFOFRAME_TYPE_VENDOR:
		adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
		break;
	default:
		drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
		break;

	return 0;
}

static int adv7511_bridge_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
	struct adv7511 *adv7511 = bridge_to_adv7511(bridge);

	adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);

	return 0;
}

static int adv7511_bridge_hdmi_write_infoframe(struct drm_bridge *bridge,
					       enum hdmi_infoframe_type type,
static int adv7511_bridge_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
						     const u8 *buffer, size_t len)
{
	struct adv7511 *adv7511 = bridge_to_adv7511(bridge);

	switch (type) {
	case HDMI_INFOFRAME_TYPE_AUDIO:
	/* send current Audio infoframe values while updating */
	regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
			   BIT(5), BIT(5));
@@ -934,8 +941,15 @@ static int adv7511_bridge_hdmi_write_infoframe(struct drm_bridge *bridge,
			   BIT(5), 0);

	adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AUDIO_INFOFRAME);
		break;
	case HDMI_INFOFRAME_TYPE_AVI:

	return 0;
}

static int adv7511_bridge_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
						   const u8 *buffer, size_t len)
{
	struct adv7511 *adv7511 = bridge_to_adv7511(bridge);

	/* send current AVI infoframe values while updating */
	regmap_update_bits(adv7511->regmap, ADV7511_REG_INFOFRAME_UPDATE,
			   BIT(6), BIT(6));
@@ -952,23 +966,32 @@ static int adv7511_bridge_hdmi_write_infoframe(struct drm_bridge *bridge,
			   BIT(6), 0);

	adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_AVI_INFOFRAME);
		break;
	case HDMI_INFOFRAME_TYPE_SPD:

	return 0;
}

static int adv7511_bridge_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
						   const u8 *buffer, size_t len)
{
	struct adv7511 *adv7511 = bridge_to_adv7511(bridge);

	adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPD);
	regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPD(0),
			  buffer, len);
	adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPD);
		break;
	case HDMI_INFOFRAME_TYPE_VENDOR:

	return 0;
}

static int adv7511_bridge_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
						    const u8 *buffer, size_t len)
{
	struct adv7511 *adv7511 = bridge_to_adv7511(bridge);

	adv7511_packet_disable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
	regmap_bulk_write(adv7511->regmap_packet, ADV7511_PACKET_SPARE1(0),
			  buffer, len);
	adv7511_packet_enable(adv7511, ADV7511_PACKET_ENABLE_SPARE1);
		break;
	default:
		drm_dbg_driver(adv7511->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
		break;
	}

	return 0;
}
@@ -986,8 +1009,14 @@ static const struct drm_bridge_funcs adv7511_bridge_funcs = {
	.atomic_reset = drm_atomic_helper_bridge_reset,

	.hdmi_tmds_char_rate_valid = adv7511_bridge_hdmi_tmds_char_rate_valid,
	.hdmi_clear_infoframe = adv7511_bridge_hdmi_clear_infoframe,
	.hdmi_write_infoframe = adv7511_bridge_hdmi_write_infoframe,
	.hdmi_clear_audio_infoframe = adv7511_bridge_hdmi_clear_audio_infoframe,
	.hdmi_write_audio_infoframe = adv7511_bridge_hdmi_write_audio_infoframe,
	.hdmi_clear_avi_infoframe = adv7511_bridge_hdmi_clear_avi_infoframe,
	.hdmi_write_avi_infoframe = adv7511_bridge_hdmi_write_avi_infoframe,
	.hdmi_clear_spd_infoframe = adv7511_bridge_hdmi_clear_spd_infoframe,
	.hdmi_write_spd_infoframe = adv7511_bridge_hdmi_write_spd_infoframe,
	.hdmi_clear_hdmi_infoframe = adv7511_bridge_hdmi_clear_hdmi_infoframe,
	.hdmi_write_hdmi_infoframe = adv7511_bridge_hdmi_write_hdmi_infoframe,

	.hdmi_audio_startup = adv7511_hdmi_audio_startup,
	.hdmi_audio_prepare = adv7511_hdmi_audio_prepare,
@@ -1322,7 +1351,8 @@ static int adv7511_probe(struct i2c_client *i2c)

	adv7511->bridge.ops = DRM_BRIDGE_OP_DETECT |
		DRM_BRIDGE_OP_EDID |
		DRM_BRIDGE_OP_HDMI;
		DRM_BRIDGE_OP_HDMI |
		DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME;
	if (adv7511->i2c_main->irq)
		adv7511->bridge.ops |= DRM_BRIDGE_OP_HPD;

+23 −18
Original line number Diff line number Diff line
@@ -584,37 +584,40 @@ static void inno_hdmi_init_hw(struct inno_hdmi *hdmi)
	hdmi_modb(hdmi, HDMI_STATUS, m_MASK_INT_HOTPLUG, v_MASK_INT_HOTPLUG(1));
}

static int inno_hdmi_bridge_clear_infoframe(struct drm_bridge *bridge,
					    enum hdmi_infoframe_type type)
static int inno_hdmi_bridge_clear_avi_infoframe(struct drm_bridge *bridge)
{
	struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);

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

	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, INFOFRAME_AVI);

	return 0;
}

static int inno_hdmi_bridge_write_infoframe(struct drm_bridge *bridge,
					    enum hdmi_infoframe_type type,
static int inno_hdmi_bridge_write_avi_infoframe(struct drm_bridge *bridge,
						const u8 *buffer, size_t len)
{
	struct inno_hdmi *hdmi = bridge_to_inno_hdmi(bridge);
	ssize_t i;

	if (type != HDMI_INFOFRAME_TYPE_AVI) {
		drm_err(bridge->dev, "Unsupported infoframe type: %u\n", type);
	inno_hdmi_bridge_clear_avi_infoframe(bridge);

	for (i = 0; i < len; i++)
		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, buffer[i]);

	return 0;
}

	inno_hdmi_bridge_clear_infoframe(bridge, type);
static int inno_hdmi_bridge_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
	drm_warn_once(bridge->encoder->dev, "HDMI VSI not implemented\n");

	for (i = 0; i < len; i++)
		hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + i, buffer[i]);
	return 0;
}

static int inno_hdmi_bridge_write_hdmi_infoframe(struct drm_bridge *bridge,
						 const u8 *buffer, size_t len)
{
	drm_warn_once(bridge->encoder->dev, "HDMI VSI not implemented\n");

	return 0;
}
@@ -883,8 +886,10 @@ static const struct drm_bridge_funcs inno_hdmi_bridge_funcs = {
	.atomic_disable = inno_hdmi_bridge_atomic_disable,
	.detect = inno_hdmi_bridge_detect,
	.edid_read = inno_hdmi_bridge_edid_read,
	.hdmi_clear_infoframe = inno_hdmi_bridge_clear_infoframe,
	.hdmi_write_infoframe = inno_hdmi_bridge_write_infoframe,
	.hdmi_clear_avi_infoframe = inno_hdmi_bridge_clear_avi_infoframe,
	.hdmi_write_avi_infoframe = inno_hdmi_bridge_write_avi_infoframe,
	.hdmi_clear_hdmi_infoframe = inno_hdmi_bridge_clear_hdmi_infoframe,
	.hdmi_write_hdmi_infoframe = inno_hdmi_bridge_write_hdmi_infoframe,
	.mode_valid = inno_hdmi_bridge_mode_valid,
};

+49 −46
Original line number Diff line number Diff line
@@ -759,34 +759,30 @@ it6263_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
	return MODE_OK;
}

static int it6263_hdmi_clear_infoframe(struct drm_bridge *bridge,
				       enum hdmi_infoframe_type type)
static int it6263_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
{
	struct it6263 *it = bridge_to_it6263(bridge);

	switch (type) {
	case HDMI_INFOFRAME_TYPE_AVI:
	regmap_write(it->hdmi_regmap, HDMI_REG_AVI_INFOFRM_CTRL, 0);
		break;
	case HDMI_INFOFRAME_TYPE_VENDOR:
		regmap_write(it->hdmi_regmap, HDMI_REG_PKT_NULL_CTRL, 0);
		break;
	default:
		dev_dbg(it->dev, "unsupported HDMI infoframe 0x%x\n", type);

	return 0;
}

static int it6263_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
	struct it6263 *it = bridge_to_it6263(bridge);

	regmap_write(it->hdmi_regmap, HDMI_REG_PKT_NULL_CTRL, 0);

	return 0;
}

static int it6263_hdmi_write_infoframe(struct drm_bridge *bridge,
				       enum hdmi_infoframe_type type,
static int it6263_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
					   const u8 *buffer, size_t len)
{
	struct it6263 *it = bridge_to_it6263(bridge);
	struct regmap *regmap = it->hdmi_regmap;

	switch (type) {
	case HDMI_INFOFRAME_TYPE_AVI:
	/* write the first AVI infoframe data byte chunk(DB1-DB5) */
	regmap_bulk_write(regmap, HDMI_REG_AVI_DB1,
			  &buffer[HDMI_INFOFRAME_HEADER_SIZE],
@@ -803,17 +799,22 @@ static int it6263_hdmi_write_infoframe(struct drm_bridge *bridge,

	regmap_write(regmap, HDMI_REG_AVI_INFOFRM_CTRL,
		     ENABLE_PKT | REPEAT_PKT);
		break;
	case HDMI_INFOFRAME_TYPE_VENDOR:

	return 0;
}

static int it6263_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
					    const u8 *buffer, size_t len)
{
	struct it6263 *it = bridge_to_it6263(bridge);
	struct regmap *regmap = it->hdmi_regmap;

	/* write header and payload */
	regmap_bulk_write(regmap, HDMI_REG_PKT_HB(0), buffer, len);

	regmap_write(regmap, HDMI_REG_PKT_NULL_CTRL,
		     ENABLE_PKT | REPEAT_PKT);
		break;
	default:
		dev_dbg(it->dev, "unsupported HDMI infoframe 0x%x\n", type);
	}


	return 0;
}
@@ -830,8 +831,10 @@ static const struct drm_bridge_funcs it6263_bridge_funcs = {
	.edid_read = it6263_bridge_edid_read,
	.atomic_get_input_bus_fmts = it6263_bridge_atomic_get_input_bus_fmts,
	.hdmi_tmds_char_rate_valid = it6263_hdmi_tmds_char_rate_valid,
	.hdmi_clear_infoframe = it6263_hdmi_clear_infoframe,
	.hdmi_write_infoframe = it6263_hdmi_write_infoframe,
	.hdmi_clear_avi_infoframe = it6263_hdmi_clear_avi_infoframe,
	.hdmi_write_avi_infoframe = it6263_hdmi_write_avi_infoframe,
	.hdmi_clear_hdmi_infoframe = it6263_hdmi_clear_hdmi_infoframe,
	.hdmi_write_hdmi_infoframe = it6263_hdmi_write_hdmi_infoframe,
};

static int it6263_probe(struct i2c_client *client)
+81 −62
Original line number Diff line number Diff line
@@ -843,85 +843,97 @@ lt9611_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
#define LT9611_INFOFRAME_AUDIO	0x02
#define LT9611_INFOFRAME_AVI	0x08
#define LT9611_INFOFRAME_SPD	0x10
#define LT9611_INFOFRAME_VENDOR	0x20
#define LT9611_INFOFRAME_HDMI	0x20

static int lt9611_hdmi_clear_infoframe(struct drm_bridge *bridge,
				       enum hdmi_infoframe_type type)
static int lt9611_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
{
	struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
	unsigned int mask;

	switch (type) {
	case HDMI_INFOFRAME_TYPE_AUDIO:
		mask = LT9611_INFOFRAME_AUDIO;
		break;
	regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AUDIO, 0);

	case HDMI_INFOFRAME_TYPE_AVI:
		mask = LT9611_INFOFRAME_AVI;
		break;
	return 0;
}

	case HDMI_INFOFRAME_TYPE_SPD:
		mask = LT9611_INFOFRAME_SPD;
		break;
static int lt9611_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
{
	struct lt9611 *lt9611 = bridge_to_lt9611(bridge);

	case HDMI_INFOFRAME_TYPE_VENDOR:
		mask = LT9611_INFOFRAME_VENDOR;
		break;
	regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AVI, 0);

	default:
		drm_dbg_driver(lt9611->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
		mask = 0;
		break;
	return 0;
}

	if (mask)
		regmap_update_bits(lt9611->regmap, 0x843d, mask, 0);
static int lt9611_hdmi_clear_spd_infoframe(struct drm_bridge *bridge)
{
	struct lt9611 *lt9611 = bridge_to_lt9611(bridge);

	regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_SPD, 0);

	return 0;
}

static int lt9611_hdmi_write_infoframe(struct drm_bridge *bridge,
				       enum hdmi_infoframe_type type,
static int lt9611_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
	struct lt9611 *lt9611 = bridge_to_lt9611(bridge);

	regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_HDMI, 0);

	return 0;
}

static int lt9611_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
					     const u8 *buffer, size_t len)
{
	struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
	unsigned int mask, addr;
	int i;

	switch (type) {
	case HDMI_INFOFRAME_TYPE_AUDIO:
		mask = LT9611_INFOFRAME_AUDIO;
		addr = 0x84b2;
		break;
	for (i = 0; i < len; i++)
		regmap_write(lt9611->regmap, 0x84b2 + i, buffer[i]);

	regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AUDIO, LT9611_INFOFRAME_AUDIO);

	return 0;
}

	case HDMI_INFOFRAME_TYPE_AVI:
		mask = LT9611_INFOFRAME_AVI;
		addr = 0x8440;
		break;
static int lt9611_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
					   const u8 *buffer, size_t len)
{
	struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
	int i;

	case HDMI_INFOFRAME_TYPE_SPD:
		mask = LT9611_INFOFRAME_SPD;
		addr = 0x8493;
		break;
	for (i = 0; i < len; i++)
		regmap_write(lt9611->regmap, 0x8440 + i, buffer[i]);

	case HDMI_INFOFRAME_TYPE_VENDOR:
		mask = LT9611_INFOFRAME_VENDOR;
		addr = 0x8474;
		break;
	regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AVI, LT9611_INFOFRAME_AVI);

	default:
		drm_dbg_driver(lt9611->bridge.dev, "Unsupported HDMI InfoFrame %x\n", type);
		mask = 0;
		break;
	return 0;
}

	if (mask) {
static int lt9611_hdmi_write_spd_infoframe(struct drm_bridge *bridge,
					   const u8 *buffer, size_t len)
{
	struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
	int i;

	for (i = 0; i < len; i++)
			regmap_write(lt9611->regmap, addr + i, buffer[i]);
		regmap_write(lt9611->regmap, 0x8493 + i, buffer[i]);

		regmap_update_bits(lt9611->regmap, 0x843d, mask, mask);
	regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_SPD, LT9611_INFOFRAME_SPD);

	return 0;
}

static int lt9611_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
					    const u8 *buffer, size_t len)
{
	struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
	int i;

	for (i = 0; i < len; i++)
		regmap_write(lt9611->regmap, 0x8474 + i, buffer[i]);

	regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_HDMI, LT9611_INFOFRAME_HDMI);

	return 0;
}

@@ -1003,8 +1015,14 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
	.atomic_get_input_bus_fmts = lt9611_atomic_get_input_bus_fmts,

	.hdmi_tmds_char_rate_valid = lt9611_hdmi_tmds_char_rate_valid,
	.hdmi_write_infoframe = lt9611_hdmi_write_infoframe,
	.hdmi_clear_infoframe = lt9611_hdmi_clear_infoframe,
	.hdmi_write_audio_infoframe = lt9611_hdmi_write_audio_infoframe,
	.hdmi_clear_audio_infoframe = lt9611_hdmi_clear_audio_infoframe,
	.hdmi_write_avi_infoframe = lt9611_hdmi_write_avi_infoframe,
	.hdmi_clear_avi_infoframe = lt9611_hdmi_clear_avi_infoframe,
	.hdmi_write_spd_infoframe = lt9611_hdmi_write_spd_infoframe,
	.hdmi_clear_spd_infoframe = lt9611_hdmi_clear_spd_infoframe,
	.hdmi_write_hdmi_infoframe = lt9611_hdmi_write_hdmi_infoframe,
	.hdmi_clear_hdmi_infoframe = lt9611_hdmi_clear_hdmi_infoframe,

	.hdmi_audio_startup = lt9611_hdmi_audio_startup,
	.hdmi_audio_prepare = lt9611_hdmi_audio_prepare,
@@ -1132,7 +1150,8 @@ static int lt9611_probe(struct i2c_client *client)
	lt9611->bridge.of_node = client->dev.of_node;
	lt9611->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
			     DRM_BRIDGE_OP_HPD | DRM_BRIDGE_OP_MODES |
			     DRM_BRIDGE_OP_HDMI | DRM_BRIDGE_OP_HDMI_AUDIO;
			     DRM_BRIDGE_OP_HDMI | DRM_BRIDGE_OP_HDMI_AUDIO |
			     DRM_BRIDGE_OP_HDMI_SPD_INFOFRAME;
	lt9611->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
	lt9611->bridge.vendor = "Lontium";
	lt9611->bridge.product = "LT9611";
+74 −38
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
#include <drm/drm_modes.h>
#include <drm/drm_print.h>

#include <media/cec.h>

@@ -956,57 +957,85 @@ dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge,
	return MODE_OK;
}

static int dw_hdmi_qp_bridge_clear_infoframe(struct drm_bridge *bridge,
					     enum hdmi_infoframe_type type)
static int dw_hdmi_qp_bridge_clear_avi_infoframe(struct drm_bridge *bridge)
{
	struct dw_hdmi_qp *hdmi = bridge->driver_private;

	switch (type) {
	case HDMI_INFOFRAME_TYPE_AVI:
	dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_AVI_TX_EN | PKTSCHED_GCP_TX_EN,
		       PKTSCHED_PKT_EN);
		break;

	case HDMI_INFOFRAME_TYPE_DRM:
	return 0;
}

static int dw_hdmi_qp_bridge_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
	/* FIXME: add support for this InfoFrame */

	drm_warn_once(bridge->encoder->dev, "HDMI VSI not supported\n");

	return 0;
}

static int dw_hdmi_qp_bridge_clear_hdr_drm_infoframe(struct drm_bridge *bridge)
{
	struct dw_hdmi_qp *hdmi = bridge->driver_private;

	dw_hdmi_qp_mod(hdmi, 0, PKTSCHED_DRMI_TX_EN, PKTSCHED_PKT_EN);
		break;

	case HDMI_INFOFRAME_TYPE_AUDIO:
	return 0;
}

static int dw_hdmi_qp_bridge_clear_audio_infoframe(struct drm_bridge *bridge)
{
	struct dw_hdmi_qp *hdmi = bridge->driver_private;

	dw_hdmi_qp_mod(hdmi, 0,
		       PKTSCHED_ACR_TX_EN |
		       PKTSCHED_AUDS_TX_EN |
		       PKTSCHED_AUDI_TX_EN,
		       PKTSCHED_PKT_EN);
		break;
	default:
		dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
	}

	return 0;
}

static int dw_hdmi_qp_bridge_write_infoframe(struct drm_bridge *bridge,
					     enum hdmi_infoframe_type type,
static int dw_hdmi_qp_bridge_write_avi_infoframe(struct drm_bridge *bridge,
						 const u8 *buffer, size_t len)
{
	struct dw_hdmi_qp *hdmi = bridge->driver_private;

	dw_hdmi_qp_bridge_clear_infoframe(bridge, type);
	dw_hdmi_qp_bridge_clear_avi_infoframe(bridge);

	switch (type) {
	case HDMI_INFOFRAME_TYPE_AVI:
	return dw_hdmi_qp_config_avi_infoframe(hdmi, buffer, len);
}

	case HDMI_INFOFRAME_TYPE_DRM:
		return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len);
static int dw_hdmi_qp_bridge_write_hdmi_infoframe(struct drm_bridge *bridge,
						  const u8 *buffer, size_t len)
{
	dw_hdmi_qp_bridge_clear_hdmi_infoframe(bridge);

	case HDMI_INFOFRAME_TYPE_AUDIO:
		return dw_hdmi_qp_config_audio_infoframe(hdmi, buffer, len);
	/* FIXME: add support for the HDMI VSI */

	default:
		dev_dbg(hdmi->dev, "Unsupported infoframe type %x\n", type);
	return 0;
}

static int dw_hdmi_qp_bridge_write_hdr_drm_infoframe(struct drm_bridge *bridge,
						     const u8 *buffer, size_t len)
{
	struct dw_hdmi_qp *hdmi = bridge->driver_private;

	dw_hdmi_qp_bridge_clear_hdr_drm_infoframe(bridge);

	return dw_hdmi_qp_config_drm_infoframe(hdmi, buffer, len);
}

static int dw_hdmi_qp_bridge_write_audio_infoframe(struct drm_bridge *bridge,
						   const u8 *buffer, size_t len)
{
	struct dw_hdmi_qp *hdmi = bridge->driver_private;

	dw_hdmi_qp_bridge_clear_audio_infoframe(bridge);

	return dw_hdmi_qp_config_audio_infoframe(hdmi, buffer, len);
}

#ifdef CONFIG_DRM_DW_HDMI_QP_CEC
@@ -1191,8 +1220,14 @@ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = {
	.detect = dw_hdmi_qp_bridge_detect,
	.edid_read = dw_hdmi_qp_bridge_edid_read,
	.hdmi_tmds_char_rate_valid = dw_hdmi_qp_bridge_tmds_char_rate_valid,
	.hdmi_clear_infoframe = dw_hdmi_qp_bridge_clear_infoframe,
	.hdmi_write_infoframe = dw_hdmi_qp_bridge_write_infoframe,
	.hdmi_clear_avi_infoframe = dw_hdmi_qp_bridge_clear_avi_infoframe,
	.hdmi_write_avi_infoframe = dw_hdmi_qp_bridge_write_avi_infoframe,
	.hdmi_clear_hdmi_infoframe = dw_hdmi_qp_bridge_clear_hdmi_infoframe,
	.hdmi_write_hdmi_infoframe = dw_hdmi_qp_bridge_write_hdmi_infoframe,
	.hdmi_clear_hdr_drm_infoframe = dw_hdmi_qp_bridge_clear_hdr_drm_infoframe,
	.hdmi_write_hdr_drm_infoframe = dw_hdmi_qp_bridge_write_hdr_drm_infoframe,
	.hdmi_clear_audio_infoframe = dw_hdmi_qp_bridge_clear_audio_infoframe,
	.hdmi_write_audio_infoframe = dw_hdmi_qp_bridge_write_audio_infoframe,
	.hdmi_audio_startup = dw_hdmi_qp_audio_enable,
	.hdmi_audio_shutdown = dw_hdmi_qp_audio_disable,
	.hdmi_audio_prepare = dw_hdmi_qp_audio_prepare,
@@ -1306,7 +1341,8 @@ struct dw_hdmi_qp *dw_hdmi_qp_bind(struct platform_device *pdev,
	hdmi->bridge.ops = DRM_BRIDGE_OP_DETECT |
			   DRM_BRIDGE_OP_EDID |
			   DRM_BRIDGE_OP_HDMI |
			   DRM_BRIDGE_OP_HDMI_AUDIO;
			   DRM_BRIDGE_OP_HDMI_AUDIO |
			   DRM_BRIDGE_OP_HDMI_HDR_DRM_INFOFRAME;
	if (!hdmi->no_hpd)
		hdmi->bridge.ops |= DRM_BRIDGE_OP_HPD;
	hdmi->bridge.of_node = pdev->dev.of_node;
Loading