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

Commit 6df1c83c authored by Sankeerth Billakanti's avatar Sankeerth Billakanti
Browse files

drm/msm/dp: Add support for DisplayPort on sm6150



Add support for DP PHY and controller programming on sm6150

Change-Id: I479a3b009e5bbfd0cea431896163178883db8961
Signed-off-by: default avatarSankeerth Billakanti <sbillaka@codeaurora.org>
parent b5ca255f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ msm_drm-y := \
	dp/dp_power.o \
	dp/dp_catalog.o \
	dp/dp_catalog_v420.o \
	dp/dp_catalog_v200.o \
	dp/dp_aux.o \
	dp/dp_panel.o \
	dp/dp_link.o \
+303 −262

File changed.

Preview size limit exceeded, changes collapsed.

+32 −1
Original line number Diff line number Diff line
@@ -100,7 +100,8 @@ struct dp_catalog_ctrl {

	void (*state_ctrl)(struct dp_catalog_ctrl *ctrl, u32 state);
	void (*config_ctrl)(struct dp_catalog_ctrl *ctrl);
	void (*lane_mapping)(struct dp_catalog_ctrl *ctrl);
	void (*lane_mapping)(struct dp_catalog_ctrl *ctrl, bool flipped,
				char *lane_map);
	void (*mainlink_ctrl)(struct dp_catalog_ctrl *ctrl, bool enable);
	void (*set_pattern)(struct dp_catalog_ctrl *ctrl, u32 pattern);
	void (*reset)(struct dp_catalog_ctrl *ctrl);
@@ -293,10 +294,40 @@ static inline u8 dp_header_get_parity(u32 data)
	return parity_byte;
}

static inline u32 dp_read(char *exe_mode, struct dp_io_data *io_data,
				u32 offset)
{
	u32 data = 0;

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

	return data;
}

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

	if (!strcmp(exe_mode, "sw") || !strcmp(exe_mode, "all")) {
		if (io_data->buf)
			memcpy(io_data->buf + offset, &data, sizeof(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 *catalog,
		void *io);

int dp_catalog_get_v200(struct device *dev, struct dp_catalog *catalog,
		void *io);

#endif /* _DP_CATALOG_H_ */
+325 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__

#include <linux/delay.h>

#include "dp_catalog.h"
#include "dp_reg.h"

#define dp_catalog_get_priv_v200(x) ({ \
	struct dp_catalog *dp_catalog; \
	dp_catalog = container_of(x, struct dp_catalog, x); \
	dp_catalog->priv.data; \
})

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_io_data *dp_p1;
	struct dp_io_data *dp_tcsr;
};

struct dp_catalog_private_v200 {
	struct device *dev;
	struct dp_catalog_io *io;

	char exe_mode[SZ_4];
};

