mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 05:50:17 +00:00 
			
		
		
		
	video: tegra20: dsi: add ganged mode support
Implement ganged mode support for the Tegra DSI driver. The DSI host controller to gang up with is specified via a phandle in the device tree and the resolved DSI host controller used for the programming of the ganged-mode registers. Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
This commit is contained in:
		
							parent
							
								
									68ebc7c86b
								
							
						
					
					
						commit
						744bce5123
					
				@ -20,6 +20,7 @@
 | 
				
			|||||||
#include <asm/gpio.h>
 | 
					#include <asm/gpio.h>
 | 
				
			||||||
#include <asm/io.h>
 | 
					#include <asm/io.h>
 | 
				
			||||||
#include <asm/arch/clock.h>
 | 
					#include <asm/arch/clock.h>
 | 
				
			||||||
 | 
					#include <asm/arch-tegra/clk_rst.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "tegra-dc.h"
 | 
					#include "tegra-dc.h"
 | 
				
			||||||
#include "tegra-dsi.h"
 | 
					#include "tegra-dsi.h"
 | 
				
			||||||
@ -50,6 +51,10 @@ struct tegra_dsi_priv {
 | 
				
			|||||||
	int host_fifo_depth;
 | 
						int host_fifo_depth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	u32 version;
 | 
						u32 version;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* for ganged-mode support */
 | 
				
			||||||
 | 
						struct udevice *master;
 | 
				
			||||||
 | 
						struct udevice *slave;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void tegra_dc_enable_controller(struct udevice *dev)
 | 
					static void tegra_dc_enable_controller(struct udevice *dev)
 | 
				
			||||||
@ -595,6 +600,17 @@ static void tegra_dsi_set_phy_timing(struct dsi_timing_reg *ptiming,
 | 
				
			|||||||
	writel(value, &ptiming->dsi_bta_timing);
 | 
						writel(value, &ptiming->dsi_bta_timing);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void tegra_dsi_ganged_enable(struct udevice *dev, unsigned int start,
 | 
				
			||||||
 | 
									    unsigned int size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct tegra_dsi_priv *priv = dev_get_priv(dev);
 | 
				
			||||||
 | 
						struct dsi_ganged_mode_reg *ganged = &priv->dsi->ganged;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						writel(start, &ganged->ganged_mode_start);
 | 
				
			||||||
 | 
						writel(size << 16 | size, &ganged->ganged_mode_size);
 | 
				
			||||||
 | 
						writel(DSI_GANGED_MODE_CONTROL_ENABLE, &ganged->ganged_mode_ctrl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void tegra_dsi_configure(struct udevice *dev,
 | 
					static void tegra_dsi_configure(struct udevice *dev,
 | 
				
			||||||
				unsigned long mode_flags)
 | 
									unsigned long mode_flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -679,9 +695,19 @@ static void tegra_dsi_configure(struct udevice *dev,
 | 
				
			|||||||
		writel(hact << 16 | hbp, &len->dsi_pkt_len_2_3);
 | 
							writel(hact << 16 | hbp, &len->dsi_pkt_len_2_3);
 | 
				
			||||||
		writel(hfp, &len->dsi_pkt_len_4_5);
 | 
							writel(hfp, &len->dsi_pkt_len_4_5);
 | 
				
			||||||
		writel(0x0f0f << 16, &len->dsi_pkt_len_6_7);
 | 
							writel(0x0f0f << 16, &len->dsi_pkt_len_6_7);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* set SOL delay (for non-burst mode only) */
 | 
				
			||||||
 | 
							writel(8 * mul / div, &misc->dsi_sol_delay);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							if (priv->master || priv->slave) {
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
								 * For ganged mode, assume symmetric left-right mode.
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
 | 
								value = 1 + (timing->hactive.typ / 2) * mul / div;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			/* 1 byte (DCS command) + pixel data */
 | 
								/* 1 byte (DCS command) + pixel data */
 | 
				
			||||||
			value = 1 + timing->hactive.typ * mul / div;
 | 
								value = 1 + timing->hactive.typ * mul / div;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		writel(0, &len->dsi_pkt_len_0_1);
 | 
							writel(0, &len->dsi_pkt_len_0_1);
 | 
				
			||||||
		writel(value << 16, &len->dsi_pkt_len_2_3);
 | 
							writel(value << 16, &len->dsi_pkt_len_2_3);
 | 
				
			||||||
@ -691,10 +717,40 @@ static void tegra_dsi_configure(struct udevice *dev,
 | 
				
			|||||||
		value = MIPI_DCS_WRITE_MEMORY_START << 8 |
 | 
							value = MIPI_DCS_WRITE_MEMORY_START << 8 |
 | 
				
			||||||
			MIPI_DCS_WRITE_MEMORY_CONTINUE;
 | 
								MIPI_DCS_WRITE_MEMORY_CONTINUE;
 | 
				
			||||||
		writel(value, &len->dsi_dcs_cmds);
 | 
							writel(value, &len->dsi_dcs_cmds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* set SOL delay */
 | 
				
			||||||
 | 
							if (priv->master || priv->slave) {
 | 
				
			||||||
 | 
								unsigned long delay, bclk, bclk_ganged;
 | 
				
			||||||
 | 
								unsigned int lanes = device->lanes;
 | 
				
			||||||
 | 
								unsigned long htotal = timing->hactive.typ + timing->hfront_porch.typ +
 | 
				
			||||||
 | 
										       timing->hback_porch.typ + timing->hsync_len.typ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* 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(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;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* set SOL delay (for non-burst mode only) */
 | 
							writel(value, &misc->dsi_sol_delay);
 | 
				
			||||||
	writel(8 * mul / div, &misc->dsi_sol_delay);
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (priv->slave) {
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * TODO: Support modes other than symmetrical left-right
 | 
				
			||||||
 | 
							 * split.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							tegra_dsi_ganged_enable(dev, 0, timing->hactive.typ / 2);
 | 
				
			||||||
 | 
							tegra_dsi_ganged_enable(priv->slave, timing->hactive.typ / 2,
 | 
				
			||||||
 | 
										timing->hactive.typ / 2);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int tegra_dsi_encoder_enable(struct udevice *dev)
 | 
					static int tegra_dsi_encoder_enable(struct udevice *dev)
 | 
				
			||||||
@ -774,6 +830,9 @@ static int tegra_dsi_encoder_enable(struct udevice *dev)
 | 
				
			|||||||
	value |= DSI_POWER_CONTROL_ENABLE;
 | 
						value |= DSI_POWER_CONTROL_ENABLE;
 | 
				
			||||||
	writel(value, &misc->dsi_pwr_ctrl);
 | 
						writel(value, &misc->dsi_pwr_ctrl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (priv->slave)
 | 
				
			||||||
 | 
							tegra_dsi_encoder_enable(priv->slave);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -803,6 +862,14 @@ static void tegra_dsi_init_clocks(struct udevice *dev)
 | 
				
			|||||||
	unsigned int mul, div;
 | 
						unsigned int mul, div;
 | 
				
			||||||
	unsigned long bclk, plld;
 | 
						unsigned long bclk, plld;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!priv->slave) {
 | 
				
			||||||
 | 
							/* Change DSIB clock parent to match DSIA */
 | 
				
			||||||
 | 
							struct clk_rst_ctlr *clkrst =
 | 
				
			||||||
 | 
								(struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							clrbits_le32(&clkrst->plld2.pll_base, BIT(25)); /* DSIB_CLK_SRC */
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tegra_dsi_get_muldiv(device->format, &mul, &div);
 | 
						tegra_dsi_get_muldiv(device->format, &mul, &div);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bclk = (priv->timing.pixelclock.typ * mul) /
 | 
						bclk = (priv->timing.pixelclock.typ * mul) /
 | 
				
			||||||
@ -854,6 +921,24 @@ static void tegra_dsi_init_clocks(struct udevice *dev)
 | 
				
			|||||||
	reset_set_enable(priv->dsi_clk, 0);
 | 
						reset_set_enable(priv->dsi_clk, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int tegra_dsi_ganged_probe(struct udevice *dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct tegra_dsi_priv *mpriv = dev_get_priv(dev);
 | 
				
			||||||
 | 
						struct udevice *gangster;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uclass_get_device_by_phandle(UCLASS_PANEL, dev,
 | 
				
			||||||
 | 
									     "nvidia,ganged-mode", &gangster);
 | 
				
			||||||
 | 
						if (gangster) {
 | 
				
			||||||
 | 
							/* Ganged mode is set */
 | 
				
			||||||
 | 
							struct tegra_dsi_priv *spriv = dev_get_priv(gangster);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							mpriv->slave = gangster;
 | 
				
			||||||
 | 
							spriv->master = dev;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int tegra_dsi_bridge_probe(struct udevice *dev)
 | 
					static int tegra_dsi_bridge_probe(struct udevice *dev)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct tegra_dsi_priv *priv = dev_get_priv(dev);
 | 
						struct tegra_dsi_priv *priv = dev_get_priv(dev);
 | 
				
			||||||
@ -873,6 +958,8 @@ static int tegra_dsi_bridge_probe(struct udevice *dev)
 | 
				
			|||||||
	priv->video_fifo_depth = 1920;
 | 
						priv->video_fifo_depth = 1920;
 | 
				
			||||||
	priv->host_fifo_depth = 64;
 | 
						priv->host_fifo_depth = 64;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tegra_dsi_ganged_probe(dev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = reset_get_by_name(dev, "dsi", &reset_ctl);
 | 
						ret = reset_get_by_name(dev, "dsi", &reset_ctl);
 | 
				
			||||||
	if (ret) {
 | 
						if (ret) {
 | 
				
			||||||
		log_debug("%s: reset_get_by_name() failed: %d\n",
 | 
							log_debug("%s: reset_get_by_name() failed: %d\n",
 | 
				
			||||||
 | 
				
			|||||||
@ -98,9 +98,9 @@ struct dsi_timeout_reg {
 | 
				
			|||||||
	uint dsi_to_tally;		/* _DSI_TO_TALLY_0 */
 | 
						uint dsi_to_tally;		/* _DSI_TO_TALLY_0 */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* DSI PAD control register 0x04b ~ 0x04e */
 | 
					/* DSI PAD control register 0x04b ~ 0x052 */
 | 
				
			||||||
struct dsi_pad_ctrl_reg {
 | 
					struct dsi_pad_ctrl_reg {
 | 
				
			||||||
	/* Address 0x04b ~ 0x04e */
 | 
						/* Address 0x04b ~ 0x052 */
 | 
				
			||||||
	uint pad_ctrl;			/* _PAD_CONTROL_0 */
 | 
						uint pad_ctrl;			/* _PAD_CONTROL_0 */
 | 
				
			||||||
	uint pad_ctrl_cd;		/* _PAD_CONTROL_CD_0 */
 | 
						uint pad_ctrl_cd;		/* _PAD_CONTROL_CD_0 */
 | 
				
			||||||
	uint pad_cd_status;		/* _PAD_CD_STATUS_0 */
 | 
						uint pad_cd_status;		/* _PAD_CD_STATUS_0 */
 | 
				
			||||||
@ -111,6 +111,14 @@ struct dsi_pad_ctrl_reg {
 | 
				
			|||||||
	uint pad_ctrl_4;		/* _PAD_CONTROL_4 */
 | 
						uint pad_ctrl_4;		/* _PAD_CONTROL_4 */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* DSI ganged mode register 0x053 ~ 0x04e */
 | 
				
			||||||
 | 
					struct dsi_ganged_mode_reg {
 | 
				
			||||||
 | 
						/* Address 0x053 ~ 0x055 */
 | 
				
			||||||
 | 
						uint ganged_mode_ctrl;		/* _DSI_GANGED_MODE_CONTROL_0 */
 | 
				
			||||||
 | 
						uint ganged_mode_start;		/* _DSI_GANGED_MODE_START_0 */
 | 
				
			||||||
 | 
						uint ganged_mode_size;		/* _DSI_GANGED_MODE_SIZE_0 */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Display Serial Interface (DSI_) regs */
 | 
					/* Display Serial Interface (DSI_) regs */
 | 
				
			||||||
struct dsi_ctlr {
 | 
					struct dsi_ctlr {
 | 
				
			||||||
	struct dsi_syncpt_reg syncpt;	/* SYNCPT register 0x000 ~ 0x002 */
 | 
						struct dsi_syncpt_reg syncpt;	/* SYNCPT register 0x000 ~ 0x002 */
 | 
				
			||||||
@ -133,6 +141,7 @@ struct dsi_ctlr {
 | 
				
			|||||||
	uint reserved5[4];		/* reserved_5[4] */
 | 
						uint reserved5[4];		/* reserved_5[4] */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct dsi_pad_ctrl_reg pad;	/* PAD registers 0x04b ~ 0x04e */
 | 
						struct dsi_pad_ctrl_reg pad;	/* PAD registers 0x04b ~ 0x04e */
 | 
				
			||||||
 | 
						struct dsi_ganged_mode_reg ganged; /* GANGED registers 0x053 ~ 0x055 */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DSI_POWER_CONTROL_ENABLE	BIT(0)
 | 
					#define DSI_POWER_CONTROL_ENABLE	BIT(0)
 | 
				
			||||||
@ -202,6 +211,8 @@ struct dsi_ctlr {
 | 
				
			|||||||
#define DSI_PAD_PREEMP_PD(x)		(((x) & 0x3) << 4)
 | 
					#define DSI_PAD_PREEMP_PD(x)		(((x) & 0x3) << 4)
 | 
				
			||||||
#define DSI_PAD_PREEMP_PU(x)		(((x) & 0x3) << 0)
 | 
					#define DSI_PAD_PREEMP_PU(x)		(((x) & 0x3) << 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DSI_GANGED_MODE_CONTROL_ENABLE	BIT(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * pixel format as used in the DSI_CONTROL_FORMAT field
 | 
					 * pixel format as used in the DSI_CONTROL_FORMAT field
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user