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

Commit 308342e5 authored by Jin Li's avatar Jin Li Committed by Ray Zhang
Browse files

drm/msm: add info frame configuration for hdmi controller



When HDMI controller is configured as non-DVI with CEA mode, SDE
driver needs to program AVI, VSIF and SPD information into HW to
generate correct info frame.

CRs-Fixed: 2010135
Change-Id: Ib218761c63b13aa229fc24519ceb9ccd0bd34ce2
Signed-off-by: default avatarJin Li <jinl@codeaurora.org>
Signed-off-by: default avatarRay Zhang <rayz@codeaurora.org>
parent 618520c1
Loading
Loading
Loading
Loading
+147 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "drm_edid.h"
#include "sde_kms.h"
#include "sde_hdmi.h"
#include "hdmi.h"
@@ -26,6 +27,20 @@ struct sde_hdmi_bridge {
};
#define to_hdmi_bridge(x) container_of(x, struct sde_hdmi_bridge, base)

/* for AVI program */
#define HDMI_AVI_INFOFRAME_BUFFER_SIZE \
	(HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE)
#define HDMI_VS_INFOFRAME_BUFFER_SIZE (HDMI_INFOFRAME_HEADER_SIZE + 6)
#define HDMI_SPD_INFOFRAME_BUFFER_SIZE \
	(HDMI_INFOFRAME_HEADER_SIZE + HDMI_SPD_INFOFRAME_SIZE)
#define HDMI_DEFAULT_VENDOR_NAME "unknown"
#define HDMI_DEFAULT_PRODUCT_NAME "msm"
#define LEFT_SHIFT_BYTE(x) ((x) << 8)
#define LEFT_SHIFT_WORD(x) ((x) << 16)
#define LEFT_SHIFT_24BITS(x) ((x) << 24)
#define HDMI_AVI_IFRAME_LINE_NUMBER 1
#define HDMI_VENDOR_IFRAME_LINE_NUMBER 3

void _sde_hdmi_bridge_destroy(struct drm_bridge *bridge)
{
}
@@ -138,6 +153,124 @@ static void _sde_hdmi_bridge_post_disable(struct drm_bridge *bridge)
	}
}

static void _sde_hdmi_bridge_set_avi_infoframe(struct hdmi *hdmi,
	const struct drm_display_mode *mode)
{
	u8 avi_iframe[HDMI_AVI_INFOFRAME_BUFFER_SIZE] = {0};
	u8 *avi_frame = &avi_iframe[HDMI_INFOFRAME_HEADER_SIZE];
	u8 checksum;
	u32 reg_val;
	struct hdmi_avi_infoframe info;

	drm_hdmi_avi_infoframe_from_display_mode(&info, mode);
	hdmi_avi_infoframe_pack(&info, avi_iframe, sizeof(avi_iframe));
	checksum = avi_iframe[HDMI_INFOFRAME_HEADER_SIZE - 1];

	reg_val = checksum |
		LEFT_SHIFT_BYTE(avi_frame[0]) |
		LEFT_SHIFT_WORD(avi_frame[1]) |
		LEFT_SHIFT_24BITS(avi_frame[2]);
	hdmi_write(hdmi, REG_HDMI_AVI_INFO(0), reg_val);

	reg_val = avi_frame[3] |
		LEFT_SHIFT_BYTE(avi_frame[4]) |
		LEFT_SHIFT_WORD(avi_frame[5]) |
		LEFT_SHIFT_24BITS(avi_frame[6]);
	hdmi_write(hdmi, REG_HDMI_AVI_INFO(1), reg_val);

	reg_val = avi_frame[7] |
		LEFT_SHIFT_BYTE(avi_frame[8]) |
		LEFT_SHIFT_WORD(avi_frame[9]) |
		LEFT_SHIFT_24BITS(avi_frame[10]);
	hdmi_write(hdmi, REG_HDMI_AVI_INFO(2), reg_val);

	reg_val = avi_frame[11] |
		LEFT_SHIFT_BYTE(avi_frame[12]) |
		LEFT_SHIFT_24BITS(avi_iframe[1]);
	hdmi_write(hdmi, REG_HDMI_AVI_INFO(3), reg_val);

	/* AVI InfFrame enable (every frame) */
	hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0,
		hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0) | BIT(1) | BIT(0));

	reg_val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
	reg_val &= ~0x3F;
	reg_val |= HDMI_AVI_IFRAME_LINE_NUMBER;
	hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, reg_val);
}

static void _sde_hdmi_bridge_set_vs_infoframe(struct hdmi *hdmi,
	const struct drm_display_mode *mode)
{
	u8 vs_iframe[HDMI_VS_INFOFRAME_BUFFER_SIZE] = {0};
	u32 reg_val;
	struct hdmi_vendor_infoframe info;
	int rc = 0;

	rc = drm_hdmi_vendor_infoframe_from_display_mode(&info, mode);
	if (rc < 0) {
		SDE_DEBUG("don't send vendor infoframe\n");
		return;
	}
	hdmi_vendor_infoframe_pack(&info, vs_iframe, sizeof(vs_iframe));

	reg_val = (info.s3d_struct << 24) | (info.vic << 16) |
			(vs_iframe[3] << 8) | (vs_iframe[7] << 5) |
			vs_iframe[2];
	hdmi_write(hdmi, REG_HDMI_VENSPEC_INFO0, reg_val);

	/* vendor specific info-frame enable (every frame) */
	hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL0,
		hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL0) | BIT(13) | BIT(12));

	reg_val = hdmi_read(hdmi, REG_HDMI_INFOFRAME_CTRL1);
	reg_val &= ~0x3F000000;
	reg_val |= (HDMI_VENDOR_IFRAME_LINE_NUMBER << 24);
	hdmi_write(hdmi, REG_HDMI_INFOFRAME_CTRL1, reg_val);
}

