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

Commit fc5cffb7 authored by Samantha Tran's avatar Samantha Tran Committed by Govinda Rajulu Chenna
Browse files

drm/msm/dp: add support for different execution modes



Add support for switching different execution modes like
hardware mode, software mode or both. In hardware mode,
DP works as normal. In software mode, DP driver works on
IO mapped buffers instead of hardware. This mode can be
used to simulate different variants of hardware.

Change-Id: I102b2e37220048d819131bcc4b6d5083499915ee
Signed-off-by: default avatarSamantha Tran <samtran@codeaurora.org>
Signed-off-by: default avatarGovinda Rajulu Chenna <gchenna@codeaurora.org>
parent 3188c322
Loading
Loading
Loading
Loading
+29 −16
Original line number Diff line number Diff line
@@ -50,6 +50,33 @@ struct dp_aux_private {
	u8 *edid;
};

#ifdef CONFIG_DYNAMIC_DEBUG
static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
		struct drm_dp_aux_msg *msg)
{
	DEFINE_DYNAMIC_DEBUG_METADATA(ddm, "dp aux tracker");

	if (unlikely(ddm.flags & _DPRINTK_FLAGS_PRINT)) {
		u8 buf[SZ_64];
		struct dp_aux_private *aux = container_of(drm_aux,
			struct dp_aux_private, drm_aux);

		snprintf(buf, SZ_64, "[drm-dp] %5s %5s %5xh(%2zu): ",
			aux->native ? "NATIVE" : "I2C",
			aux->read ? "READ" : "WRITE",
			msg->address, msg->size);

		print_hex_dump(KERN_DEBUG, buf, DUMP_PREFIX_NONE,
			8, 1, msg->buffer, msg->size, false);
	}
}
#else
static void dp_aux_hex_dump(struct drm_dp_aux *drm_aux,
		struct drm_dp_aux_msg *msg)
{
}
#endif

