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

Commit 39ef398e authored by Padmanabhan Komanduru's avatar Padmanabhan Komanduru Committed by Steve Cohen
Browse files

drm/msm/dp: add support for SPD infoframe for DisplayPort



Add changes to communicate the source vendor name and the product
description to the sink using Generic-1 register set for Display Port.

Change-Id: I29688d2f8bb609adf652e3a8fd7c3e3389084360
Signed-off-by: default avatarPadmanabhan Komanduru <pkomandu@codeaurora.org>
parent bcf61ae4
Loading
Loading
Loading
Loading
+14 −86
Original line number Diff line number Diff line
@@ -23,13 +23,6 @@
#include "dp_audio.h"
#include "dp_panel.h"

#define HEADER_BYTE_2_BIT	 0
#define PARITY_BYTE_2_BIT	 8
#define HEADER_BYTE_1_BIT	16
#define PARITY_BYTE_1_BIT	24
#define HEADER_BYTE_3_BIT	16
#define PARITY_BYTE_3_BIT	24

struct dp_audio_private {
	struct platform_device *ext_pdev;
	struct platform_device *pdev;
@@ -50,71 +43,6 @@ struct dp_audio_private {
	struct dp_audio dp_audio;
};

static u8 dp_audio_get_g0_value(u8 data)
{
	u8 c[4];
	u8 g[4];
	u8 ret_data = 0;
	u8 i;

	for (i = 0; i < 4; i++)
		c[i] = (data >> i) & 0x01;

	g[0] = c[3];
	g[1] = c[0] ^ c[3];
	g[2] = c[1];
	g[3] = c[2];

	for (i = 0; i < 4; i++)
		ret_data = ((g[i] & 0x01) << i) | ret_data;

	return ret_data;
}

static u8 dp_audio_get_g1_value(u8 data)
{
	u8 c[4];
	u8 g[4];
	u8 ret_data = 0;
	u8 i;

	for (i = 0; i < 4; i++)
		c[i] = (data >> i) & 0x01;

	g[0] = c[0] ^ c[3];
	g[1] = c[0] ^ c[1] ^ c[3];
	g[2] = c[1] ^ c[2];
	g[3] = c[2] ^ c[3];

	for (i = 0; i < 4; i++)
		ret_data = ((g[i] & 0x01) << i) | ret_data;

	return ret_data;
}

static u8 dp_audio_calculate_parity(u32 data)
{
	u8 x0 = 0;
	u8 x1 = 0;
	u8 ci = 0;
	u8 iData = 0;
	u8 i = 0;
	u8 parity_byte;
	u8 num_byte = (data & 0xFF00) > 0 ? 8 : 2;

	for (i = 0; i < num_byte; i++) {
		iData = (data >> i*4) & 0xF;

		ci = iData ^ x1;
		x1 = x0 ^ dp_audio_get_g1_value(ci);
		x0 = dp_audio_get_g0_value(ci);
	}

	parity_byte = x1 | (x0 << 4);

	return parity_byte;
}

static u32 dp_audio_get_header(struct dp_catalog_audio *catalog,
		enum dp_catalog_audio_sdp_type sdp,
		enum dp_catalog_audio_header_type header)
@@ -148,7 +76,7 @@ static void dp_audio_stream_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_1);

	new_value = 0x02;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_1_BIT)
			| (parity_byte << PARITY_BYTE_1_BIT));
	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
@@ -160,7 +88,7 @@ static void dp_audio_stream_sdp(struct dp_audio_private *audio)
	value = dp_audio_get_header(catalog,
			DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_2);
	new_value = value;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_2_BIT)
			| (parity_byte << PARITY_BYTE_2_BIT));
	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
@@ -174,7 +102,7 @@ static void dp_audio_stream_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_3);

	new_value = audio->channels - 1;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_3_BIT)
			| (parity_byte << PARITY_BYTE_3_BIT));
	pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
@@ -195,7 +123,7 @@ static void dp_audio_timestamp_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_1);

	new_value = 0x1;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_1_BIT)
			| (parity_byte << PARITY_BYTE_1_BIT));
	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
@@ -208,7 +136,7 @@ static void dp_audio_timestamp_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_2);

	new_value = 0x17;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_2_BIT)
			| (parity_byte << PARITY_BYTE_2_BIT));
	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
@@ -221,7 +149,7 @@ static void dp_audio_timestamp_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_3);

	new_value = (0x0 | (0x11 << 2));
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_3_BIT)
			| (parity_byte << PARITY_BYTE_3_BIT));
	pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