static void _sde_hdmi_bridge_set_spd_infoframe(struct hdmi *hdmi,
	const struct drm_display_mode *mode)
{
	u8 spd_iframe[HDMI_SPD_INFOFRAME_BUFFER_SIZE] = {0};
	u32 packet_payload, packet_control, packet_header;
	struct hdmi_spd_infoframe info;
	int i;

	/* Need to query vendor and product name from platform setup */
	hdmi_spd_infoframe_init(&info, HDMI_DEFAULT_VENDOR_NAME,
		HDMI_DEFAULT_PRODUCT_NAME);
	hdmi_spd_infoframe_pack(&info, spd_iframe, sizeof(spd_iframe));

	packet_header = spd_iframe[0]
			| LEFT_SHIFT_BYTE(spd_iframe[1] & 0x7f)
			| LEFT_SHIFT_WORD(spd_iframe[2] & 0x7f);
	hdmi_write(hdmi, REG_HDMI_GENERIC1_HDR, packet_header);

	for (i = 0; i < MAX_REG_HDMI_GENERIC1_INDEX; i++) {
		packet_payload = spd_iframe[3 + i * 4]
			| LEFT_SHIFT_BYTE(spd_iframe[4 + i * 4] & 0x7f)
			| LEFT_SHIFT_WORD(spd_iframe[5 + i * 4] & 0x7f)
			| LEFT_SHIFT_24BITS(spd_iframe[6 + i * 4] & 0x7f);
		hdmi_write(hdmi, REG_HDMI_GENERIC1(i), packet_payload);
	}

	packet_payload = (spd_iframe[27] & 0x7f)
			| LEFT_SHIFT_BYTE(spd_iframe[28] & 0x7f);
	hdmi_write(hdmi, REG_HDMI_GENERIC1(MAX_REG_HDMI_GENERIC1_INDEX),
		packet_payload);

	/*
	 * GENERIC1_LINE | GENERIC1_CONT | GENERIC1_SEND
	 * Setup HDMI TX generic packet control
	 * Enable this packet to transmit every frame
	 * Enable HDMI TX engine to transmit Generic packet 1
	 */
	packet_control = hdmi_read(hdmi, REG_HDMI_GEN_PKT_CTRL);
	packet_control |= ((0x1 << 24) | (1 << 5) | (1 << 4));
	hdmi_write(hdmi, REG_HDMI_GEN_PKT_CTRL, packet_control);
}

static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
		 struct drm_display_mode *mode,
		 struct drm_display_mode *adjusted_mode)
@@ -196,6 +329,20 @@ static void _sde_hdmi_bridge_mode_set(struct drm_bridge *bridge,
	DRM_DEBUG("frame_ctrl=%08x\n", frame_ctrl);
	hdmi_write(hdmi, REG_HDMI_FRAME_CTRL, frame_ctrl);

	/*
	 * Setup info frame
	 * Current drm_edid driver doesn't have all CEA formats defined in
	 * latest CEA-861(CTA-861) spec. So, don't check if mode is CEA mode
	 * in here. Once core framework is updated, the check needs to be
	 * added back.
	 */
	if (hdmi->hdmi_mode) {
		_sde_hdmi_bridge_set_avi_infoframe(hdmi, mode);
		_sde_hdmi_bridge_set_vs_infoframe(hdmi, mode);
		_sde_hdmi_bridge_set_spd_infoframe(hdmi, mode);
		DRM_DEBUG("hdmi setup info frame\n");
	}

	hdmi_audio_update(hdmi);
}

+3 −0
Original line number Diff line number Diff line
@@ -111,6 +111,8 @@ static inline uint32_t HDMI_ACR_PKT_CTRL_N_MULTIPLIER(uint32_t val)
#define HDMI_INFOFRAME_CTRL0_AUDIO_INFO_SOURCE			0x00000040
#define HDMI_INFOFRAME_CTRL0_AUDIO_INFO_UPDATE			0x00000080

#define REG_HDMI_INFOFRAME_CTRL1				0x00000030

#define REG_HDMI_GEN_PKT_CTRL					0x00000034
#define HDMI_GEN_PKT_CTRL_GENERIC0_SEND				0x00000001
#define HDMI_GEN_PKT_CTRL_GENERIC0_CONT				0x00000002
@@ -150,6 +152,7 @@ static inline uint32_t REG_HDMI_GENERIC0(uint32_t i0) { return 0x00000088 + 0x4*

#define REG_HDMI_GENERIC1_HDR					0x000000a4

#define MAX_REG_HDMI_GENERIC1_INDEX				6
static inline uint32_t REG_HDMI_GENERIC1(uint32_t i0) { return 0x000000a8 + 0x4*i0; }

static inline uint32_t REG_HDMI_ACR(enum hdmi_acr_cts i0) { return 0x000000c4 + 0x8*i0; }