Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cffe1e89 authored by Arnaud Pouliquen's avatar Arnaud Pouliquen Committed by Benjamin Gaignard
Browse files

drm: sti: HDMI add audio infoframe



Add a default audio infoframe for HDMI compliance

Signed-off-by: default avatarArnaud Pouliquen <arnaud.pouliquen@st.com>
parent 8adb5776
Loading
Loading
Loading
Loading
+137 −42
Original line number Diff line number Diff line
@@ -42,8 +42,17 @@
#define HDMI_SW_DI_1_PKT_WORD5          0x0228
#define HDMI_SW_DI_1_PKT_WORD6          0x022C
#define HDMI_SW_DI_CFG                  0x0230
#define HDMI_SW_DI_2_HEAD_WORD          0x0600
#define HDMI_SW_DI_2_PKT_WORD0          0x0604
#define HDMI_SW_DI_2_PKT_WORD1          0x0608
#define HDMI_SW_DI_2_PKT_WORD2          0x060C
#define HDMI_SW_DI_2_PKT_WORD3          0x0610
#define HDMI_SW_DI_2_PKT_WORD4          0x0614
#define HDMI_SW_DI_2_PKT_WORD5          0x0618
#define HDMI_SW_DI_2_PKT_WORD6          0x061C

#define HDMI_IFRAME_SLOT_AVI            1
#define HDMI_IFRAME_SLOT_AUDIO          2

#define  XCAT(prefix, x, suffix)        prefix ## x ## suffix
#define  HDMI_SW_DI_N_HEAD_WORD(x)      XCAT(HDMI_SW_DI_, x, _HEAD_WORD)
@@ -99,6 +108,10 @@

#define HDMI_STA_SW_RST                 BIT(1)

#define HDMI_INFOFRAME_HEADER_TYPE(x)    (((x) & 0xff) <<  0)
#define HDMI_INFOFRAME_HEADER_VERSION(x) (((x) & 0xff) <<  8)
#define HDMI_INFOFRAME_HEADER_LEN(x)     (((x) & 0x0f) << 16)

struct sti_hdmi_connector {
	struct drm_connector drm_connector;
	struct drm_encoder *encoder;
@@ -227,6 +240,90 @@ static void hdmi_config(struct sti_hdmi *hdmi)
	hdmi_write(hdmi, conf, HDMI_CFG);
}

/**
 * Helper to concatenate infoframe in 32 bits word
 *
 * @ptr: pointer on the hdmi internal structure
 * @data: infoframe to write
 * @size: size to write
 */
static inline unsigned int hdmi_infoframe_subpack(const u8 *ptr, size_t size)
{
	unsigned long value = 0;
	size_t i;

	for (i = size; i > 0; i--)
		value = (value << 8) | ptr[i - 1];

	return value;
}

/**
 * Helper to write info frame
 *
 * @hdmi: pointer on the hdmi internal structure
 * @data: infoframe to write
 * @size: size to write
 */
static void hdmi_infoframe_write_infopack(struct sti_hdmi *hdmi, const u8 *data)
{
	const u8 *ptr = data;
	u32 val, slot, mode, i;
	u32 head_offset, pack_offset;
	size_t size;

	switch (*ptr) {
	case HDMI_INFOFRAME_TYPE_AVI:
		slot = HDMI_IFRAME_SLOT_AVI;
		mode = HDMI_IFRAME_FIELD;
		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI);
		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI);
		size = HDMI_AVI_INFOFRAME_SIZE;
		break;

	case HDMI_INFOFRAME_TYPE_AUDIO:
		slot = HDMI_IFRAME_SLOT_AUDIO;
		mode = HDMI_IFRAME_FRAME;
		head_offset = HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AUDIO);
		pack_offset = HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AUDIO);
		size = HDMI_AUDIO_INFOFRAME_SIZE;
		break;

	default:
		DRM_ERROR("unsupported infoframe type: %#x\n", *ptr);
		return;
	}

	/* Disable transmission slot for updated infoframe */
	val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
	val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, slot);
	hdmi_write(hdmi, val, HDMI_SW_DI_CFG);

	val = HDMI_INFOFRAME_HEADER_TYPE(*ptr++);
	val |= HDMI_INFOFRAME_HEADER_VERSION(*ptr++);
	val |= HDMI_INFOFRAME_HEADER_LEN(*ptr++);
	writel(val, hdmi->regs + head_offset);

	/*
	 * Each subpack contains 4 bytes
	 * The First Bytes of the first subpacket must contain the checksum
	 * Packet size in increase by one.
	 */
	for (i = 0; i < size; i += sizeof(u32)) {
		size_t num;

		num = min_t(size_t, size - i, sizeof(u32));
		val = hdmi_infoframe_subpack(ptr, num);
		ptr += sizeof(u32);
		writel(val, hdmi->regs + pack_offset + i);
	}

	/* Enable transmission slot for updated infoframe */
	val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
	val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, slot);
	hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
}