@@ -241,7 +169,7 @@ static void dp_audio_infoframe_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_1);

	new_value = 0x84;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_1_BIT)
			| (parity_byte << PARITY_BYTE_1_BIT));
	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
@@ -254,7 +182,7 @@ static void dp_audio_infoframe_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_2);

	new_value = 0x1b;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_2_BIT)
			| (parity_byte << PARITY_BYTE_2_BIT));
	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
@@ -267,7 +195,7 @@ static void dp_audio_infoframe_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_3);

	new_value = (0x0 | (0x11 << 2));
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_3_BIT)
			| (parity_byte << PARITY_BYTE_3_BIT));
	pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
@@ -287,7 +215,7 @@ static void dp_audio_copy_management_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_1);

	new_value = 0x05;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_1_BIT)
			| (parity_byte << PARITY_BYTE_1_BIT));
	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
@@ -300,7 +228,7 @@ static void dp_audio_copy_management_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_2);

	new_value = 0x0F;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_2_BIT)
			| (parity_byte << PARITY_BYTE_2_BIT));
	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
@@ -313,7 +241,7 @@ static void dp_audio_copy_management_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_3);

	new_value = 0x0;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_3_BIT)
			| (parity_byte << PARITY_BYTE_3_BIT));
	pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
@@ -333,7 +261,7 @@ static void dp_audio_isrc_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_1);

	new_value = 0x06;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_1_BIT)
			| (parity_byte << PARITY_BYTE_1_BIT));
	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
@@ -346,7 +274,7 @@ static void dp_audio_isrc_sdp(struct dp_audio_private *audio)
			DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_2);

	new_value = 0x0F;
	parity_byte = dp_audio_calculate_parity(new_value);
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_2_BIT)
			| (parity_byte << PARITY_BYTE_2_BIT));
	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+126 −0
Original line number Diff line number Diff line
@@ -1312,6 +1312,131 @@ static void dp_catalog_audio_enable(struct dp_catalog_audio *audio)
	wmb();
}

static void dp_catalog_config_spd_header(struct dp_catalog_panel *panel)
{
	struct dp_catalog_private *catalog;
	void __iomem *base;
	u32 value, new_value;
	u8 parity_byte;

	if (!panel)
		return;

	dp_catalog_get_priv(panel);
	base = catalog->io->dp_link.base;

	/* Config header and parity byte 1 */
	value = dp_read(base + MMSS_DP_GENERIC1_0);

	new_value = 0x83;
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_1_BIT)
			| (parity_byte << PARITY_BYTE_1_BIT));
	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
			value, parity_byte);
	dp_write(base + MMSS_DP_GENERIC1_0, value);

	/* Config header and parity byte 2 */
	value = dp_read(base + MMSS_DP_GENERIC1_1);

	new_value = 0x1b;
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_2_BIT)
			| (parity_byte << PARITY_BYTE_2_BIT));
	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
			value, parity_byte);
	dp_write(base + MMSS_DP_GENERIC1_1, value);

	/* Config header and parity byte 3 */
	value = dp_read(base + MMSS_DP_GENERIC1_1);

	new_value = (0x0 | (0x12 << 2));
	parity_byte = dp_header_get_parity(new_value);
	value |= ((new_value << HEADER_BYTE_3_BIT)
			| (parity_byte << PARITY_BYTE_3_BIT));
	pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
			new_value, parity_byte);
	dp_write(base + MMSS_DP_GENERIC1_1, value);
}

