diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/dsi.c')
| -rw-r--r-- | drivers/gpu/drm/tegra/dsi.c | 811 | 
1 files changed, 688 insertions, 123 deletions
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index f7874458926a..33f67fd601c6 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -11,6 +11,7 @@  #include <linux/host1x.h>  #include <linux/module.h>  #include <linux/of.h> +#include <linux/of_platform.h>  #include <linux/platform_device.h>  #include <linux/reset.h> @@ -26,9 +27,6 @@  #include "dsi.h"  #include "mipi-phy.h" -#define DSI_VIDEO_FIFO_DEPTH (1920 / 4) -#define DSI_HOST_FIFO_DEPTH 64 -  struct tegra_dsi {  	struct host1x_client client;  	struct tegra_output output; @@ -54,6 +52,13 @@ struct tegra_dsi {  	struct regulator *vdd;  	bool enabled; + +	unsigned int video_fifo_depth; +	unsigned int host_fifo_depth; + +	/* for ganged-mode support */ +	struct tegra_dsi *master; +	struct tegra_dsi *slave;  };  static inline struct tegra_dsi * @@ -318,6 +323,21 @@ static const u32 pkt_seq_video_non_burst_sync_events[NUM_PKT_SEQ] = {  	[11] = PKT_ID0(MIPI_DSI_BLANKING_PACKET) | PKT_LEN0(4),  }; +static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = { +	[ 0] = 0, +	[ 1] = 0, +	[ 2] = 0, +	[ 3] = 0, +	[ 4] = 0, +	[ 5] = 0, +	[ 6] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(3) | PKT_LP, +	[ 7] = 0, +	[ 8] = 0, +	[ 9] = 0, +	[10] = PKT_ID0(MIPI_DSI_DCS_LONG_WRITE) | PKT_LEN0(5) | PKT_LP, +	[11] = 0, +}; +  static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)  {  	struct mipi_dphy_timing timing; @@ -329,7 +349,7 @@ static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)  	if (rate < 0)  		return rate; -	period = DIV_ROUND_CLOSEST(1000000000UL, rate * 2); +	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate * 2);  	err = mipi_dphy_timing_get_default(&timing, period);  	if (err < 0) @@ -369,6 +389,9 @@ static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi)  		DSI_TIMING_FIELD(timing.tago, period, 1);  	tegra_dsi_writel(dsi, value, DSI_BTA_TIMING); +	if (dsi->slave) +		return tegra_dsi_set_phy_timing(dsi->slave); +  	return 0;  } @@ -426,26 +449,59 @@ static int tegra_dsi_get_format(enum mipi_dsi_pixel_format format,  	return 0;  } -static int tegra_output_dsi_enable(struct tegra_output *output) +static void tegra_dsi_ganged_enable(struct tegra_dsi *dsi, unsigned int start, +				    unsigned int size) +{ +	u32 value; + +	tegra_dsi_writel(dsi, start, DSI_GANGED_MODE_START); +	tegra_dsi_writel(dsi, size << 16 | size, DSI_GANGED_MODE_SIZE); + +	value = DSI_GANGED_MODE_CONTROL_ENABLE; +	tegra_dsi_writel(dsi, value, DSI_GANGED_MODE_CONTROL); +} + +static void tegra_dsi_enable(struct tegra_dsi *dsi) +{ +	u32 value; + +	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); +	value |= DSI_POWER_CONTROL_ENABLE; +	tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + +	if (dsi->slave) +		tegra_dsi_enable(dsi->slave); +} + +static unsigned int tegra_dsi_get_lanes(struct tegra_dsi *dsi) +{ +	if (dsi->master) +		return dsi->master->lanes + dsi->lanes; + +	if (dsi->slave) +		return dsi->lanes + dsi->slave->lanes; + +	return dsi->lanes; +} + +static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, +			       const struct drm_display_mode *mode)  { -	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); -	struct drm_display_mode *mode = &dc->base.mode;  	unsigned int hact, hsw, hbp, hfp, i, mul, div; -	struct tegra_dsi *dsi = to_dsi(output);  	enum tegra_dsi_format format; -	unsigned long value;  	const u32 *pkt_seq; +	u32 value;  	int err; -	if (dsi->enabled) -		return 0; -  	if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {  		DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n");  		pkt_seq = pkt_seq_video_non_burst_sync_pulses; -	} else { +	} else if (dsi->flags & MIPI_DSI_MODE_VIDEO) {  		DRM_DEBUG_KMS("Non-burst video mode with sync events\n");  		pkt_seq = pkt_seq_video_non_burst_sync_events; +	} else { +		DRM_DEBUG_KMS("Command mode\n"); +		pkt_seq = pkt_seq_command_mode;  	}  	err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); @@ -456,61 +512,136 @@ static int tegra_output_dsi_enable(struct tegra_output *output)  	if (err < 0)  		return err; -	err = clk_enable(dsi->clk); -	if (err < 0) -		return err; - -	reset_control_deassert(dsi->rst); -  	value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) |  		DSI_CONTROL_LANES(dsi->lanes - 1) | -		DSI_CONTROL_SOURCE(dc->pipe); +		DSI_CONTROL_SOURCE(pipe);  	tegra_dsi_writel(dsi, value, DSI_CONTROL); -	tegra_dsi_writel(dsi, DSI_VIDEO_FIFO_DEPTH, DSI_MAX_THRESHOLD); +	tegra_dsi_writel(dsi, dsi->video_fifo_depth, DSI_MAX_THRESHOLD); -	value = DSI_HOST_CONTROL_HS | DSI_HOST_CONTROL_CS | -		DSI_HOST_CONTROL_ECC; +	value = DSI_HOST_CONTROL_HS;  	tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL);  	value = tegra_dsi_readl(dsi, DSI_CONTROL); +  	if (dsi->flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)  		value |= DSI_CONTROL_HS_CLK_CTRL; +  	value &= ~DSI_CONTROL_TX_TRIG(3); -	value &= ~DSI_CONTROL_DCS_ENABLE; + +	/* enable DCS commands for command mode */ +	if (dsi->flags & MIPI_DSI_MODE_VIDEO) +		value &= ~DSI_CONTROL_DCS_ENABLE; +	else +		value |= DSI_CONTROL_DCS_ENABLE; +  	value |= DSI_CONTROL_VIDEO_ENABLE;  	value &= ~DSI_CONTROL_HOST_ENABLE;  	tegra_dsi_writel(dsi, value, DSI_CONTROL); -	err = tegra_dsi_set_phy_timing(dsi); -	if (err < 0) -		return err; -  	for (i = 0; i < NUM_PKT_SEQ; i++)  		tegra_dsi_writel(dsi, pkt_seq[i], DSI_PKT_SEQ_0_LO + i); -	/* horizontal active pixels */ -	hact = mode->hdisplay * mul / div; +	if (dsi->flags & MIPI_DSI_MODE_VIDEO) { +		/* horizontal active pixels */ +		hact = mode->hdisplay * mul / div; -	/* horizontal sync width */ -	hsw = (mode->hsync_end - mode->hsync_start) * mul / div; -	hsw -= 10; +		/* horizontal sync width */ +		hsw = (mode->hsync_end - mode->hsync_start) * mul / div; +		hsw -= 10; -	/* horizontal back porch */ -	hbp = (mode->htotal - mode->hsync_end) * mul / div; -	hbp -= 14; +		/* horizontal back porch */ +		hbp = (mode->htotal - mode->hsync_end) * mul / div; +		hbp -= 14; -	/* horizontal front porch */ -	hfp = (mode->hsync_start  - mode->hdisplay) * mul / div; -	hfp -= 8; +		/* horizontal front porch */ +		hfp = (mode->hsync_start - mode->hdisplay) * mul / div; +		hfp -= 8; -	tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1); -	tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3); -	tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5); -	tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7); +		tegra_dsi_writel(dsi, hsw << 16 | 0, DSI_PKT_LEN_0_1); +		tegra_dsi_writel(dsi, hact << 16 | hbp, DSI_PKT_LEN_2_3); +		tegra_dsi_writel(dsi, hfp, DSI_PKT_LEN_4_5); +		tegra_dsi_writel(dsi, 0x0f0f << 16, DSI_PKT_LEN_6_7); -	/* set SOL delay */ -	tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY); +		/* set SOL delay (for non-burst mode only) */ +		tegra_dsi_writel(dsi, 8 * mul / div, DSI_SOL_DELAY); + +		/* TODO: implement ganged mode */ +	} else { +		u16 bytes; + +		if (dsi->master || dsi->slave) { +			/* +			 * For ganged mode, assume symmetric left-right mode. +			 */ +			bytes = 1 + (mode->hdisplay / 2) * mul / div; +		} else { +			/* 1 byte (DCS command) + pixel data */ +			bytes = 1 + mode->hdisplay * mul / div; +		} + +		tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_0_1); +		tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_2_3); +		tegra_dsi_writel(dsi, bytes << 16, DSI_PKT_LEN_4_5); +		tegra_dsi_writel(dsi, 0, DSI_PKT_LEN_6_7); + +		value = MIPI_DCS_WRITE_MEMORY_START << 8 | +			MIPI_DCS_WRITE_MEMORY_CONTINUE; +		tegra_dsi_writel(dsi, value, DSI_DCS_CMDS); + +		/* set SOL delay */ +		if (dsi->master || dsi->slave) { +			unsigned int lanes = tegra_dsi_get_lanes(dsi); +			unsigned long delay, bclk, bclk_ganged; + +			/* SOL to valid, valid to FIFO and FIFO write delay */ +			delay = 4 + 4 + 2; +			delay = DIV_ROUND_UP(delay * mul, div * lanes); +			/* FIFO read delay */ +			delay = delay + 6; + +			bclk = DIV_ROUND_UP(mode->htotal * mul, div * lanes); +			bclk_ganged = DIV_ROUND_UP(bclk * lanes / 2, lanes); +			value = bclk - bclk_ganged + delay + 20; +		} else { +			/* TODO: revisit for non-ganged mode */ +			value = 8 * mul / div; +		} + +		tegra_dsi_writel(dsi, value, DSI_SOL_DELAY); +	} + +	if (dsi->slave) { +		err = tegra_dsi_configure(dsi->slave, pipe, mode); +		if (err < 0) +			return err; + +		/* +		 * TODO: Support modes other than symmetrical left-right +		 * split. +		 */ +		tegra_dsi_ganged_enable(dsi, 0, mode->hdisplay / 2); +		tegra_dsi_ganged_enable(dsi->slave, mode->hdisplay / 2, +					mode->hdisplay / 2); +	} + +	return 0; +} + +static int tegra_output_dsi_enable(struct tegra_output *output) +{ +	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); +	const struct drm_display_mode *mode = &dc->base.mode; +	struct tegra_dsi *dsi = to_dsi(output); +	u32 value; +	int err; + +	if (dsi->enabled) +		return 0; + +	err = tegra_dsi_configure(dsi, dc->pipe, mode); +	if (err < 0) +		return err;  	/* enable display controller */  	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); @@ -531,28 +662,79 @@ static int tegra_output_dsi_enable(struct tegra_output *output)  	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);  	/* enable DSI controller */ -	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); -	value |= DSI_POWER_CONTROL_ENABLE; -	tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); +	tegra_dsi_enable(dsi);  	dsi->enabled = true;  	return 0;  } +static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout) +{ +	u32 value; + +	timeout = jiffies + msecs_to_jiffies(timeout); + +	while (time_before(jiffies, timeout)) { +		value = tegra_dsi_readl(dsi, DSI_STATUS); +		if (value & DSI_STATUS_IDLE) +			return 0; + +		usleep_range(1000, 2000); +	} + +	return -ETIMEDOUT; +} + +static void tegra_dsi_video_disable(struct tegra_dsi *dsi) +{ +	u32 value; + +	value = tegra_dsi_readl(dsi, DSI_CONTROL); +	value &= ~DSI_CONTROL_VIDEO_ENABLE; +	tegra_dsi_writel(dsi, value, DSI_CONTROL); + +	if (dsi->slave) +		tegra_dsi_video_disable(dsi->slave); +} + +static void tegra_dsi_ganged_disable(struct tegra_dsi *dsi) +{ +	tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_START); +	tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_SIZE); +	tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL); +} + +static void tegra_dsi_disable(struct tegra_dsi *dsi) +{ +	u32 value; + +	if (dsi->slave) { +		tegra_dsi_ganged_disable(dsi->slave); +		tegra_dsi_ganged_disable(dsi); +	} + +	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); +	value &= ~DSI_POWER_CONTROL_ENABLE; +	tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + +	if (dsi->slave) +		tegra_dsi_disable(dsi->slave); + +	usleep_range(5000, 10000); +} +  static int tegra_output_dsi_disable(struct tegra_output *output)  {  	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);  	struct tegra_dsi *dsi = to_dsi(output);  	unsigned long value; +	int err;  	if (!dsi->enabled)  		return 0; -	/* disable DSI controller */ -	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); -	value &= ~DSI_POWER_CONTROL_ENABLE; -	tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); +	tegra_dsi_video_disable(dsi);  	/*  	 * The following accesses registers of the display controller, so make @@ -576,39 +758,68 @@ static int tegra_output_dsi_disable(struct tegra_output *output)  		tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);  	} -	clk_disable(dsi->clk); +	err = tegra_dsi_wait_idle(dsi, 100); +	if (err < 0) +		dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err); + +	tegra_dsi_disable(dsi);  	dsi->enabled = false;  	return 0;  } +static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk, +				  unsigned int vrefresh) +{ +	unsigned int timeout; +	u32 value; + +	/* one frame high-speed transmission timeout */ +	timeout = (bclk / vrefresh) / 512; +	value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout); +	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0); + +	/* 2 ms peripheral timeout for panel */ +	timeout = 2 * bclk / 512 * 1000; +	value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000); +	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1); + +	value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); +	tegra_dsi_writel(dsi, value, DSI_TO_TALLY); + +	if (dsi->slave) +		tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh); +} +  static int tegra_output_dsi_setup_clock(struct tegra_output *output,  					struct clk *clk, unsigned long pclk,  					unsigned int *divp)  {  	struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc);  	struct drm_display_mode *mode = &dc->base.mode; -	unsigned int timeout, mul, div, vrefresh;  	struct tegra_dsi *dsi = to_dsi(output); -	unsigned long bclk, plld, value; +	unsigned int mul, div, vrefresh, lanes; +	unsigned long bclk, plld;  	int err; +	lanes = tegra_dsi_get_lanes(dsi); +  	err = tegra_dsi_get_muldiv(dsi->format, &mul, &div);  	if (err < 0)  		return err; -	DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", mul, div, dsi->lanes); +	DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", mul, div, lanes);  	vrefresh = drm_mode_vrefresh(mode);  	DRM_DEBUG_KMS("vrefresh: %u\n", vrefresh);  	/* compute byte clock */ -	bclk = (pclk * mul) / (div * dsi->lanes); +	bclk = (pclk * mul) / (div * lanes);  	/*  	 * Compute bit clock and round up to the next MHz.  	 */ -	plld = DIV_ROUND_UP(bclk * 8, 1000000) * 1000000; +	plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC;  	/*  	 * We divide the frequency by two here, but we make up for that by @@ -640,25 +851,17 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output,  	 * not working properly otherwise. Perhaps the PLLs cannot generate  	 * frequencies sufficiently high.  	 */ -	*divp = ((8 * mul) / (div * dsi->lanes)) - 2; +	*divp = ((8 * mul) / (div * lanes)) - 2;  	/*  	 * XXX: Move the below somewhere else so that we don't need to have  	 * access to the vrefresh in this function?  	 */ +	tegra_dsi_set_timeout(dsi, bclk, vrefresh); -	/* one frame high-speed transmission timeout */ -	timeout = (bclk / vrefresh) / 512; -	value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout); -	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0); - -	/* 2 ms peripheral timeout for panel */ -	timeout = 2 * bclk / 512 * 1000; -	value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000); -	tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1); - -	value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); -	tegra_dsi_writel(dsi, value, DSI_TO_TALLY); +	err = tegra_dsi_set_phy_timing(dsi); +	if (err < 0) +		return err;  	return 0;  } @@ -695,7 +898,7 @@ static int tegra_dsi_pad_enable(struct tegra_dsi *dsi)  static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi)  { -	unsigned long value; +	u32 value;  	tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0);  	tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1); @@ -720,14 +923,17 @@ static int tegra_dsi_init(struct host1x_client *client)  	struct tegra_dsi *dsi = host1x_client_to_dsi(client);  	int err; -	dsi->output.type = TEGRA_OUTPUT_DSI; -	dsi->output.dev = client->dev; -	dsi->output.ops = &dsi_ops; - -	err = tegra_output_init(drm, &dsi->output); -	if (err < 0) { -		dev_err(client->dev, "output setup failed: %d\n", err); -		return err; +	/* Gangsters must not register their own outputs. */ +	if (!dsi->master) { +		dsi->output.type = TEGRA_OUTPUT_DSI; +		dsi->output.dev = client->dev; +		dsi->output.ops = &dsi_ops; + +		err = tegra_output_init(drm, &dsi->output); +		if (err < 0) { +			dev_err(client->dev, "output setup failed: %d\n", err); +			return err; +		}  	}  	if (IS_ENABLED(CONFIG_DEBUG_FS)) { @@ -736,12 +942,6 @@ static int tegra_dsi_init(struct host1x_client *client)  			dev_err(dsi->dev, "debugfs setup failed: %d\n", err);  	} -	err = tegra_dsi_pad_calibrate(dsi); -	if (err < 0) { -		dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); -		return err; -	} -  	return 0;  } @@ -756,16 +956,20 @@ static int tegra_dsi_exit(struct host1x_client *client)  			dev_err(dsi->dev, "debugfs cleanup failed: %d\n", err);  	} -	err = tegra_output_disable(&dsi->output); -	if (err < 0) { -		dev_err(client->dev, "output failed to disable: %d\n", err); -		return err; -	} - -	err = tegra_output_exit(&dsi->output); -	if (err < 0) { -		dev_err(client->dev, "output cleanup failed: %d\n", err); -		return err; +	if (!dsi->master) { +		err = tegra_output_disable(&dsi->output); +		if (err < 0) { +			dev_err(client->dev, "output failed to disable: %d\n", +				err); +			return err; +		} + +		err = tegra_output_exit(&dsi->output); +		if (err < 0) { +			dev_err(client->dev, "output cleanup failed: %d\n", +				err); +			return err; +		}  	}  	return 0; @@ -792,20 +996,324 @@ static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi)  	return 0;  } +static const char * const error_report[16] = { +	"SoT Error", +	"SoT Sync Error", +	"EoT Sync Error", +	"Escape Mode Entry Command Error", +	"Low-Power Transmit Sync Error", +	"Peripheral Timeout Error", +	"False Control Error", +	"Contention Detected", +	"ECC Error, single-bit", +	"ECC Error, multi-bit", +	"Checksum Error", +	"DSI Data Type Not Recognized", +	"DSI VC ID Invalid", +	"Invalid Transmission Length", +	"Reserved", +	"DSI Protocol Violation", +}; + +static ssize_t tegra_dsi_read_response(struct tegra_dsi *dsi, +				       const struct mipi_dsi_msg *msg, +				       size_t count) +{ +	u8 *rx = msg->rx_buf; +	unsigned int i, j, k; +	size_t size = 0; +	u16 errors; +	u32 value; + +	/* read and parse packet header */ +	value = tegra_dsi_readl(dsi, DSI_RD_DATA); + +	switch (value & 0x3f) { +	case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: +		errors = (value >> 8) & 0xffff; +		dev_dbg(dsi->dev, "Acknowledge and error report: %04x\n", +			errors); +		for (i = 0; i < ARRAY_SIZE(error_report); i++) +			if (errors & BIT(i)) +				dev_dbg(dsi->dev, "  %2u: %s\n", i, +					error_report[i]); +		break; + +	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: +		rx[0] = (value >> 8) & 0xff; +		size = 1; +		break; + +	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: +		rx[0] = (value >>  8) & 0xff; +		rx[1] = (value >> 16) & 0xff; +		size = 2; +		break; + +	case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: +		size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff); +		break; + +	case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: +		size = ((value >> 8) & 0xff00) | ((value >> 8) & 0xff); +		break; + +	default: +		dev_err(dsi->dev, "unhandled response type: %02x\n", +			value & 0x3f); +		return -EPROTO; +	} + +	size = min(size, msg->rx_len); + +	if (msg->rx_buf && size > 0) { +		for (i = 0, j = 0; i < count - 1; i++, j += 4) { +			u8 *rx = msg->rx_buf + j; + +			value = tegra_dsi_readl(dsi, DSI_RD_DATA); + +			for (k = 0; k < 4 && (j + k) < msg->rx_len; k++) +				rx[j + k] = (value >> (k << 3)) & 0xff; +		} +	} + +	return size; +} + +static int tegra_dsi_transmit(struct tegra_dsi *dsi, unsigned long timeout) +{ +	tegra_dsi_writel(dsi, DSI_TRIGGER_HOST, DSI_TRIGGER); + +	timeout = jiffies + msecs_to_jiffies(timeout); + +	while (time_before(jiffies, timeout)) { +		u32 value = tegra_dsi_readl(dsi, DSI_TRIGGER); +		if ((value & DSI_TRIGGER_HOST) == 0) +			return 0; + +		usleep_range(1000, 2000); +	} + +	DRM_DEBUG_KMS("timeout waiting for transmission to complete\n"); +	return -ETIMEDOUT; +} + +static int tegra_dsi_wait_for_response(struct tegra_dsi *dsi, +				       unsigned long timeout) +{ +	timeout = jiffies + msecs_to_jiffies(250); + +	while (time_before(jiffies, timeout)) { +		u32 value = tegra_dsi_readl(dsi, DSI_STATUS); +		u8 count = value & 0x1f; + +		if (count > 0) +			return count; + +		usleep_range(1000, 2000); +	} + +	DRM_DEBUG_KMS("peripheral returned no data\n"); +	return -ETIMEDOUT; +} + +static void tegra_dsi_writesl(struct tegra_dsi *dsi, unsigned long offset, +			      const void *buffer, size_t size) +{ +	const u8 *buf = buffer; +	size_t i, j; +	u32 value; + +	for (j = 0; j < size; j += 4) { +		value = 0; + +		for (i = 0; i < 4 && j + i < size; i++) +			value |= buf[j + i] << (i << 3); + +		tegra_dsi_writel(dsi, value, DSI_WR_DATA); +	} +} + +static ssize_t tegra_dsi_host_transfer(struct mipi_dsi_host *host, +				       const struct mipi_dsi_msg *msg) +{ +	struct tegra_dsi *dsi = host_to_tegra(host); +	struct mipi_dsi_packet packet; +	const u8 *header; +	size_t count; +	ssize_t err; +	u32 value; + +	err = mipi_dsi_create_packet(&packet, msg); +	if (err < 0) +		return err; + +	header = packet.header; + +	/* maximum FIFO depth is 1920 words */ +	if (packet.size > dsi->video_fifo_depth * 4) +		return -ENOSPC; + +	/* reset underflow/overflow flags */ +	value = tegra_dsi_readl(dsi, DSI_STATUS); +	if (value & (DSI_STATUS_UNDERFLOW | DSI_STATUS_OVERFLOW)) { +		value = DSI_HOST_CONTROL_FIFO_RESET; +		tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); +		usleep_range(10, 20); +	} + +	value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); +	value |= DSI_POWER_CONTROL_ENABLE; +	tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + +	usleep_range(5000, 10000); + +	value = DSI_HOST_CONTROL_CRC_RESET | DSI_HOST_CONTROL_TX_TRIG_HOST | +		DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC; + +	if ((msg->flags & MIPI_DSI_MSG_USE_LPM) == 0) +		value |= DSI_HOST_CONTROL_HS; + +	/* +	 * The host FIFO has a maximum of 64 words, so larger transmissions +	 * need to use the video FIFO. +	 */ +	if (packet.size > dsi->host_fifo_depth * 4) +		value |= DSI_HOST_CONTROL_FIFO_SEL; + +	tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); + +	/* +	 * For reads and messages with explicitly requested ACK, generate a +	 * BTA sequence after the transmission of the packet. +	 */ +	if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) || +	    (msg->rx_buf && msg->rx_len > 0)) { +		value = tegra_dsi_readl(dsi, DSI_HOST_CONTROL); +		value |= DSI_HOST_CONTROL_PKT_BTA; +		tegra_dsi_writel(dsi, value, DSI_HOST_CONTROL); +	} + +	value = DSI_CONTROL_LANES(0) | DSI_CONTROL_HOST_ENABLE; +	tegra_dsi_writel(dsi, value, DSI_CONTROL); + +	/* write packet header, ECC is generated by hardware */ +	value = header[2] << 16 | header[1] << 8 | header[0]; +	tegra_dsi_writel(dsi, value, DSI_WR_DATA); + +	/* write payload (if any) */ +	if (packet.payload_length > 0) +		tegra_dsi_writesl(dsi, DSI_WR_DATA, packet.payload, +				  packet.payload_length); + +	err = tegra_dsi_transmit(dsi, 250); +	if (err < 0) +		return err; + +	if ((msg->flags & MIPI_DSI_MSG_REQ_ACK) || +	    (msg->rx_buf && msg->rx_len > 0)) { +		err = tegra_dsi_wait_for_response(dsi, 250); +		if (err < 0) +			return err; + +		count = err; + +		value = tegra_dsi_readl(dsi, DSI_RD_DATA); +		switch (value) { +		case 0x84: +			/* +			dev_dbg(dsi->dev, "ACK\n"); +			*/ +			break; + +		case 0x87: +			/* +			dev_dbg(dsi->dev, "ESCAPE\n"); +			*/ +			break; + +		default: +			dev_err(dsi->dev, "unknown status: %08x\n", value); +			break; +		} + +		if (count > 1) { +			err = tegra_dsi_read_response(dsi, msg, count); +			if (err < 0) +				dev_err(dsi->dev, +					"failed to parse response: %zd\n", +					err); +			else { +				/* +				 * For read commands, return the number of +				 * bytes returned by the peripheral. +				 */ +				count = err; +			} +		} +	} else { +		/* +		 * For write commands, we have transmitted the 4-byte header +		 * plus the variable-length payload. +		 */ +		count = 4 + packet.payload_length; +	} + +	return count; +} + +static int tegra_dsi_ganged_setup(struct tegra_dsi *dsi) +{ +	struct clk *parent; +	int err; + +	/* make sure both DSI controllers share the same PLL */ +	parent = clk_get_parent(dsi->slave->clk); +	if (!parent) +		return -EINVAL; + +	err = clk_set_parent(parent, dsi->clk_parent); +	if (err < 0) +		return err; + +	return 0; +} +  static int tegra_dsi_host_attach(struct mipi_dsi_host *host,  				 struct mipi_dsi_device *device)  {  	struct tegra_dsi *dsi = host_to_tegra(host); -	struct tegra_output *output = &dsi->output;  	dsi->flags = device->mode_flags;  	dsi->format = device->format;  	dsi->lanes = device->lanes; -	output->panel = of_drm_find_panel(device->dev.of_node); -	if (output->panel) { -		if (output->connector.dev) +	if (dsi->slave) { +		int err; + +		dev_dbg(dsi->dev, "attaching dual-channel device %s\n", +			dev_name(&device->dev)); + +		err = tegra_dsi_ganged_setup(dsi); +		if (err < 0) { +			dev_err(dsi->dev, "failed to set up ganged mode: %d\n", +				err); +			return err; +		} +	} + +	/* +	 * Slaves don't have a panel associated with them, so they provide +	 * merely the second channel. +	 */ +	if (!dsi->master) { +		struct tegra_output *output = &dsi->output; + +		output->panel = of_drm_find_panel(device->dev.of_node); +		if (output->panel && output->connector.dev) { +			drm_panel_attach(output->panel, &output->connector);  			drm_helper_hpd_irq_event(output->connector.dev); +		}  	}  	return 0; @@ -818,10 +1326,10 @@ static int tegra_dsi_host_detach(struct mipi_dsi_host *host,  	struct tegra_output *output = &dsi->output;  	if (output->panel && &device->dev == output->panel->dev) { +		output->panel = NULL; +  		if (output->connector.dev)  			drm_helper_hpd_irq_event(output->connector.dev); - -		output->panel = NULL;  	}  	return 0; @@ -830,8 +1338,29 @@ static int tegra_dsi_host_detach(struct mipi_dsi_host *host,  static const struct mipi_dsi_host_ops tegra_dsi_host_ops = {  	.attach = tegra_dsi_host_attach,  	.detach = tegra_dsi_host_detach, +	.transfer = tegra_dsi_host_transfer,  }; +static int tegra_dsi_ganged_probe(struct tegra_dsi *dsi) +{ +	struct device_node *np; + +	np = of_parse_phandle(dsi->dev->of_node, "nvidia,ganged-mode", 0); +	if (np) { +		struct platform_device *gangster = of_find_device_by_node(np); + +		dsi->slave = platform_get_drvdata(gangster); +		of_node_put(np); + +		if (!dsi->slave) +			return -EPROBE_DEFER; + +		dsi->slave->master = dsi; +	} + +	return 0; +} +  static int tegra_dsi_probe(struct platform_device *pdev)  {  	struct tegra_dsi *dsi; @@ -843,11 +1372,19 @@ static int tegra_dsi_probe(struct platform_device *pdev)  		return -ENOMEM;  	dsi->output.dev = dsi->dev = &pdev->dev; +	dsi->video_fifo_depth = 1920; +	dsi->host_fifo_depth = 64; + +	err = tegra_dsi_ganged_probe(dsi); +	if (err < 0) +		return err;  	err = tegra_output_probe(&dsi->output);  	if (err < 0)  		return err; +	dsi->output.connector.polled = DRM_CONNECTOR_POLL_HPD; +  	/*  	 * Assume these values by default. When a DSI peripheral driver  	 * attaches to the DSI host, the parameters will be taken from @@ -861,68 +1398,83 @@ static int tegra_dsi_probe(struct platform_device *pdev)  	if (IS_ERR(dsi->rst))  		return PTR_ERR(dsi->rst); +	err = reset_control_deassert(dsi->rst); +	if (err < 0) { +		dev_err(&pdev->dev, "failed to bring DSI out of reset: %d\n", +			err); +		return err; +	} +  	dsi->clk = devm_clk_get(&pdev->dev, NULL);  	if (IS_ERR(dsi->clk)) {  		dev_err(&pdev->dev, "cannot get DSI clock\n"); -		return PTR_ERR(dsi->clk); +		err = PTR_ERR(dsi->clk); +		goto reset;  	}  	err = clk_prepare_enable(dsi->clk);  	if (err < 0) {  		dev_err(&pdev->dev, "cannot enable DSI clock\n"); -		return err; +		goto reset;  	}  	dsi->clk_lp = devm_clk_get(&pdev->dev, "lp");  	if (IS_ERR(dsi->clk_lp)) {  		dev_err(&pdev->dev, "cannot get low-power clock\n"); -		return PTR_ERR(dsi->clk_lp); +		err = PTR_ERR(dsi->clk_lp); +		goto disable_clk;  	}  	err = clk_prepare_enable(dsi->clk_lp);  	if (err < 0) {  		dev_err(&pdev->dev, "cannot enable low-power clock\n"); -		return err; +		goto disable_clk;  	}  	dsi->clk_parent = devm_clk_get(&pdev->dev, "parent");  	if (IS_ERR(dsi->clk_parent)) {  		dev_err(&pdev->dev, "cannot get parent clock\n"); -		return PTR_ERR(dsi->clk_parent); -	} - -	err = clk_prepare_enable(dsi->clk_parent); -	if (err < 0) { -		dev_err(&pdev->dev, "cannot enable parent clock\n"); -		return err; +		err = PTR_ERR(dsi->clk_parent); +		goto disable_clk_lp;  	}  	dsi->vdd = devm_regulator_get(&pdev->dev, "avdd-dsi-csi");  	if (IS_ERR(dsi->vdd)) {  		dev_err(&pdev->dev, "cannot get VDD supply\n"); -		return PTR_ERR(dsi->vdd); +		err = PTR_ERR(dsi->vdd); +		goto disable_clk_lp;  	}  	err = regulator_enable(dsi->vdd);  	if (err < 0) {  		dev_err(&pdev->dev, "cannot enable VDD supply\n"); -		return err; +		goto disable_clk_lp;  	}  	err = tegra_dsi_setup_clocks(dsi);  	if (err < 0) {  		dev_err(&pdev->dev, "cannot setup clocks\n"); -		return err; +		goto disable_vdd;  	}  	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);  	dsi->regs = devm_ioremap_resource(&pdev->dev, regs); -	if (IS_ERR(dsi->regs)) -		return PTR_ERR(dsi->regs); +	if (IS_ERR(dsi->regs)) { +		err = PTR_ERR(dsi->regs); +		goto disable_vdd; +	}  	dsi->mipi = tegra_mipi_request(&pdev->dev); -	if (IS_ERR(dsi->mipi)) -		return PTR_ERR(dsi->mipi); +	if (IS_ERR(dsi->mipi)) { +		err = PTR_ERR(dsi->mipi); +		goto disable_vdd; +	} + +	err = tegra_dsi_pad_calibrate(dsi); +	if (err < 0) { +		dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); +		goto mipi_free; +	}  	dsi->host.ops = &tegra_dsi_host_ops;  	dsi->host.dev = &pdev->dev; @@ -930,7 +1482,7 @@ static int tegra_dsi_probe(struct platform_device *pdev)  	err = mipi_dsi_host_register(&dsi->host);  	if (err < 0) {  		dev_err(&pdev->dev, "failed to register DSI host: %d\n", err); -		return err; +		goto mipi_free;  	}  	INIT_LIST_HEAD(&dsi->client.list); @@ -941,12 +1493,26 @@ static int tegra_dsi_probe(struct platform_device *pdev)  	if (err < 0) {  		dev_err(&pdev->dev, "failed to register host1x client: %d\n",  			err); -		return err; +		goto unregister;  	}  	platform_set_drvdata(pdev, dsi);  	return 0; + +unregister: +	mipi_dsi_host_unregister(&dsi->host); +mipi_free: +	tegra_mipi_free(dsi->mipi); +disable_vdd: +	regulator_disable(dsi->vdd); +disable_clk_lp: +	clk_disable_unprepare(dsi->clk_lp); +disable_clk: +	clk_disable_unprepare(dsi->clk); +reset: +	reset_control_assert(dsi->rst); +	return err;  }  static int tegra_dsi_remove(struct platform_device *pdev) @@ -965,7 +1531,6 @@ static int tegra_dsi_remove(struct platform_device *pdev)  	tegra_mipi_free(dsi->mipi);  	regulator_disable(dsi->vdd); -	clk_disable_unprepare(dsi->clk_parent);  	clk_disable_unprepare(dsi->clk_lp);  	clk_disable_unprepare(dsi->clk);  	reset_control_assert(dsi->rst);  |