static char *dp_aux_get_error(u32 aux_error)
{
	switch (aux_error) {
@@ -444,7 +471,6 @@ static int dp_aux_transfer_ready(struct dp_aux_private *aux,
static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
		struct drm_dp_aux_msg *msg)
{
	u8 buf[SZ_64];
	u32 timeout;
	ssize_t ret;
	struct dp_aux_private *aux = container_of(drm_aux,
@@ -482,13 +508,7 @@ static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
	}

	if (aux->aux_error_num == DP_AUX_ERR_NONE) {
		snprintf(buf, SZ_64, "[drm-dp] dbg: %5s %5s %5xh(%2zu): ",
			aux->native ? "NATIVE" : "I2C",
			aux->read ? "READ" : "WRITE",
			msg->address, msg->size);

		print_hex_dump(KERN_DEBUG, buf,
			DUMP_PREFIX_NONE, 8, 1, msg->buffer, msg->size, false);
		dp_aux_hex_dump(drm_aux, msg);

		msg->reply = aux->native ?
			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
@@ -511,7 +531,6 @@ static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
		struct drm_dp_aux_msg *msg)
{
	u8 buf[SZ_64];
	ssize_t ret;
	int const retry_count = 5;
	struct dp_aux_private *aux = container_of(drm_aux,
@@ -544,13 +563,7 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *drm_aux,
		if (aux->read)
			dp_aux_cmd_fifo_rx(aux, msg);

		snprintf(buf, SZ_64, "[drm-dp] %5s %5s %5xh(%2zu): ",
			aux->native ? "NATIVE" : "I2C",
			aux->read ? "READ" : "WRITE",
			msg->address, msg->size);

		print_hex_dump(KERN_DEBUG, buf,
			DUMP_PREFIX_NONE, 8, 1, msg->buffer, msg->size, false);
		dp_aux_hex_dump(drm_aux, msg);

		msg->reply = aux->native ?
			DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+556 −341

File changed.

Preview size limit exceeded, changes collapsed.

+7 −2
Original line number Diff line number Diff line
@@ -181,6 +181,7 @@ struct dp_catalog_priv {
	void *data;

	void (*put)(struct dp_catalog *catalog);
	void (*set_exe_mode)(struct dp_catalog *dp_catalog, char *mode);
};

struct dp_catalog {
@@ -189,6 +190,10 @@ struct dp_catalog {
	struct dp_catalog_audio audio;
	struct dp_catalog_panel panel;
	struct dp_catalog_priv priv;

	void (*set_exe_mode)(struct dp_catalog *dp_catalog, char *mode);
	int (*get_reg_dump)(struct dp_catalog *dp_catalog,
		char *mode, u8 **out_buf, u32 *out_buf_len);
};

static inline u8 dp_ecc_get_g0_value(u8 data)
@@ -259,7 +264,7 @@ static inline u8 dp_header_get_parity(u32 data)
struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser);
void dp_catalog_put(struct dp_catalog *catalog);

int dp_catalog_get_v420(struct device *dev, struct dp_catalog *dp_catalog,
			struct dp_parser *parser);
int dp_catalog_get_v420(struct device *dev, struct dp_catalog *catalog,
		void *io);

#endif /* _DP_CATALOG_H_ */
+95 −31
Original line number Diff line number Diff line
@@ -17,9 +17,6 @@
#include "dp_catalog.h"
#include "dp_reg.h"

#define dp_read(offset) readl_relaxed((offset))
#define dp_write(offset, data) writel_relaxed((data), (offset))

#define dp_catalog_get_priv_v420(x) ({ \
	struct dp_catalog *dp_catalog; \
	dp_catalog = container_of(x, struct dp_catalog, x); \
@@ -44,15 +41,62 @@ static u8 const vm_voltage_swing[MAX_VOLTAGE_LEVELS][MAX_PRE_EMP_LEVELS] = {
	{0xFF, 0xFF, 0xFF, 0xFF}  /* sw1, 1.2 v, optional */
};

struct dp_catalog_io {
	struct dp_io_data *dp_ahb;
	struct dp_io_data *dp_aux;
	struct dp_io_data *dp_link;
	struct dp_io_data *dp_p0;
	struct dp_io_data *dp_phy;
	struct dp_io_data *dp_ln_tx0;
	struct dp_io_data *dp_ln_tx1;
	struct dp_io_data *dp_mmss_cc;
	struct dp_io_data *dp_pll;
	struct dp_io_data *usb3_dp_com;
	struct dp_io_data *hdcp_physical;
};

struct dp_catalog_private_v420 {
	struct device *dev;
	struct dp_io *io;
	struct dp_catalog_io *io;

	char exe_mode[SZ_4];
};

static static u32 dp_read(struct dp_catalog_private_v420 *catalog,
		struct dp_io_data *io_data, u32 offset)
{
	u32 data = 0;

	if (!strcmp(catalog->exe_mode, "hw") ||
	    !strcmp(catalog->exe_mode, "all")) {
		data = readl_relaxed(io_data->io.base + offset);
	} else if (!strcmp(catalog->exe_mode, "sw")) {
		if (io_data->buf)
			memcpy(&data, io_data->buf + offset, sizeof(offset));
	}

	return data;
}

static void dp_write(struct dp_catalog_private_v420 *catalog,
		struct dp_io_data *io_data, u32 offset, u32 data)
{
	if (!strcmp(catalog->exe_mode, "hw") ||
	    !strcmp(catalog->exe_mode, "all"))
		writel_relaxed(data, io_data->io.base + offset);

	if (!strcmp(catalog->exe_mode, "sw") ||
	    !strcmp(catalog->exe_mode, "all")) {
		if (io_data->buf)
			memcpy(io_data->buf + offset, &data, sizeof(data));
	}
}

static void dp_catalog_aux_setup_v420(struct dp_catalog_aux *aux,
		struct dp_aux_cfg *cfg)
{
	struct dp_catalog_private_v420 *catalog;
	struct dp_io_data *io_data;
	int i = 0;

	if (!aux || !cfg) {
@@ -62,11 +106,12 @@ static void dp_catalog_aux_setup_v420(struct dp_catalog_aux *aux,

	catalog = dp_catalog_get_priv_v420(aux);

	dp_write(catalog->io->phy_io.base + DP_PHY_PD_CTL, 0x7D);
	io_data = catalog->io->dp_phy;
	dp_write(catalog, io_data, DP_PHY_PD_CTL, 0x7D);
	wmb(); /* make sure PD programming happened */

	/* Turn on BIAS current for PHY/PLL */
	dp_write(catalog->io->dp_pll_io.base +
	dp_write(catalog, io_data,
		QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3d);

	/* DP AUX CFG register programming */
@@ -74,12 +119,11 @@ static void dp_catalog_aux_setup_v420(struct dp_catalog_aux *aux,
		pr_debug("%s: offset=0x%08x, value=0x%08x\n",
			dp_phy_aux_config_type_to_string(i),
			cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
		dp_write(catalog->io->phy_io.base + cfg[i].offset,
		dp_write(catalog, io_data, cfg[i].offset,
			cfg[i].lut[cfg[i].current_index]);
	}

	dp_write(catalog->io->phy_io.base + DP_PHY_AUX_INTERRUPT_MASK_V420,
		0x1F);
	dp_write(catalog, io_data, DP_PHY_AUX_INTERRUPT_MASK_V420, 0x1F);
}

static void dp_catalog_ctrl_config_msa_v420(struct dp_catalog_ctrl *ctrl,
@@ -93,7 +137,7 @@ static void dp_catalog_ctrl_config_msa_v420(struct dp_catalog_ctrl *ctrl,
	u32 const link_rate_hbr2 = 540000;
	u32 const link_rate_hbr3 = 810000;
	struct dp_catalog_private_v420 *catalog;
	void __iomem *base_cc, *base_ctrl;
	struct dp_io_data *io_data;

	if (!ctrl || !rate) {
		pr_err("invalid input\n");
@@ -121,10 +165,10 @@ static void dp_catalog_ctrl_config_msa_v420(struct dp_catalog_ctrl *ctrl,
		 */
		mvid = (u32) mvid_calc;
	} else {
		base_cc = catalog->io->dp_cc_io.base;
		io_data = catalog->io->dp_mmss_cc;

		pixel_m = dp_read(base_cc + MMSS_DP_PIXEL_M_V420);
		pixel_n = dp_read(base_cc + MMSS_DP_PIXEL_N_V420);
		pixel_m = dp_read(catalog, io_data, MMSS_DP_PIXEL_M_V420);
		pixel_n = dp_read(catalog, io_data, MMSS_DP_PIXEL_N_V420);
		pr_debug("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);

		mvid = (pixel_m & 0xFFFF) * 5;
@@ -139,10 +183,10 @@ static void dp_catalog_ctrl_config_msa_v420(struct dp_catalog_ctrl *ctrl,
			nvid *= 3;
	}

	base_ctrl = catalog->io->ctrl_io.base;
	io_data = catalog->io->dp_link;
	pr_debug("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
	dp_write(base_ctrl + DP_SOFTWARE_MVID, mvid);
	dp_write(base_ctrl + DP_SOFTWARE_NVID, nvid);
	dp_write(catalog, io_data, DP_SOFTWARE_MVID, mvid);
	dp_write(catalog, io_data, DP_SOFTWARE_NVID, nvid);
}

static void dp_catalog_ctrl_phy_lane_cfg_v420(struct dp_catalog_ctrl *ctrl,
@@ -151,6 +195,7 @@ static void dp_catalog_ctrl_phy_lane_cfg_v420(struct dp_catalog_ctrl *ctrl,
	u32 info = 0x0;
	struct dp_catalog_private_v420 *catalog;
	u8 orientation = BIT(!!flipped);
	struct dp_io_data *io_data;

	if (!ctrl) {
		pr_err("invalid input\n");
@@ -158,19 +203,20 @@ static void dp_catalog_ctrl_phy_lane_cfg_v420(struct dp_catalog_ctrl *ctrl,
	}

	catalog = dp_catalog_get_priv_v420(ctrl);
	io_data = catalog->io->dp_phy;

	info |= (ln_cnt & 0x0F);
	info |= ((orientation & 0x0F) << 4);
	pr_debug("Shared Info = 0x%x\n", info);

	dp_write(catalog->io->phy_io.base + DP_PHY_SPARE0_V420, info);
	dp_write(catalog, io_data, DP_PHY_SPARE0_V420, info);
}

static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
		u8 v_level, u8 p_level)
{
	struct dp_catalog_private_v420 *catalog;
	void __iomem *base0, *base1;
	struct dp_io_data *io_data;
	u8 value0, value1;

	if (!ctrl || !((v_level < MAX_VOLTAGE_LEVELS)
@@ -180,8 +226,6 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
	}

	catalog = dp_catalog_get_priv_v420(ctrl);
	base0 = catalog->io->ln_tx0_io.base;
	base1 = catalog->io->ln_tx1_io.base;

	pr_debug("hw: v=%d p=%d\n", v_level, p_level);

@@ -189,10 +233,13 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
	value1 = vm_pre_emphasis[v_level][p_level];

	/* program default setting first */
	dp_write(base0 + TXn_TX_DRV_LVL_V420, 0x2A);
	dp_write(base1 + TXn_TX_DRV_LVL_V420, 0x2A);
	dp_write(base0 + TXn_TX_EMP_POST1_LVL, 0x20);
	dp_write(base1 + TXn_TX_EMP_POST1_LVL, 0x20);
	io_data = catalog->io->dp_ln_tx0;
	dp_write(catalog, io_data, TXn_TX_DRV_LVL_V420, 0x2A);
	dp_write(catalog, io_data, TXn_TX_EMP_POST1_LVL, 0x20);

	io_data = catalog->io->dp_ln_tx1;
	dp_write(catalog, io_data, TXn_TX_DRV_LVL_V420, 0x2A);
	dp_write(catalog, io_data, TXn_TX_EMP_POST1_LVL, 0x20);

	/* Enable MUX to use Cursor values from these registers */
	value0 |= BIT(5);
@@ -200,10 +247,13 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,

	/* Configure host and panel only if both values are allowed */
	if (value0 != 0xFF && value1 != 0xFF) {
		dp_write(base0 + TXn_TX_DRV_LVL_V420, value0);
		dp_write(base1 + TXn_TX_DRV_LVL_V420, value0);
		dp_write(base0 + TXn_TX_EMP_POST1_LVL, value1);
		dp_write(base1 + TXn_TX_EMP_POST1_LVL, value1);
		io_data = catalog->io->dp_ln_tx0;
		dp_write(catalog, io_data, TXn_TX_DRV_LVL_V420, value0);
		dp_write(catalog, io_data, TXn_TX_EMP_POST1_LVL, value1);

		io_data = catalog->io->dp_ln_tx1;
		dp_write(catalog, io_data, TXn_TX_DRV_LVL_V420, value0);
		dp_write(catalog, io_data, TXn_TX_EMP_POST1_LVL, value1);

		pr_debug("hw: vx_value=0x%x px_value=0x%x\n",
			value0, value1);
@@ -224,12 +274,24 @@ static void dp_catalog_put_v420(struct dp_catalog *catalog)
	devm_kfree(catalog_priv->dev, catalog_priv);
}

static void dp_catalog_set_exe_mode_v420(struct dp_catalog *catalog, char *mode)
{
	struct dp_catalog_private_v420 *catalog_priv;

	if (!catalog || !catalog->priv.data)
		return;

	catalog_priv = catalog->priv.data;

	strlcpy(catalog_priv->exe_mode, mode, sizeof(catalog_priv->exe_mode));
}

int dp_catalog_get_v420(struct device *dev, struct dp_catalog *catalog,
			struct dp_parser *parser)
		void *io)
{
	struct dp_catalog_private_v420 *catalog_priv;

	if (!dev || !catalog || !parser) {
	if (!dev || !catalog) {
		pr_err("invalid input\n");
		return -EINVAL;
	}
@@ -239,10 +301,12 @@ int dp_catalog_get_v420(struct device *dev, struct dp_catalog *catalog,
		return -ENOMEM;

	catalog_priv->dev = dev;
	catalog_priv->io = &parser->io;
	catalog_priv->io = io;
	catalog->priv.data = catalog_priv;

	catalog->priv.put          = dp_catalog_put_v420;
	catalog->priv.set_exe_mode = dp_catalog_set_exe_mode_v420;

	catalog->aux.setup         = dp_catalog_aux_setup_v420;
	catalog->ctrl.config_msa   = dp_catalog_ctrl_config_msa_v420;
	catalog->ctrl.phy_lane_cfg = dp_catalog_ctrl_phy_lane_cfg_v420;
+131 −4
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

#include <linux/debugfs.h>

#include "dp_parser.h"
#include "dp_power.h"
#include "dp_catalog.h"
#include "dp_aux.h"
@@ -38,10 +37,14 @@ struct dp_debug_private {

	int vdo;

	char exe_mode[SZ_32];
	char reg_dump[SZ_32];

	struct dp_usbpd *usbpd;
	struct dp_link *link;
	struct dp_panel *panel;
	struct dp_aux *aux;
	struct dp_catalog *catalog;
	struct drm_connector **connector;
	struct device *dev;
	struct work_struct sim_work;
@@ -398,6 +401,36 @@ static ssize_t dp_debug_tpg_write(struct file *file,
	return len;
}

static ssize_t dp_debug_write_exe_mode(struct file *file,
		const char __user *user_buff, size_t count, loff_t *ppos)
{
	struct dp_debug_private *debug = file->private_data;
	char *buf;
	size_t len = 0;

	if (!debug)
		return -ENODEV;

	if (*ppos)
		return 0;

	len = min_t(size_t, count, SZ_32 - 1);
	buf = memdup_user(user_buff, len);
	buf[len] = '\0';

	if (sscanf(buf, "%3s", debug->exe_mode) != 1)
		goto end;

	if (strcmp(debug->exe_mode, "hw") &&
	    strcmp(debug->exe_mode, "sw") &&
	    strcmp(debug->exe_mode, "all"))
		goto end;

	debug->catalog->set_exe_mode(debug->catalog, debug->exe_mode);
end:
	return len;
}

static ssize_t dp_debug_read_connected(struct file *file,
		char __user *user_buff, size_t count, loff_t *ppos)
{
@@ -910,6 +943,71 @@ static ssize_t dp_debug_write_attention(struct file *file,
	return len;
}

static ssize_t dp_debug_write_dump(struct file *file,
		const char __user *user_buff, size_t count, loff_t *ppos)
{
	struct dp_debug_private *debug = file->private_data;
	char buf[SZ_32];
	size_t len = 0;

	if (!debug)
		return -ENODEV;

	if (*ppos)
		return 0;

	/* Leave room for termination char */
	len = min_t(size_t, count, SZ_32 - 1);
	if (copy_from_user(buf, user_buff, len))
		goto end;

	buf[len] = '\0';

	if (sscanf(buf, "%31s", debug->reg_dump) != 1)
		goto end;

	/* qfprom register dump not supported */
	if (!strcmp(debug->reg_dump, "qfprom_physical"))
		strlcpy(debug->reg_dump, "clear", sizeof(debug->reg_dump));
end:
	return len;
}

static ssize_t dp_debug_read_dump(struct file *file,
		char __user *user_buff, size_t count, loff_t *ppos)
{
	int rc = 0;
	struct dp_debug_private *debug = file->private_data;
	u8 *buf = NULL;
	u32 len = 0;
	char prefix[SZ_32];

	if (!debug)
		return -ENODEV;

	if (*ppos)
		return 0;

	if (!debug->usbpd->hpd_high || !strlen(debug->reg_dump))
		goto end;

	rc = debug->catalog->get_reg_dump(debug->catalog,
		debug->reg_dump, &buf, &len);
	if (rc)
		goto end;

	snprintf(prefix, sizeof(prefix), "%s: ", debug->reg_dump);
	print_hex_dump(KERN_DEBUG, prefix, DUMP_PREFIX_NONE,
		16, 4, buf, len, false);

	if (copy_to_user(user_buff, buf, len))
		return -EFAULT;

	*ppos += len;
end:
	return len;
}

static const struct file_operations dp_debug_fops = {
	.open = simple_open,
	.read = dp_debug_read_info,
@@ -947,6 +1045,10 @@ static const struct file_operations bw_code_fops = {
	.read = dp_debug_bw_code_read,
	.write = dp_debug_bw_code_write,
};
static const struct file_operations exe_mode_fops = {
	.open = simple_open,
	.write = dp_debug_write_exe_mode,
};

static const struct file_operations tpg_fops = {
	.open = simple_open,
@@ -970,6 +1072,12 @@ static const struct file_operations attention_fops = {
	.write = dp_debug_write_attention,
};

static const struct file_operations dump_fops = {
	.open = simple_open,
	.write = dp_debug_write_dump,
	.read = dp_debug_read_dump,
};

static int dp_debug_init(struct dp_debug *dp_debug)
{
	int rc = 0;
@@ -1032,7 +1140,14 @@ static int dp_debug_init(struct dp_debug *dp_debug)
		rc = PTR_ERR(file);
		pr_err("[%s] debugfs max_bw_code failed, rc=%d\n",
		       DEBUG_NAME, rc);
		goto error_remove_dir;
	}

	file = debugfs_create_file("exe_mode", 0644, dir,
			debug, &exe_mode_fops);
	if (IS_ERR_OR_NULL(file)) {
		rc = PTR_ERR(file);
		pr_err("[%s] debugfs register failed, rc=%d\n",
		       DEBUG_NAME, rc);
	}

	file = debugfs_create_file("edid", 0644, dir,
@@ -1092,6 +1207,16 @@ static int dp_debug_init(struct dp_debug *dp_debug)
		goto error_remove_dir;
	}

	file = debugfs_create_file("dump", 0644, dir,
		debug, &dump_fops);

	if (IS_ERR_OR_NULL(file)) {
		rc = PTR_ERR(file);
		pr_err("[%s] debugfs dump failed, rc=%d\n",
			DEBUG_NAME, rc);
		goto error_remove_dir;
	}

	return 0;

error_remove_dir:
@@ -1112,13 +1237,14 @@ static void dp_debug_sim_work(struct work_struct *work)

struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
			struct dp_usbpd *usbpd, struct dp_link *link,
			struct dp_aux *aux, struct drm_connector **connector)
			struct dp_aux *aux, struct drm_connector **connector,
			struct dp_catalog *catalog)
{
	int rc = 0;
	struct dp_debug_private *debug;
	struct dp_debug *dp_debug;

	if (!dev || !panel || !usbpd || !link) {
	if (!dev || !panel || !usbpd || !link || !catalog) {
		pr_err("invalid input\n");
		rc = -EINVAL;
		goto error;
@@ -1139,6 +1265,7 @@ struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
	debug->aux = aux;
	debug->dev = dev;
	debug->connector = connector;
	debug->catalog = catalog;

	dp_debug = &debug->dp_debug;
	dp_debug->vdisplay = 0;
Loading