static void dp_catalog_panel_config_spd(struct dp_catalog_panel *panel)
{
	struct dp_catalog_private *catalog;
	void __iomem *base;
	u32 spd_cfg = 0, spd_cfg2 = 0;
	u8 *vendor = NULL, *product = NULL;
	/*
	 * Source Device Information
	 * 00h unknown
	 * 01h Digital STB
	 * 02h DVD
	 * 03h D-VHS
	 * 04h HDD Video
	 * 05h DVC
	 * 06h DSC
	 * 07h Video CD
	 * 08h Game
	 * 09h PC general
	 * 0ah Bluray-Disc
	 * 0bh Super Audio CD
	 * 0ch HD DVD
	 * 0dh PMP
	 * 0eh-ffh reserved
	 */
	u32 device_type = 0;

	if (!panel)
		return;

	dp_catalog_get_priv(panel);
	base = catalog->io->dp_link.base;

	dp_catalog_config_spd_header(panel);

	vendor = panel->spd_vendor_name;
	product = panel->spd_product_description;

	dp_write(base + MMSS_DP_GENERIC1_2, ((vendor[0] & 0x7f) |
				((vendor[1] & 0x7f) << 8) |
				((vendor[2] & 0x7f) << 16) |
				((vendor[3] & 0x7f) << 24)));
	dp_write(base + MMSS_DP_GENERIC1_3, ((vendor[4] & 0x7f) |
				((vendor[5] & 0x7f) << 8) |
				((vendor[6] & 0x7f) << 16) |
				((vendor[7] & 0x7f) << 24)));
	dp_write(base + MMSS_DP_GENERIC1_4, ((product[0] & 0x7f) |
			((product[1] & 0x7f) << 8) |
			((product[2] & 0x7f) << 16) |
			((product[3] & 0x7f) << 24)));
	dp_write(base + MMSS_DP_GENERIC1_5, ((product[4] & 0x7f) |
			((product[5] & 0x7f) << 8) |
			((product[6] & 0x7f) << 16) |
			((product[7] & 0x7f) << 24)));
	dp_write(base + MMSS_DP_GENERIC1_6, ((product[8] & 0x7f) |
			((product[9] & 0x7f) << 8) |
			((product[10] & 0x7f) << 16) |
			((product[11] & 0x7f) << 24)));
	dp_write(base + MMSS_DP_GENERIC1_7, ((product[12] & 0x7f) |
			((product[13] & 0x7f) << 8) |
			((product[14] & 0x7f) << 16) |
			((product[15] & 0x7f) << 24)));
	dp_write(base + MMSS_DP_GENERIC1_8, device_type);
	dp_write(base + MMSS_DP_GENERIC1_9, 0x00);

	spd_cfg = dp_read(base + MMSS_DP_SDP_CFG);
	/* GENERIC1_SDP for SPD Infoframe */
	spd_cfg |= BIT(18);
	dp_write(base + MMSS_DP_SDP_CFG, spd_cfg);

	spd_cfg2 = dp_read(base + MMSS_DP_SDP_CFG2);
	/* 28 data bytes for SPD Infoframe with GENERIC1 set */
	spd_cfg2 |= BIT(17);
	dp_write(base + MMSS_DP_SDP_CFG2, spd_cfg2);

	dp_write(base + MMSS_DP_SDP_CFG3, 0x1);
	dp_write(base + MMSS_DP_SDP_CFG3, 0x0);
}

struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io)
{
	int rc = 0;
@@ -1364,6 +1489,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io)
		.timing_cfg = dp_catalog_panel_timing_cfg,
		.config_hdr = dp_catalog_panel_config_hdr,
		.tpg_config = dp_catalog_panel_tpg_cfg,
		.config_spd = dp_catalog_panel_config_spd,
	};

	if (!io) {
+75 −0
Original line number Diff line number Diff line
@@ -109,6 +109,13 @@ struct dp_catalog_ctrl {
	u32 (*read_phy_pattern)(struct dp_catalog_ctrl *ctrl);
};

#define HEADER_BYTE_2_BIT	 0
#define PARITY_BYTE_2_BIT	 8
#define HEADER_BYTE_1_BIT	16
#define PARITY_BYTE_1_BIT	24
#define HEADER_BYTE_3_BIT	16
#define PARITY_BYTE_3_BIT	24

enum dp_catalog_audio_sdp_type {
	DP_AUDIO_SDP_STREAM,
	DP_AUDIO_SDP_TIMESTAMP,
@@ -144,6 +151,8 @@ struct dp_catalog_panel {
	u32 sync_start;
	u32 width_blanking;
	u32 dp_active;
	u8 *spd_vendor_name;
	u8 *spd_product_description;

	struct dp_catalog_hdr_data hdr_data;

@@ -159,6 +168,7 @@ struct dp_catalog_panel {
	int (*timing_cfg)(struct dp_catalog_panel *panel);
	void (*config_hdr)(struct dp_catalog_panel *panel);
	void (*tpg_config)(struct dp_catalog_panel *panel, bool enable);
	void (*config_spd)(struct dp_catalog_panel *panel);
};

struct dp_catalog {
@@ -168,6 +178,71 @@ struct dp_catalog {
	struct dp_catalog_panel panel;
};

static inline u8 dp_ecc_get_g0_value(u8 data)
{
	u8 c[4];
	u8 g[4];
	u8 ret_data = 0;
	u8 i;

	for (i = 0; i < 4; i++)
		c[i] = (data >> i) & 0x01;

	g[0] = c[3];
	g[1] = c[0] ^ c[3];
	g[2] = c[1];
	g[3] = c[2];

	for (i = 0; i < 4; i++)
		ret_data = ((g[i] & 0x01) << i) | ret_data;

	return ret_data;
}

static inline u8 dp_ecc_get_g1_value(u8 data)
{
	u8 c[4];
	u8 g[4];
	u8 ret_data = 0;
	u8 i;

	for (i = 0; i < 4; i++)
		c[i] = (data >> i) & 0x01;

	g[0] = c[0] ^ c[3];
	g[1] = c[0] ^ c[1] ^ c[3];
	g[2] = c[1] ^ c[2];
	g[3] = c[2] ^ c[3];

	for (i = 0; i < 4; i++)
		ret_data = ((g[i] & 0x01) << i) | ret_data;

	return ret_data;
}

static inline u8 dp_header_get_parity(u32 data)
{
	u8 x0 = 0;
	u8 x1 = 0;
	u8 ci = 0;
	u8 iData = 0;
	u8 i = 0;
	u8 parity_byte;
	u8 num_byte = (data & 0xFF00) > 0 ? 8 : 2;

	for (i = 0; i < num_byte; i++) {
		iData = (data >> i*4) & 0xF;

		ci = iData ^ x1;
		x1 = x0 ^ dp_ecc_get_g1_value(ci);
		x0 = dp_ecc_get_g0_value(ci);
	}

	parity_byte = x1 | (x0 << 4);

	return parity_byte;
}

struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io);
void dp_catalog_put(struct dp_catalog *catalog);

+2 −0
Original line number Diff line number Diff line
@@ -982,6 +982,8 @@ static int dp_display_post_enable(struct dp_display *dp_display)
		goto end;
	}

	dp->panel->spd_config(dp->panel);

	if (dp->audio_supported) {
		dp->audio->bw_code = dp->link->link_params.bw_code;
		dp->audio->lane_count = dp->link->link_params.lane_count;
+39 −0
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@ struct dp_panel_private {
	bool custom_edid;
	bool custom_dpcd;
	bool panel_on;
	u8 spd_vendor_name[8];
	u8 spd_product_description[16];
};

static const struct dp_panel_info fail_safe = {
@@ -52,6 +54,13 @@ static const struct dp_panel_info fail_safe = {
	.bpp = 24,
};

/* OEM NAME */
static const u8 vendor_name[8] = {81, 117, 97, 108, 99, 111, 109, 109};

/* MODEL NAME */
static const u8 product_desc[16] = {83, 110, 97, 112, 100, 114, 97, 103,
	111, 110, 0, 0, 0, 0, 0, 0};

static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
{
	int rlen, rc = 0;
@@ -700,6 +709,32 @@ static int dp_panel_setup_hdr(struct dp_panel *dp_panel,
	return rc;
}

static int dp_panel_spd_config(struct dp_panel *dp_panel)
{
	int rc = 0;
	struct dp_panel_private *panel;

	if (!dp_panel) {
		pr_err("invalid input\n");
		rc = -EINVAL;
		goto end;
	}

	if (!dp_panel->spd_enabled) {
		pr_debug("SPD Infoframe not enabled\n");
		goto end;
	}

	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);

	panel->catalog->spd_vendor_name = panel->spd_vendor_name;
	panel->catalog->spd_product_description =
		panel->spd_product_description;
	panel->catalog->config_spd(panel->catalog);
end:
	return rc;
}

struct dp_panel *dp_panel_get(struct dp_panel_in *in)
{
	int rc = 0;
@@ -726,6 +761,9 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
	dp_panel = &panel->dp_panel;
	panel->aux_cfg_update_done = false;
	dp_panel->max_bw_code = DP_LINK_BW_8_1;
	dp_panel->spd_enabled = true;
	memcpy(panel->spd_vendor_name, vendor_name, (sizeof(u8) * 8));
	memcpy(panel->spd_product_description, product_desc, (sizeof(u8) * 16));

	dp_panel->init = dp_panel_init_panel_info;
	dp_panel->deinit = dp_panel_deinit_panel_info;
@@ -738,6 +776,7 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
	dp_panel->set_edid = dp_panel_set_edid;
	dp_panel->set_dpcd = dp_panel_set_dpcd;
	dp_panel->tpg_config = dp_panel_tpg_config;
	dp_panel->spd_config = dp_panel_spd_config;

	dp_panel_edid_register(panel);
	dp_panel->setup_hdr = dp_panel_setup_hdr;
Loading