static void dp_catalog_aux_clear_hw_interrupts_v200(struct dp_catalog_aux *aux)
{
	struct dp_catalog_private_v200 *catalog;
	struct dp_io_data *io_data;
	u32 data = 0;

	if (!aux) {
		pr_err("invalid input\n");
		return;
	}

	catalog = dp_catalog_get_priv_v200(aux);
	io_data = catalog->io->dp_phy;

	data = dp_read(catalog->exe_mode, io_data,
				DP_PHY_AUX_INTERRUPT_STATUS_V200);

	dp_write(catalog->exe_mode, io_data, DP_PHY_AUX_INTERRUPT_CLEAR_V200,
			0x1f);
	wmb(); /* make sure 0x1f is written before next write */
	dp_write(catalog->exe_mode, io_data, DP_PHY_AUX_INTERRUPT_CLEAR_V200,
			0x9f);
	wmb(); /* make sure 0x9f is written before next write */
	dp_write(catalog->exe_mode, io_data, DP_PHY_AUX_INTERRUPT_CLEAR_V200,
			0);
	wmb(); /* make sure register is cleared */
}

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

	if (!aux || !cfg) {
		pr_err("invalid input\n");
		return;
	}

	catalog = dp_catalog_get_priv_v200(aux);

	io_data = catalog->io->dp_ahb;
	sw_reset = dp_read(catalog->exe_mode, io_data, DP_SW_RESET);

	sw_reset |= BIT(0);
	dp_write(catalog->exe_mode, io_data, DP_SW_RESET, sw_reset);
	usleep_range(1000, 1010); /* h/w recommended delay */

	sw_reset &= ~BIT(0);
	dp_write(catalog->exe_mode, io_data, DP_SW_RESET, sw_reset);

	dp_write(catalog->exe_mode, io_data, DP_PHY_CTRL, 0x4); /* bit 2 */
	udelay(1000);
	dp_write(catalog->exe_mode, io_data, DP_PHY_CTRL, 0x0); /* bit 2 */
	wmb(); /* make sure programming happened */

	io_data = catalog->io->dp_tcsr;
	dp_write(catalog->exe_mode, io_data, 0x4c, 0x1); /* bit 0 & 2 */
	wmb(); /* make sure programming happened */

	io_data = catalog->io->dp_phy;
	dp_write(catalog->exe_mode, io_data, DP_PHY_PD_CTL, 0x3c);
	wmb(); /* make sure PD programming happened */
	dp_write(catalog->exe_mode, io_data, DP_PHY_PD_CTL, 0x3d);
	wmb(); /* make sure PD programming happened */

	/* DP AUX CFG register programming */
	io_data = catalog->io->dp_phy;
	for (i = 0; i < PHY_AUX_CFG_MAX; i++)
		dp_write(catalog->exe_mode, io_data, cfg[i].offset,
				cfg[i].lut[cfg[i].current_index]);

	dp_write(catalog->exe_mode, io_data, DP_PHY_AUX_INTERRUPT_MASK_V200,
			0x1F);
	wmb(); /* make sure AUX configuration is done before enabling it */
}