/**
 * Prepare and configure the AVI infoframe
 *
@@ -243,8 +340,6 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
	struct drm_display_mode *mode = &hdmi->mode;
	struct hdmi_avi_infoframe infoframe;
	u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
	u8 *frame = buffer + HDMI_INFOFRAME_HEADER_SIZE;
	u32 val;
	int ret;

	DRM_DEBUG_DRIVER("\n");
@@ -266,47 +361,43 @@ static int hdmi_avi_infoframe_config(struct sti_hdmi *hdmi)
		return ret;
	}

	/* Disable transmission slot for AVI infoframe */
	val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
	val &= ~HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_MASK, HDMI_IFRAME_SLOT_AVI);
	hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
	hdmi_infoframe_write_infopack(hdmi, buffer);

	return 0;
}

	/* Infoframe header */
	val =  buffer[0];
	val |= buffer[1] << 8;
	val |= buffer[2] << 16;
	hdmi_write(hdmi, val, HDMI_SW_DI_N_HEAD_WORD(HDMI_IFRAME_SLOT_AVI));

	/* Infoframe packet bytes */
	val =  buffer[3];
	val |= *(frame++) << 8;
	val |= *(frame++) << 16;
	val |= *(frame++) << 24;
	hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD0(HDMI_IFRAME_SLOT_AVI));

	val =  *(frame++);
	val |= *(frame++) << 8;
	val |= *(frame++) << 16;
	val |= *(frame++) << 24;
	hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD1(HDMI_IFRAME_SLOT_AVI));

	val =  *(frame++);
	val |= *(frame++) << 8;
	val |= *(frame++) << 16;
	val |= *(frame++) << 24;
	hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD2(HDMI_IFRAME_SLOT_AVI));

	val = *(frame++);
	val |= *(frame) << 8;
	hdmi_write(hdmi, val, HDMI_SW_DI_N_PKT_WORD3(HDMI_IFRAME_SLOT_AVI));

	/* Enable transmission slot for AVI infoframe
	 * According to the hdmi specification, AVI infoframe should be
	 * transmitted at least once per two video fields
/**
 * Prepare and configure the AUDIO infoframe
 *
 * AUDIO infoframe are transmitted once per frame and
 * contains information about HDMI transmission mode such as audio codec,
 * sample size, ...
 *
 * @hdmi: pointer on the hdmi internal structure
 *
 * Return negative value if error occurs
 */
	val = hdmi_read(hdmi, HDMI_SW_DI_CFG);
	val |= HDMI_IFRAME_CFG_DI_N(HDMI_IFRAME_FIELD, HDMI_IFRAME_SLOT_AVI);
	hdmi_write(hdmi, val, HDMI_SW_DI_CFG);
static int hdmi_audio_infoframe_config(struct sti_hdmi *hdmi)
{
	struct hdmi_audio_infoframe infofame;
	u8 buffer[HDMI_INFOFRAME_SIZE(AUDIO)];
	int ret;

	ret = hdmi_audio_infoframe_init(&infofame);
	if (ret < 0) {
		DRM_ERROR("failed to setup audio infoframe: %d\n", ret);
		return ret;
	}

	infofame.channels = 2;

	ret = hdmi_audio_infoframe_pack(&infofame, buffer, sizeof(buffer));
	if (ret < 0) {
		DRM_ERROR("failed to pack audio infoframe: %d\n", ret);
		return ret;
	}

	hdmi_infoframe_write_infopack(hdmi, buffer);

	return 0;
}
@@ -427,6 +518,10 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge)
	if (hdmi_avi_infoframe_config(hdmi))
		DRM_ERROR("Unable to configure AVI infoframe\n");

	/* Program AUDIO infoframe */
	if (hdmi_audio_infoframe_config(hdmi))
		DRM_ERROR("Unable to configure AUDIO infoframe\n");

	/* Sw reset */
	hdmi_swreset(hdmi);
}