xref: /linux/drivers/gpu/host1x/tegra114-mipi.c (revision 00c6649bafef628955569dd39a59e3170e48f7b5)
1*c888c4c8SSvyatoslav Ryhel /*
2*c888c4c8SSvyatoslav Ryhel  * Copyright (C) 2013 NVIDIA Corporation
3*c888c4c8SSvyatoslav Ryhel  *
4*c888c4c8SSvyatoslav Ryhel  * Permission to use, copy, modify, distribute, and sell this software and its
5*c888c4c8SSvyatoslav Ryhel  * documentation for any purpose is hereby granted without fee, provided that
6*c888c4c8SSvyatoslav Ryhel  * the above copyright notice appear in all copies and that both that copyright
7*c888c4c8SSvyatoslav Ryhel  * notice and this permission notice appear in supporting documentation, and
8*c888c4c8SSvyatoslav Ryhel  * that the name of the copyright holders not be used in advertising or
9*c888c4c8SSvyatoslav Ryhel  * publicity pertaining to distribution of the software without specific,
10*c888c4c8SSvyatoslav Ryhel  * written prior permission.  The copyright holders make no representations
11*c888c4c8SSvyatoslav Ryhel  * about the suitability of this software for any purpose.  It is provided "as
12*c888c4c8SSvyatoslav Ryhel  * is" without express or implied warranty.
13*c888c4c8SSvyatoslav Ryhel  *
14*c888c4c8SSvyatoslav Ryhel  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15*c888c4c8SSvyatoslav Ryhel  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16*c888c4c8SSvyatoslav Ryhel  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17*c888c4c8SSvyatoslav Ryhel  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18*c888c4c8SSvyatoslav Ryhel  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19*c888c4c8SSvyatoslav Ryhel  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20*c888c4c8SSvyatoslav Ryhel  * OF THIS SOFTWARE.
21*c888c4c8SSvyatoslav Ryhel  */
22*c888c4c8SSvyatoslav Ryhel 
23*c888c4c8SSvyatoslav Ryhel #include <linux/clk.h>
24*c888c4c8SSvyatoslav Ryhel #include <linux/host1x.h>
25*c888c4c8SSvyatoslav Ryhel #include <linux/io.h>
26*c888c4c8SSvyatoslav Ryhel #include <linux/iopoll.h>
27*c888c4c8SSvyatoslav Ryhel #include <linux/of_platform.h>
28*c888c4c8SSvyatoslav Ryhel #include <linux/platform_device.h>
29*c888c4c8SSvyatoslav Ryhel #include <linux/slab.h>
30*c888c4c8SSvyatoslav Ryhel #include <linux/tegra-mipi-cal.h>
31*c888c4c8SSvyatoslav Ryhel 
32*c888c4c8SSvyatoslav Ryhel #include "dev.h"
33*c888c4c8SSvyatoslav Ryhel 
34*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CTRL			0x00
35*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CTRL_NOISE_FILTER(x)	(((x) & 0xf) << 26)
36*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CTRL_PRESCALE(x)	(((x) & 0x3) << 24)
37*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CTRL_CLKEN_OVR		BIT(4)
38*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CTRL_START		BIT(0)
39*c888c4c8SSvyatoslav Ryhel 
40*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_AUTOCAL_CTRL		0x01
41*c888c4c8SSvyatoslav Ryhel 
42*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_STATUS			0x02
43*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_STATUS_DONE		BIT(16)
44*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_STATUS_ACTIVE		BIT(0)
45*c888c4c8SSvyatoslav Ryhel 
46*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIA		0x05
47*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIB		0x06
48*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIC		0x07
49*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSID		0x08
50*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIE		0x09
51*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIF		0x0a
52*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIA		0x0e
53*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIB		0x0f
54*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIC		0x10
55*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSID		0x11
56*c888c4c8SSvyatoslav Ryhel 
57*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIA_CLK	0x19
58*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIB_CLK	0x1a
59*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIAB_CLK	0x1b
60*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSIC_CLK	0x1c
61*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSICD_CLK	0x1c
62*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_DSID_CLK	0x1d
63*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_CSIE_CLK	0x1d
64*c888c4c8SSvyatoslav Ryhel 
65*c888c4c8SSvyatoslav Ryhel /* for data and clock lanes */
66*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_SELECT		BIT(21)
67*c888c4c8SSvyatoslav Ryhel 
68*c888c4c8SSvyatoslav Ryhel /* for data lanes */
69*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_HSPDOS(x)	(((x) & 0x1f) << 16)
70*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_HSPUOS(x)	(((x) & 0x1f) <<  8)
71*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_TERMOS(x)	(((x) & 0x1f) <<  0)
72*c888c4c8SSvyatoslav Ryhel 
73*c888c4c8SSvyatoslav Ryhel /* for clock lanes */
74*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_HSCLKPDOSD(x)	(((x) & 0x1f) <<  8)
75*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_CONFIG_HSCLKPUOSD(x)	(((x) & 0x1f) <<  0)
76*c888c4c8SSvyatoslav Ryhel 
77*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_CFG0		0x16
78*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_PDVCLAMP	BIT(1)
79*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF	BIT(0)
80*c888c4c8SSvyatoslav Ryhel 
81*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_CFG1		0x17
82*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
83*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
84*c888c4c8SSvyatoslav Ryhel 
85*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_CFG2		0x18
86*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_VCLAMP(x)	(((x) & 0x7) << 16)
87*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_VAUXP(x)	(((x) & 0x7) << 4)
88*c888c4c8SSvyatoslav Ryhel #define MIPI_CAL_BIAS_PAD_PDVREG	BIT(1)
89*c888c4c8SSvyatoslav Ryhel 
90*c888c4c8SSvyatoslav Ryhel struct tegra_mipi_pad {
91*c888c4c8SSvyatoslav Ryhel 	unsigned long data;
92*c888c4c8SSvyatoslav Ryhel 	unsigned long clk;
93*c888c4c8SSvyatoslav Ryhel };
94*c888c4c8SSvyatoslav Ryhel 
95*c888c4c8SSvyatoslav Ryhel struct tegra_mipi_soc {
96*c888c4c8SSvyatoslav Ryhel 	bool has_clk_lane;
97*c888c4c8SSvyatoslav Ryhel 	const struct tegra_mipi_pad *pads;
98*c888c4c8SSvyatoslav Ryhel 	unsigned int num_pads;
99*c888c4c8SSvyatoslav Ryhel 
100*c888c4c8SSvyatoslav Ryhel 	bool clock_enable_override;
101*c888c4c8SSvyatoslav Ryhel 	bool needs_vclamp_ref;
102*c888c4c8SSvyatoslav Ryhel 
103*c888c4c8SSvyatoslav Ryhel 	/* bias pad configuration settings */
104*c888c4c8SSvyatoslav Ryhel 	u8 pad_drive_down_ref;
105*c888c4c8SSvyatoslav Ryhel 	u8 pad_drive_up_ref;
106*c888c4c8SSvyatoslav Ryhel 
107*c888c4c8SSvyatoslav Ryhel 	u8 pad_vclamp_level;
108*c888c4c8SSvyatoslav Ryhel 	u8 pad_vauxp_level;
109*c888c4c8SSvyatoslav Ryhel 
110*c888c4c8SSvyatoslav Ryhel 	/* calibration settings for data lanes */
111*c888c4c8SSvyatoslav Ryhel 	u8 hspdos;
112*c888c4c8SSvyatoslav Ryhel 	u8 hspuos;
113*c888c4c8SSvyatoslav Ryhel 	u8 termos;
114*c888c4c8SSvyatoslav Ryhel 
115*c888c4c8SSvyatoslav Ryhel 	/* calibration settings for clock lanes */
116*c888c4c8SSvyatoslav Ryhel 	u8 hsclkpdos;
117*c888c4c8SSvyatoslav Ryhel 	u8 hsclkpuos;
118*c888c4c8SSvyatoslav Ryhel };
119*c888c4c8SSvyatoslav Ryhel 
120*c888c4c8SSvyatoslav Ryhel struct tegra_mipi {
121*c888c4c8SSvyatoslav Ryhel 	const struct tegra_mipi_soc *soc;
122*c888c4c8SSvyatoslav Ryhel 	struct device *dev;
123*c888c4c8SSvyatoslav Ryhel 	void __iomem *regs;
124*c888c4c8SSvyatoslav Ryhel 	struct mutex lock; /* for register access */
125*c888c4c8SSvyatoslav Ryhel 	struct clk *clk;
126*c888c4c8SSvyatoslav Ryhel 
127*c888c4c8SSvyatoslav Ryhel 	unsigned long usage_count;
128*c888c4c8SSvyatoslav Ryhel };
129*c888c4c8SSvyatoslav Ryhel 
130*c888c4c8SSvyatoslav Ryhel static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
131*c888c4c8SSvyatoslav Ryhel 				   unsigned long offset)
132*c888c4c8SSvyatoslav Ryhel {
133*c888c4c8SSvyatoslav Ryhel 	return readl(mipi->regs + (offset << 2));
134*c888c4c8SSvyatoslav Ryhel }
135*c888c4c8SSvyatoslav Ryhel 
136*c888c4c8SSvyatoslav Ryhel static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
137*c888c4c8SSvyatoslav Ryhel 				     unsigned long offset)
138*c888c4c8SSvyatoslav Ryhel {
139*c888c4c8SSvyatoslav Ryhel 	writel(value, mipi->regs + (offset << 2));
140*c888c4c8SSvyatoslav Ryhel }
141*c888c4c8SSvyatoslav Ryhel 
142*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_power_up(struct tegra_mipi *mipi)
143*c888c4c8SSvyatoslav Ryhel {
144*c888c4c8SSvyatoslav Ryhel 	u32 value;
145*c888c4c8SSvyatoslav Ryhel 	int err;
146*c888c4c8SSvyatoslav Ryhel 
147*c888c4c8SSvyatoslav Ryhel 	err = clk_enable(mipi->clk);
148*c888c4c8SSvyatoslav Ryhel 	if (err < 0)
149*c888c4c8SSvyatoslav Ryhel 		return err;
150*c888c4c8SSvyatoslav Ryhel 
151*c888c4c8SSvyatoslav Ryhel 	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
152*c888c4c8SSvyatoslav Ryhel 	value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
153*c888c4c8SSvyatoslav Ryhel 
154*c888c4c8SSvyatoslav Ryhel 	if (mipi->soc->needs_vclamp_ref)
155*c888c4c8SSvyatoslav Ryhel 		value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
156*c888c4c8SSvyatoslav Ryhel 
157*c888c4c8SSvyatoslav Ryhel 	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
158*c888c4c8SSvyatoslav Ryhel 
159*c888c4c8SSvyatoslav Ryhel 	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
160*c888c4c8SSvyatoslav Ryhel 	value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
161*c888c4c8SSvyatoslav Ryhel 	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
162*c888c4c8SSvyatoslav Ryhel 
163*c888c4c8SSvyatoslav Ryhel 	clk_disable(mipi->clk);
164*c888c4c8SSvyatoslav Ryhel 
165*c888c4c8SSvyatoslav Ryhel 	return 0;
166*c888c4c8SSvyatoslav Ryhel }
167*c888c4c8SSvyatoslav Ryhel 
168*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_power_down(struct tegra_mipi *mipi)
169*c888c4c8SSvyatoslav Ryhel {
170*c888c4c8SSvyatoslav Ryhel 	u32 value;
171*c888c4c8SSvyatoslav Ryhel 	int err;
172*c888c4c8SSvyatoslav Ryhel 
173*c888c4c8SSvyatoslav Ryhel 	err = clk_enable(mipi->clk);
174*c888c4c8SSvyatoslav Ryhel 	if (err < 0)
175*c888c4c8SSvyatoslav Ryhel 		return err;
176*c888c4c8SSvyatoslav Ryhel 
177*c888c4c8SSvyatoslav Ryhel 	/*
178*c888c4c8SSvyatoslav Ryhel 	 * The MIPI_CAL_BIAS_PAD_PDVREG controls a voltage regulator that
179*c888c4c8SSvyatoslav Ryhel 	 * supplies the DSI pads. This must be kept enabled until none of the
180*c888c4c8SSvyatoslav Ryhel 	 * DSI lanes are used anymore.
181*c888c4c8SSvyatoslav Ryhel 	 */
182*c888c4c8SSvyatoslav Ryhel 	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
183*c888c4c8SSvyatoslav Ryhel 	value |= MIPI_CAL_BIAS_PAD_PDVREG;
184*c888c4c8SSvyatoslav Ryhel 	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
185*c888c4c8SSvyatoslav Ryhel 
186*c888c4c8SSvyatoslav Ryhel 	/*
187*c888c4c8SSvyatoslav Ryhel 	 * MIPI_CAL_BIAS_PAD_PDVCLAMP and MIPI_CAL_BIAS_PAD_E_VCLAMP_REF
188*c888c4c8SSvyatoslav Ryhel 	 * control a regulator that supplies current to the pre-driver logic.
189*c888c4c8SSvyatoslav Ryhel 	 * Powering down this regulator causes DSI to fail, so it must remain
190*c888c4c8SSvyatoslav Ryhel 	 * powered on until none of the DSI lanes are used anymore.
191*c888c4c8SSvyatoslav Ryhel 	 */
192*c888c4c8SSvyatoslav Ryhel 	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG0);
193*c888c4c8SSvyatoslav Ryhel 
194*c888c4c8SSvyatoslav Ryhel 	if (mipi->soc->needs_vclamp_ref)
195*c888c4c8SSvyatoslav Ryhel 		value &= ~MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
196*c888c4c8SSvyatoslav Ryhel 
197*c888c4c8SSvyatoslav Ryhel 	value |= MIPI_CAL_BIAS_PAD_PDVCLAMP;
198*c888c4c8SSvyatoslav Ryhel 	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
199*c888c4c8SSvyatoslav Ryhel 
200*c888c4c8SSvyatoslav Ryhel 	return 0;
201*c888c4c8SSvyatoslav Ryhel }
202*c888c4c8SSvyatoslav Ryhel 
203*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_enable(struct tegra_mipi_device *mipidev)
204*c888c4c8SSvyatoslav Ryhel {
205*c888c4c8SSvyatoslav Ryhel 	struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
206*c888c4c8SSvyatoslav Ryhel 	int err = 0;
207*c888c4c8SSvyatoslav Ryhel 
208*c888c4c8SSvyatoslav Ryhel 	mutex_lock(&mipi->lock);
209*c888c4c8SSvyatoslav Ryhel 
210*c888c4c8SSvyatoslav Ryhel 	if (mipi->usage_count++ == 0)
211*c888c4c8SSvyatoslav Ryhel 		err = tegra114_mipi_power_up(mipi);
212*c888c4c8SSvyatoslav Ryhel 
213*c888c4c8SSvyatoslav Ryhel 	mutex_unlock(&mipi->lock);
214*c888c4c8SSvyatoslav Ryhel 
215*c888c4c8SSvyatoslav Ryhel 	return err;
216*c888c4c8SSvyatoslav Ryhel }
217*c888c4c8SSvyatoslav Ryhel 
218*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_disable(struct tegra_mipi_device *mipidev)
219*c888c4c8SSvyatoslav Ryhel {
220*c888c4c8SSvyatoslav Ryhel 	struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
221*c888c4c8SSvyatoslav Ryhel 	int err = 0;
222*c888c4c8SSvyatoslav Ryhel 
223*c888c4c8SSvyatoslav Ryhel 	mutex_lock(&mipi->lock);
224*c888c4c8SSvyatoslav Ryhel 
225*c888c4c8SSvyatoslav Ryhel 	if (--mipi->usage_count == 0)
226*c888c4c8SSvyatoslav Ryhel 		err = tegra114_mipi_power_down(mipi);
227*c888c4c8SSvyatoslav Ryhel 
228*c888c4c8SSvyatoslav Ryhel 	mutex_unlock(&mipi->lock);
229*c888c4c8SSvyatoslav Ryhel 
230*c888c4c8SSvyatoslav Ryhel 	return err;
231*c888c4c8SSvyatoslav Ryhel }
232*c888c4c8SSvyatoslav Ryhel 
233*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_finish_calibration(struct tegra_mipi_device *mipidev)
234*c888c4c8SSvyatoslav Ryhel {
235*c888c4c8SSvyatoslav Ryhel 	struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
236*c888c4c8SSvyatoslav Ryhel 	void __iomem *status_reg = mipi->regs + (MIPI_CAL_STATUS << 2);
237*c888c4c8SSvyatoslav Ryhel 	u32 value;
238*c888c4c8SSvyatoslav Ryhel 	int err;
239*c888c4c8SSvyatoslav Ryhel 
240*c888c4c8SSvyatoslav Ryhel 	err = readl_relaxed_poll_timeout(status_reg, value,
241*c888c4c8SSvyatoslav Ryhel 					 !(value & MIPI_CAL_STATUS_ACTIVE) &&
242*c888c4c8SSvyatoslav Ryhel 					 (value & MIPI_CAL_STATUS_DONE), 50,
243*c888c4c8SSvyatoslav Ryhel 					 250000);
244*c888c4c8SSvyatoslav Ryhel 	mutex_unlock(&mipi->lock);
245*c888c4c8SSvyatoslav Ryhel 	clk_disable(mipi->clk);
246*c888c4c8SSvyatoslav Ryhel 
247*c888c4c8SSvyatoslav Ryhel 	return err;
248*c888c4c8SSvyatoslav Ryhel }
249*c888c4c8SSvyatoslav Ryhel 
250*c888c4c8SSvyatoslav Ryhel static int tegra114_mipi_start_calibration(struct tegra_mipi_device *mipidev)
251*c888c4c8SSvyatoslav Ryhel {
252*c888c4c8SSvyatoslav Ryhel 	struct tegra_mipi *mipi = platform_get_drvdata(mipidev->pdev);
253*c888c4c8SSvyatoslav Ryhel 	const struct tegra_mipi_soc *soc = mipi->soc;
254*c888c4c8SSvyatoslav Ryhel 	unsigned int i;
255*c888c4c8SSvyatoslav Ryhel 	u32 value;
256*c888c4c8SSvyatoslav Ryhel 	int err;
257*c888c4c8SSvyatoslav Ryhel 
258*c888c4c8SSvyatoslav Ryhel 	err = clk_enable(mipi->clk);
259*c888c4c8SSvyatoslav Ryhel 	if (err < 0)
260*c888c4c8SSvyatoslav Ryhel 		return err;
261*c888c4c8SSvyatoslav Ryhel 
262*c888c4c8SSvyatoslav Ryhel 	mutex_lock(&mipi->lock);
263*c888c4c8SSvyatoslav Ryhel 
264*c888c4c8SSvyatoslav Ryhel 	value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(soc->pad_drive_down_ref) |
265*c888c4c8SSvyatoslav Ryhel 		MIPI_CAL_BIAS_PAD_DRV_UP_REF(soc->pad_drive_up_ref);
266*c888c4c8SSvyatoslav Ryhel 	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG1);
267*c888c4c8SSvyatoslav Ryhel 
268*c888c4c8SSvyatoslav Ryhel 	value = tegra_mipi_readl(mipi, MIPI_CAL_BIAS_PAD_CFG2);
269*c888c4c8SSvyatoslav Ryhel 	value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
270*c888c4c8SSvyatoslav Ryhel 	value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
271*c888c4c8SSvyatoslav Ryhel 	value |= MIPI_CAL_BIAS_PAD_VCLAMP(soc->pad_vclamp_level);
272*c888c4c8SSvyatoslav Ryhel 	value |= MIPI_CAL_BIAS_PAD_VAUXP(soc->pad_vauxp_level);
273*c888c4c8SSvyatoslav Ryhel 	tegra_mipi_writel(mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
274*c888c4c8SSvyatoslav Ryhel 
275*c888c4c8SSvyatoslav Ryhel 	for (i = 0; i < soc->num_pads; i++) {
276*c888c4c8SSvyatoslav Ryhel 		u32 clk = 0, data = 0;
277*c888c4c8SSvyatoslav Ryhel 
278*c888c4c8SSvyatoslav Ryhel 		if (mipidev->pads & BIT(i)) {
279*c888c4c8SSvyatoslav Ryhel 			data = MIPI_CAL_CONFIG_SELECT |
280*c888c4c8SSvyatoslav Ryhel 			       MIPI_CAL_CONFIG_HSPDOS(soc->hspdos) |
281*c888c4c8SSvyatoslav Ryhel 			       MIPI_CAL_CONFIG_HSPUOS(soc->hspuos) |
282*c888c4c8SSvyatoslav Ryhel 			       MIPI_CAL_CONFIG_TERMOS(soc->termos);
283*c888c4c8SSvyatoslav Ryhel 			clk = MIPI_CAL_CONFIG_SELECT |
284*c888c4c8SSvyatoslav Ryhel 			      MIPI_CAL_CONFIG_HSCLKPDOSD(soc->hsclkpdos) |
285*c888c4c8SSvyatoslav Ryhel 			      MIPI_CAL_CONFIG_HSCLKPUOSD(soc->hsclkpuos);
286*c888c4c8SSvyatoslav Ryhel 		}
287*c888c4c8SSvyatoslav Ryhel 
288*c888c4c8SSvyatoslav Ryhel 		tegra_mipi_writel(mipi, data, soc->pads[i].data);
289*c888c4c8SSvyatoslav Ryhel 
290*c888c4c8SSvyatoslav Ryhel 		if (soc->has_clk_lane && soc->pads[i].clk != 0)
291*c888c4c8SSvyatoslav Ryhel 			tegra_mipi_writel(mipi, clk, soc->pads[i].clk);
292*c888c4c8SSvyatoslav Ryhel 	}
293*c888c4c8SSvyatoslav Ryhel 
294*c888c4c8SSvyatoslav Ryhel 	value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
295*c888c4c8SSvyatoslav Ryhel 	value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
296*c888c4c8SSvyatoslav Ryhel 	value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
297*c888c4c8SSvyatoslav Ryhel 	value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa);
298*c888c4c8SSvyatoslav Ryhel 	value |= MIPI_CAL_CTRL_PRESCALE(0x2);
299*c888c4c8SSvyatoslav Ryhel 
300*c888c4c8SSvyatoslav Ryhel 	if (!soc->clock_enable_override)
301*c888c4c8SSvyatoslav Ryhel 		value &= ~MIPI_CAL_CTRL_CLKEN_OVR;
302*c888c4c8SSvyatoslav Ryhel 	else
303*c888c4c8SSvyatoslav Ryhel 		value |= MIPI_CAL_CTRL_CLKEN_OVR;
304*c888c4c8SSvyatoslav Ryhel 
305*c888c4c8SSvyatoslav Ryhel 	tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
306*c888c4c8SSvyatoslav Ryhel 
307*c888c4c8SSvyatoslav Ryhel 	/* clear any pending status bits */
308*c888c4c8SSvyatoslav Ryhel 	value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
309*c888c4c8SSvyatoslav Ryhel 	tegra_mipi_writel(mipi, value, MIPI_CAL_STATUS);
310*c888c4c8SSvyatoslav Ryhel 
311*c888c4c8SSvyatoslav Ryhel 	value = tegra_mipi_readl(mipi, MIPI_CAL_CTRL);
312*c888c4c8SSvyatoslav Ryhel 	value |= MIPI_CAL_CTRL_START;
313*c888c4c8SSvyatoslav Ryhel 	tegra_mipi_writel(mipi, value, MIPI_CAL_CTRL);
314*c888c4c8SSvyatoslav Ryhel 
315*c888c4c8SSvyatoslav Ryhel 	/*
316*c888c4c8SSvyatoslav Ryhel 	 * Wait for min 72uS to let calibration logic finish calibration
317*c888c4c8SSvyatoslav Ryhel 	 * sequence codes before waiting for pads idle state to apply the
318*c888c4c8SSvyatoslav Ryhel 	 * results.
319*c888c4c8SSvyatoslav Ryhel 	 */
320*c888c4c8SSvyatoslav Ryhel 	usleep_range(75, 80);
321*c888c4c8SSvyatoslav Ryhel 
322*c888c4c8SSvyatoslav Ryhel 	return 0;
323*c888c4c8SSvyatoslav Ryhel }
324*c888c4c8SSvyatoslav Ryhel 
325*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_ops tegra114_mipi_ops = {
326*c888c4c8SSvyatoslav Ryhel 	.enable = tegra114_mipi_enable,
327*c888c4c8SSvyatoslav Ryhel 	.disable = tegra114_mipi_disable,
328*c888c4c8SSvyatoslav Ryhel 	.start_calibration = tegra114_mipi_start_calibration,
329*c888c4c8SSvyatoslav Ryhel 	.finish_calibration = tegra114_mipi_finish_calibration,
330*c888c4c8SSvyatoslav Ryhel };
331*c888c4c8SSvyatoslav Ryhel 
332*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
333*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIA },
334*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIB },
335*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIC },
336*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSID },
337*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIE },
338*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_DSIA },
339*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_DSIB },
340*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_DSIC },
341*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_DSID },
342*c888c4c8SSvyatoslav Ryhel };
343*c888c4c8SSvyatoslav Ryhel 
344*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_soc tegra114_mipi_soc = {
345*c888c4c8SSvyatoslav Ryhel 	.has_clk_lane = false,
346*c888c4c8SSvyatoslav Ryhel 	.pads = tegra114_mipi_pads,
347*c888c4c8SSvyatoslav Ryhel 	.num_pads = ARRAY_SIZE(tegra114_mipi_pads),
348*c888c4c8SSvyatoslav Ryhel 	.clock_enable_override = true,
349*c888c4c8SSvyatoslav Ryhel 	.needs_vclamp_ref = true,
350*c888c4c8SSvyatoslav Ryhel 	.pad_drive_down_ref = 0x2,
351*c888c4c8SSvyatoslav Ryhel 	.pad_drive_up_ref = 0x0,
352*c888c4c8SSvyatoslav Ryhel 	.pad_vclamp_level = 0x0,
353*c888c4c8SSvyatoslav Ryhel 	.pad_vauxp_level = 0x0,
354*c888c4c8SSvyatoslav Ryhel 	.hspdos = 0x0,
355*c888c4c8SSvyatoslav Ryhel 	.hspuos = 0x4,
356*c888c4c8SSvyatoslav Ryhel 	.termos = 0x5,
357*c888c4c8SSvyatoslav Ryhel 	.hsclkpdos = 0x0,
358*c888c4c8SSvyatoslav Ryhel 	.hsclkpuos = 0x4,
359*c888c4c8SSvyatoslav Ryhel };
360*c888c4c8SSvyatoslav Ryhel 
361*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
362*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
363*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
364*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
365*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
366*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK  },
367*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK  },
368*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK  },
369*c888c4c8SSvyatoslav Ryhel };
370*c888c4c8SSvyatoslav Ryhel 
371*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_soc tegra124_mipi_soc = {
372*c888c4c8SSvyatoslav Ryhel 	.has_clk_lane = true,
373*c888c4c8SSvyatoslav Ryhel 	.pads = tegra124_mipi_pads,
374*c888c4c8SSvyatoslav Ryhel 	.num_pads = ARRAY_SIZE(tegra124_mipi_pads),
375*c888c4c8SSvyatoslav Ryhel 	.clock_enable_override = true,
376*c888c4c8SSvyatoslav Ryhel 	.needs_vclamp_ref = true,
377*c888c4c8SSvyatoslav Ryhel 	.pad_drive_down_ref = 0x2,
378*c888c4c8SSvyatoslav Ryhel 	.pad_drive_up_ref = 0x0,
379*c888c4c8SSvyatoslav Ryhel 	.pad_vclamp_level = 0x0,
380*c888c4c8SSvyatoslav Ryhel 	.pad_vauxp_level = 0x0,
381*c888c4c8SSvyatoslav Ryhel 	.hspdos = 0x0,
382*c888c4c8SSvyatoslav Ryhel 	.hspuos = 0x0,
383*c888c4c8SSvyatoslav Ryhel 	.termos = 0x0,
384*c888c4c8SSvyatoslav Ryhel 	.hsclkpdos = 0x1,
385*c888c4c8SSvyatoslav Ryhel 	.hsclkpuos = 0x2,
386*c888c4c8SSvyatoslav Ryhel };
387*c888c4c8SSvyatoslav Ryhel 
388*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_soc tegra132_mipi_soc = {
389*c888c4c8SSvyatoslav Ryhel 	.has_clk_lane = true,
390*c888c4c8SSvyatoslav Ryhel 	.pads = tegra124_mipi_pads,
391*c888c4c8SSvyatoslav Ryhel 	.num_pads = ARRAY_SIZE(tegra124_mipi_pads),
392*c888c4c8SSvyatoslav Ryhel 	.clock_enable_override = false,
393*c888c4c8SSvyatoslav Ryhel 	.needs_vclamp_ref = false,
394*c888c4c8SSvyatoslav Ryhel 	.pad_drive_down_ref = 0x0,
395*c888c4c8SSvyatoslav Ryhel 	.pad_drive_up_ref = 0x3,
396*c888c4c8SSvyatoslav Ryhel 	.pad_vclamp_level = 0x0,
397*c888c4c8SSvyatoslav Ryhel 	.pad_vauxp_level = 0x0,
398*c888c4c8SSvyatoslav Ryhel 	.hspdos = 0x0,
399*c888c4c8SSvyatoslav Ryhel 	.hspuos = 0x0,
400*c888c4c8SSvyatoslav Ryhel 	.termos = 0x0,
401*c888c4c8SSvyatoslav Ryhel 	.hsclkpdos = 0x3,
402*c888c4c8SSvyatoslav Ryhel 	.hsclkpuos = 0x2,
403*c888c4c8SSvyatoslav Ryhel };
404*c888c4c8SSvyatoslav Ryhel 
405*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_pad tegra210_mipi_pads[] = {
406*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIA, .clk = 0 },
407*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIB, .clk = 0 },
408*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIC, .clk = 0 },
409*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSID, .clk = 0 },
410*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIE, .clk = 0 },
411*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_CSIF, .clk = 0 },
412*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIA_CLK },
413*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIB_CLK },
414*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_DSIC, .clk = MIPI_CAL_CONFIG_DSIC_CLK },
415*c888c4c8SSvyatoslav Ryhel 	{ .data = MIPI_CAL_CONFIG_DSID, .clk = MIPI_CAL_CONFIG_DSID_CLK },
416*c888c4c8SSvyatoslav Ryhel };
417*c888c4c8SSvyatoslav Ryhel 
418*c888c4c8SSvyatoslav Ryhel static const struct tegra_mipi_soc tegra210_mipi_soc = {
419*c888c4c8SSvyatoslav Ryhel 	.has_clk_lane = true,
420*c888c4c8SSvyatoslav Ryhel 	.pads = tegra210_mipi_pads,
421*c888c4c8SSvyatoslav Ryhel 	.num_pads = ARRAY_SIZE(tegra210_mipi_pads),
422*c888c4c8SSvyatoslav Ryhel 	.clock_enable_override = true,
423*c888c4c8SSvyatoslav Ryhel 	.needs_vclamp_ref = false,
424*c888c4c8SSvyatoslav Ryhel 	.pad_drive_down_ref = 0x0,
425*c888c4c8SSvyatoslav Ryhel 	.pad_drive_up_ref = 0x3,
426*c888c4c8SSvyatoslav Ryhel 	.pad_vclamp_level = 0x1,
427*c888c4c8SSvyatoslav Ryhel 	.pad_vauxp_level = 0x1,
428*c888c4c8SSvyatoslav Ryhel 	.hspdos = 0x0,
429*c888c4c8SSvyatoslav Ryhel 	.hspuos = 0x2,
430*c888c4c8SSvyatoslav Ryhel 	.termos = 0x0,
431*c888c4c8SSvyatoslav Ryhel 	.hsclkpdos = 0x0,
432*c888c4c8SSvyatoslav Ryhel 	.hsclkpuos = 0x2,
433*c888c4c8SSvyatoslav Ryhel };
434*c888c4c8SSvyatoslav Ryhel 
435*c888c4c8SSvyatoslav Ryhel static const struct of_device_id tegra_mipi_of_match[] = {
436*c888c4c8SSvyatoslav Ryhel 	{ .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
437*c888c4c8SSvyatoslav Ryhel 	{ .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
438*c888c4c8SSvyatoslav Ryhel 	{ .compatible = "nvidia,tegra132-mipi", .data = &tegra132_mipi_soc },
439*c888c4c8SSvyatoslav Ryhel 	{ .compatible = "nvidia,tegra210-mipi", .data = &tegra210_mipi_soc },
440*c888c4c8SSvyatoslav Ryhel 	{ },
441*c888c4c8SSvyatoslav Ryhel };
442*c888c4c8SSvyatoslav Ryhel 
443*c888c4c8SSvyatoslav Ryhel static int tegra_mipi_probe(struct platform_device *pdev)
444*c888c4c8SSvyatoslav Ryhel {
445*c888c4c8SSvyatoslav Ryhel 	const struct of_device_id *match;
446*c888c4c8SSvyatoslav Ryhel 	struct tegra_mipi *mipi;
447*c888c4c8SSvyatoslav Ryhel 
448*c888c4c8SSvyatoslav Ryhel 	match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
449*c888c4c8SSvyatoslav Ryhel 	if (!match)
450*c888c4c8SSvyatoslav Ryhel 		return -ENODEV;
451*c888c4c8SSvyatoslav Ryhel 
452*c888c4c8SSvyatoslav Ryhel 	mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
453*c888c4c8SSvyatoslav Ryhel 	if (!mipi)
454*c888c4c8SSvyatoslav Ryhel 		return -ENOMEM;
455*c888c4c8SSvyatoslav Ryhel 
456*c888c4c8SSvyatoslav Ryhel 	mipi->soc = match->data;
457*c888c4c8SSvyatoslav Ryhel 	mipi->dev = &pdev->dev;
458*c888c4c8SSvyatoslav Ryhel 
459*c888c4c8SSvyatoslav Ryhel 	mipi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
460*c888c4c8SSvyatoslav Ryhel 	if (IS_ERR(mipi->regs))
461*c888c4c8SSvyatoslav Ryhel 		return PTR_ERR(mipi->regs);
462*c888c4c8SSvyatoslav Ryhel 
463*c888c4c8SSvyatoslav Ryhel 	mutex_init(&mipi->lock);
464*c888c4c8SSvyatoslav Ryhel 
465*c888c4c8SSvyatoslav Ryhel 	mipi->clk = devm_clk_get_prepared(&pdev->dev, NULL);
466*c888c4c8SSvyatoslav Ryhel 	if (IS_ERR(mipi->clk)) {
467*c888c4c8SSvyatoslav Ryhel 		dev_err(&pdev->dev, "failed to get clock\n");
468*c888c4c8SSvyatoslav Ryhel 		return PTR_ERR(mipi->clk);
469*c888c4c8SSvyatoslav Ryhel 	}
470*c888c4c8SSvyatoslav Ryhel 
471*c888c4c8SSvyatoslav Ryhel 	platform_set_drvdata(pdev, mipi);
472*c888c4c8SSvyatoslav Ryhel 
473*c888c4c8SSvyatoslav Ryhel 	return devm_tegra_mipi_add_provider(&pdev->dev, pdev->dev.of_node,
474*c888c4c8SSvyatoslav Ryhel 					    &tegra114_mipi_ops);
475*c888c4c8SSvyatoslav Ryhel }
476*c888c4c8SSvyatoslav Ryhel 
477*c888c4c8SSvyatoslav Ryhel struct platform_driver tegra_mipi_driver = {
478*c888c4c8SSvyatoslav Ryhel 	.driver = {
479*c888c4c8SSvyatoslav Ryhel 		.name = "tegra-mipi",
480*c888c4c8SSvyatoslav Ryhel 		.of_match_table = tegra_mipi_of_match,
481*c888c4c8SSvyatoslav Ryhel 	},
482*c888c4c8SSvyatoslav Ryhel 	.probe = tegra_mipi_probe,
483*c888c4c8SSvyatoslav Ryhel };
484