static void dp_catalog_panel_config_msa_v200(struct dp_catalog_panel *panel,
					u32 rate, u32 stream_rate_khz,
					bool fixed_nvid)
{
	u32 pixel_m, pixel_n;
	u32 mvid, nvid;
	u64 mvid_calc;
	u32 const nvid_fixed = 0x8000;
	u32 const link_rate_hbr2 = 540000;
	u32 const link_rate_hbr3 = 810000;
	struct dp_catalog_private_v200 *catalog;
	struct dp_io_data *io_data;
	u32 strm_reg_off = 0;
	u32 mvid_reg_off = 0, nvid_reg_off = 0;

	if (!panel) {
		pr_err("invalid input\n");
		return;
	}

	if (panel->stream_id >= DP_STREAM_MAX) {
		pr_err("invalid stream_id:%d\n", panel->stream_id);
		return;
	}

	catalog = dp_catalog_get_priv_v200(panel);
	if (fixed_nvid) {
		pr_debug("use fixed NVID=0x%x\n", nvid_fixed);
		nvid = nvid_fixed;

		pr_debug("link rate=%dkbps, stream_rate_khz=%uKhz",
			rate, stream_rate_khz);

		/*
		 * For intermediate results, use 64 bit arithmetic to avoid
		 * loss of precision.
		 */
		mvid_calc = (u64) stream_rate_khz * nvid;
		mvid_calc = div_u64(mvid_calc, rate);

		/*
		 * truncate back to 32 bits as this final divided value will
		 * always be within the range of a 32 bit unsigned int.
		 */
		mvid = (u32) mvid_calc;
	} else {
		io_data = catalog->io->dp_mmss_cc;

		if (panel->stream_id == DP_STREAM_1)
			strm_reg_off = MMSS_DP_PIXEL1_M_V200 -
						MMSS_DP_PIXEL_M_V200;

		pixel_m = dp_read(catalog->exe_mode, io_data,
				MMSS_DP_PIXEL_M_V200 + strm_reg_off);
		pixel_n = dp_read(catalog->exe_mode, io_data,
				MMSS_DP_PIXEL_N_V200 + strm_reg_off);
		pr_debug("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);

		mvid = (pixel_m & 0xFFFF) * 5;
		nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);

		pr_debug("rate = %d\n", rate);

		if (link_rate_hbr2 == rate)
			nvid *= 2;

		if (link_rate_hbr3 == rate)
			nvid *= 3;
	}

	io_data = catalog->io->dp_link;

	if (panel->stream_id == DP_STREAM_1) {
		mvid_reg_off = DP1_SOFTWARE_MVID - DP_SOFTWARE_MVID;
		nvid_reg_off = DP1_SOFTWARE_NVID - DP_SOFTWARE_NVID;
	}

	pr_debug("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
	dp_write(catalog->exe_mode, io_data, DP_SOFTWARE_MVID + mvid_reg_off,
			mvid);
	dp_write(catalog->exe_mode, io_data, DP_SOFTWARE_NVID + nvid_reg_off,
			nvid);
}

static void dp_catalog_ctrl_lane_mapping_v200(struct dp_catalog_ctrl *ctrl,
						bool flipped, char *lane_map)
{
	struct dp_catalog_private_v200 *catalog;
	struct dp_io_data *io_data;
	u8 l_map[4] = { 0 }, i = 0, j = 0;
	u32 lane_map_reg = 0;

	if (!ctrl) {
		pr_err("invalid input\n");
		return;
	}

	catalog = dp_catalog_get_priv_v200(ctrl);
	io_data = catalog->io->dp_link;

	/* For flip case, swap phy lanes with ML0 and ML3, ML1 and ML2 */
	if (flipped) {
		for (i = 0; i < DP_MAX_PHY_LN; i++) {
			if (lane_map[i] == DP_ML0) {
				for (j = 0; j < DP_MAX_PHY_LN; j++) {
					if (lane_map[j] == DP_ML3) {
						l_map[i] = DP_ML3;
						l_map[j] = DP_ML0;
						break;
					}
				}
			} else if (lane_map[i] == DP_ML1) {
				for (j = 0; j < DP_MAX_PHY_LN; j++) {
					if (lane_map[j] == DP_ML2) {
						l_map[i] = DP_ML2;
						l_map[j] = DP_ML1;
						break;
					}
				}
			}
		}
	} else {
		/* Normal orientation */
		for (i = 0; i < DP_MAX_PHY_LN; i++)
			l_map[i] = lane_map[i];
	}

	lane_map_reg = ((l_map[3]&3)<<6)|((l_map[2]&3)<<4)|((l_map[1]&3)<<2)
			|(l_map[0]&3);

	dp_write(catalog->exe_mode, io_data, DP_LOGICAL2PHYSICAL_LANE_MAPPING,
			lane_map_reg);
}

static void dp_catalog_ctrl_usb_reset_v200(struct dp_catalog_ctrl *ctrl,
						bool flip)
{
}

static void dp_catalog_put_v200(struct dp_catalog *catalog)
{
	struct dp_catalog_private_v200 *catalog_priv;

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

	catalog_priv = catalog->priv.data;
	devm_kfree(catalog_priv->dev, catalog_priv);
}

static void dp_catalog_set_exe_mode_v200(struct dp_catalog *catalog, char *mode)
{
	struct dp_catalog_private_v200 *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_v200(struct device *dev, struct dp_catalog *catalog,
				void *io)
{
	struct dp_catalog_private_v200 *catalog_priv;

	if (!dev || !catalog) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

	catalog_priv = devm_kzalloc(dev, sizeof(*catalog_priv), GFP_KERNEL);
	if (!catalog_priv)
		return -ENOMEM;

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

	catalog->priv.put                = dp_catalog_put_v200;
	catalog->priv.set_exe_mode       = dp_catalog_set_exe_mode_v200;

	catalog->aux.clear_hw_interrupts =
				dp_catalog_aux_clear_hw_interrupts_v200;
	catalog->aux.setup               = dp_catalog_aux_setup_v200;

	catalog->panel.config_msa        = dp_catalog_panel_config_msa_v200;

	catalog->ctrl.lane_mapping       = dp_catalog_ctrl_lane_mapping_v200;
	catalog->ctrl.usb_reset          = dp_catalog_ctrl_usb_reset_v200;

	/* Set the default execution mode to hardware mode */
	dp_catalog_set_exe_mode_v200(catalog, "hw");

	return 0;
}
+23 −47
Original line number Diff line number Diff line
@@ -63,36 +63,6 @@ struct dp_catalog_private_v420 {
	char exe_mode[SZ_4];
};

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)
{
@@ -108,12 +78,13 @@ static void dp_catalog_aux_setup_v420(struct dp_catalog_aux *aux,
	catalog = dp_catalog_get_priv_v420(aux);

	io_data = catalog->io->dp_phy;
	dp_write(catalog, io_data, DP_PHY_PD_CTL, 0x67);
	dp_write(catalog->exe_mode, io_data, DP_PHY_PD_CTL, 0x67);
	wmb(); /* make sure PD programming happened */

	/* Turn on BIAS current for PHY/PLL */
	io_data = catalog->io->dp_pll;
	dp_write(catalog, io_data, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
	dp_write(catalog->exe_mode, io_data, QSERDES_COM_BIAS_EN_CLKBUFLR_EN,
			0x17);
	wmb(); /* make sure BIAS programming happened */

	io_data = catalog->io->dp_phy;
@@ -122,12 +93,13 @@ 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_data, cfg[i].offset,
		dp_write(catalog->exe_mode, io_data, cfg[i].offset,
			cfg[i].lut[cfg[i].current_index]);
	}
	wmb(); /* make sure DP AUX CFG programming happened */

	dp_write(catalog, io_data, DP_PHY_AUX_INTERRUPT_MASK_V420, 0x1F);
	dp_write(catalog->exe_mode, io_data, DP_PHY_AUX_INTERRUPT_MASK_V420,
			0x1F);
}

static void dp_catalog_panel_config_msa_v420(struct dp_catalog_panel *panel,
@@ -179,9 +151,9 @@ static void dp_catalog_panel_config_msa_v420(struct dp_catalog_panel *panel,
		if (panel->stream_id == DP_STREAM_1)
			reg_off = MMSS_DP_PIXEL1_M_V420 - MMSS_DP_PIXEL_M_V420;

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

@@ -205,8 +177,8 @@ static void dp_catalog_panel_config_msa_v420(struct dp_catalog_panel *panel,
	}

	pr_debug("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
	dp_write(catalog, io_data, DP_SOFTWARE_MVID + mvid_off, mvid);
	dp_write(catalog, io_data, DP_SOFTWARE_NVID + nvid_off, nvid);
	dp_write(catalog->exe_mode, io_data, DP_SOFTWARE_MVID + mvid_off, mvid);
	dp_write(catalog->exe_mode, io_data, DP_SOFTWARE_NVID + nvid_off, nvid);
}

static void dp_catalog_ctrl_phy_lane_cfg_v420(struct dp_catalog_ctrl *ctrl,
@@ -229,7 +201,7 @@ static void dp_catalog_ctrl_phy_lane_cfg_v420(struct dp_catalog_ctrl *ctrl,
	info |= ((orientation & 0x0F) << 4);
	pr_debug("Shared Info = 0x%x\n", info);

	dp_write(catalog, io_data, DP_PHY_SPARE0_V420, info);
	dp_write(catalog->exe_mode, io_data, DP_PHY_SPARE0_V420, info);
}

static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
@@ -254,12 +226,12 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,

	/* program default setting first */
	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);
	dp_write(catalog->exe_mode, io_data, TXn_TX_DRV_LVL_V420, 0x2A);
	dp_write(catalog->exe_mode, 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);
	dp_write(catalog->exe_mode, io_data, TXn_TX_DRV_LVL_V420, 0x2A);
	dp_write(catalog->exe_mode, io_data, TXn_TX_EMP_POST1_LVL, 0x20);

	/* Enable MUX to use Cursor values from these registers */
	value0 |= BIT(5);
@@ -268,12 +240,16 @@ 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) {
		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);
		dp_write(catalog->exe_mode, io_data, TXn_TX_DRV_LVL_V420,
				value0);
		dp_write(catalog->exe_mode, 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);
		dp_write(catalog->exe_mode, io_data, TXn_TX_DRV_LVL_V420,
				value0);
		dp_write(catalog->exe_mode, io_data, TXn_TX_EMP_POST1_LVL,
				value1);

		pr_debug("hw: vx_value=0x%x px_value=0x%x\n",
			value0, value1);
Loading