blob: a4f4343d008f5a87b3a12653c6c8c6d469c632e3 [file] [log] [blame]
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2013 NVIDIA Corporation
4 * Copyright (c) 2023 Svyatoslav Ryhel <clamor95@gmail.com>
5 */
6
7#include <dm.h>
8#include <clk.h>
9#include <misc.h>
10#include <linux/delay.h>
11#include <linux/iopoll.h>
12
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +020013#include <asm/arch/clock.h>
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +020014#include <asm/io.h>
15
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +020016/* MIPI control registers 0x00 ~ 0x74 */
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +020017struct mipi_ctlr {
18 uint mipi_cal_ctrl;
19 uint mipi_cal_autocal_ctrl;
20 uint mipi_cal_status;
21
22 uint unused1[2];
23
24 uint mipi_cal_config_csia;
25 uint mipi_cal_config_csib;
26 uint mipi_cal_config_csic;
27 uint mipi_cal_config_csid;
28 uint mipi_cal_config_csie;
29
30 uint unused2[4];
31
32 uint mipi_cal_config_dsia;
33 uint mipi_cal_config_dsib;
34 uint mipi_cal_config_dsic;
35 uint mipi_cal_config_dsid;
36
37 uint unused3[4];
38
39 uint mipi_cal_bias_pad_cfg0;
40 uint mipi_cal_bias_pad_cfg1;
41 uint mipi_cal_bias_pad_cfg2;
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +020042
43 uint mipi_cal_dsia_config_2;
44 uint mipi_cal_dsib_config_2;
45 uint mipi_cal_cilc_config_2;
46 uint mipi_cal_cild_config_2;
47 uint mipi_cal_csie_config_2;
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +020048};
49
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +020050#define MIPI_DSIA_PADS 0x60
51#define MIPI_DSIB_PADS 0x180
52
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +020053#define MIPI_CAL_CTRL_NOISE_FILTER(x) (((x) & 0xf) << 26)
54#define MIPI_CAL_CTRL_PRESCALE(x) (((x) & 0x3) << 24)
55#define MIPI_CAL_CTRL_CLKEN_OVR BIT(4)
56#define MIPI_CAL_CTRL_START BIT(0)
57
58#define MIPI_CAL_STATUS_DONE BIT(16)
59#define MIPI_CAL_STATUS_ACTIVE BIT(0)
60
61#define MIPI_CAL_OVERIDE(x) (((x) & 0x1) << 30)
62#define MIPI_CAL_SEL(x) (((x) & 0x1) << 21)
63#define MIPI_CAL_HSPDOS(x) (((x) & 0x1f) << 16)
64#define MIPI_CAL_HSPUOS(x) (((x) & 0x1f) << 8)
65#define MIPI_CAL_TERMOS(x) (((x) & 0x1f) << 0)
66
67#define MIPI_CAL_BIAS_PAD_PDVCLAMP BIT(1)
68#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF BIT(0)
69
70#define MIPI_CAL_BIAS_PAD_DRV_DN_REF(x) (((x) & 0x7) << 16)
71#define MIPI_CAL_BIAS_PAD_DRV_UP_REF(x) (((x) & 0x7) << 8)
72
73#define MIPI_CAL_BIAS_PAD_VCLAMP(x) (((x) & 0x7) << 16)
74#define MIPI_CAL_BIAS_PAD_VAUXP(x) (((x) & 0x7) << 4)
75#define MIPI_CAL_BIAS_PAD_PDVREG BIT(1)
76
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +020077#define MIPI_CAL_HSCLKPDOSDSI(x) (((x) & 0x1f) << 8)
78#define MIPI_CAL_HSCLKPUOSDSI(x) (((x) & 0x1f) << 0)
79
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +020080struct tegra_mipi_priv {
81 struct mipi_ctlr *mipi;
82 struct clk *mipi_cal;
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +020083 u32 version;
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +020084};
85
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +020086enum {
87 T114,
88 T124,
89};
90
91static void tegra114_mipi_pads_cal(struct tegra_mipi_priv *priv,
92 int calibration_pads)
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +020093{
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +020094 u32 value;
95
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +020096 value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) |
97 MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x4) |
98 MIPI_CAL_TERMOS(0x5);
99 writel(value, &priv->mipi->mipi_cal_config_dsia);
100 writel(value, &priv->mipi->mipi_cal_config_dsib);
101
102 /* Deselect PAD C */
103 value = readl(&priv->mipi->mipi_cal_config_dsic);
104 value &= ~(MIPI_CAL_SEL(0x1));
105 writel(value, &priv->mipi->mipi_cal_config_dsic);
106
107 /* Deselect PAD D */
108 value = readl(&priv->mipi->mipi_cal_config_dsid);
109 value &= ~(MIPI_CAL_SEL(0x1));
110 writel(value, &priv->mipi->mipi_cal_config_dsid);
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +0200111}
112
113static void tegra124_mipi_pads_cal(struct tegra_mipi_priv *priv,
114 int calibration_pads)
115{
116 u32 value;
117
118 /* Calibrate DSI-A */
119 if (calibration_pads == MIPI_DSIA_PADS) {
120 printf("Calibrating DSI-A pads\n");
121
122 value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) |
123 MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) |
124 MIPI_CAL_TERMOS(0x0);
125 writel(value, &priv->mipi->mipi_cal_config_dsia);
126 writel(value, &priv->mipi->mipi_cal_config_dsib);
127
128 value = MIPI_CAL_SEL(0x1) |
129 MIPI_CAL_HSCLKPDOSDSI(0x1) |
130 MIPI_CAL_HSCLKPUOSDSI(0x2);
131 writel(value, &priv->mipi->mipi_cal_dsia_config_2);
132 writel(value, &priv->mipi->mipi_cal_dsib_config_2);
133
134 /* Deselect PAD C */
135 value = readl(&priv->mipi->mipi_cal_cilc_config_2);
136 value &= ~(MIPI_CAL_SEL(0x1));
137 writel(value, &priv->mipi->mipi_cal_cilc_config_2);
138
139 /* Deselect PAD D */
140 value = readl(&priv->mipi->mipi_cal_cild_config_2);
141 value &= ~(MIPI_CAL_SEL(0x1));
142 writel(value, &priv->mipi->mipi_cal_cild_config_2);
143 }
144
145 /* Calibrate DSI-B */
146 if (calibration_pads == MIPI_DSIB_PADS) {
147 printf("Calibrating DSI-B pads\n");
148
149 value = MIPI_CAL_OVERIDE(0x0) | MIPI_CAL_SEL(0x1) |
150 MIPI_CAL_HSPDOS(0x0) | MIPI_CAL_HSPUOS(0x0) |
151 MIPI_CAL_TERMOS(0x0);
152 writel(value, &priv->mipi->mipi_cal_config_csic);
153 writel(value, &priv->mipi->mipi_cal_config_csid);
154
155 value = MIPI_CAL_SEL(0x1) |
156 MIPI_CAL_HSCLKPDOSDSI(0x1) |
157 MIPI_CAL_HSCLKPUOSDSI(0x2);
158 writel(value, &priv->mipi->mipi_cal_cilc_config_2);
159 writel(value, &priv->mipi->mipi_cal_cild_config_2);
160
161 /* Deselect PAD A */
162 value = readl(&priv->mipi->mipi_cal_dsia_config_2);
163 value &= ~(MIPI_CAL_SEL(0x1));
164 writel(value, &priv->mipi->mipi_cal_dsia_config_2);
165
166 /* Deselect PAD B */
167 value = readl(&priv->mipi->mipi_cal_dsib_config_2);
168 value &= ~(MIPI_CAL_SEL(0x1));
169 writel(value, &priv->mipi->mipi_cal_dsib_config_2);
170 }
171}
172
173static int tegra_mipi_calibrate(struct udevice *dev, int offset, const void *buf,
174 int size)
175{
176 struct tegra_mipi_priv *priv = dev_get_priv(dev);
177 u32 value;
178
179 value = MIPI_CAL_BIAS_PAD_DRV_DN_REF(0x2) |
180 MIPI_CAL_BIAS_PAD_DRV_UP_REF(0x0);
181 writel(value, &priv->mipi->mipi_cal_bias_pad_cfg1);
182
183 value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2);
184 value &= ~MIPI_CAL_BIAS_PAD_VCLAMP(0x7);
185 value &= ~MIPI_CAL_BIAS_PAD_VAUXP(0x7);
186 writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2);
187
188 switch (priv->version) {
189 case T114:
190 tegra114_mipi_pads_cal(priv, offset);
191 break;
192
193 case T124:
194 tegra124_mipi_pads_cal(priv, offset);
195 break;
196
197 default:
198 return -EINVAL;
199 }
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +0200200
201 value = readl(&priv->mipi->mipi_cal_ctrl);
202 value &= ~MIPI_CAL_CTRL_NOISE_FILTER(0xf);
203 value &= ~MIPI_CAL_CTRL_PRESCALE(0x3);
204 value |= MIPI_CAL_CTRL_NOISE_FILTER(0xa) |
205 MIPI_CAL_CTRL_PRESCALE(0x2) |
206 MIPI_CAL_CTRL_CLKEN_OVR;
207 writel(value, &priv->mipi->mipi_cal_ctrl);
208
209 /* clear any pending status bits */
210 value = readl(&priv->mipi->mipi_cal_status);
211 writel(value, &priv->mipi->mipi_cal_status);
212
213 value = readl(&priv->mipi->mipi_cal_ctrl);
214 value |= MIPI_CAL_CTRL_START;
215 writel(value, &priv->mipi->mipi_cal_ctrl);
216
217 /*
218 * Wait for min 72uS to let calibration logic finish calibration
219 * sequence codes before waiting for pads idle state to apply the
220 * results.
221 */
222 udelay(80);
223
224 return readl_poll_sleep_timeout(&priv->mipi->mipi_cal_status, value,
225 !(value & MIPI_CAL_STATUS_ACTIVE) &&
226 (value & MIPI_CAL_STATUS_DONE), 100,
227 250000);
228}
229
230static int tegra_mipi_enable(struct udevice *dev, bool val)
231{
232 struct tegra_mipi_priv *priv = dev_get_priv(dev);
233 u32 value;
234
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +0200235 reset_set_enable(priv->mipi_cal->id, 1);
236 mdelay(100);
237 reset_set_enable(priv->mipi_cal->id, 0);
238 mdelay(1);
239
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +0200240 clk_enable(priv->mipi_cal);
241
242 value = readl(&priv->mipi->mipi_cal_bias_pad_cfg0);
243 value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
244 value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
245 writel(value, &priv->mipi->mipi_cal_bias_pad_cfg0);
246
247 value = readl(&priv->mipi->mipi_cal_bias_pad_cfg2);
248 value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
249 writel(value, &priv->mipi->mipi_cal_bias_pad_cfg2);
250
251 return 0;
252}
253
254static const struct misc_ops tegra_mipi_ops = {
255 .write = tegra_mipi_calibrate,
256 .set_enabled = tegra_mipi_enable,
257};
258
259static int tegra_mipi_probe(struct udevice *dev)
260{
261 struct tegra_mipi_priv *priv = dev_get_priv(dev);
262
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +0200263 priv->version = dev_get_driver_data(dev);
264
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +0200265 priv->mipi = (struct mipi_ctlr *)dev_read_addr_ptr(dev);
266 if (!priv->mipi) {
267 log_debug("%s: no MIPI controller address\n", __func__);
268 return -EINVAL;
269 }
270
271 priv->mipi_cal = devm_clk_get(dev, NULL);
272 if (IS_ERR(priv->mipi_cal)) {
273 log_debug("%s: Could not get MIPI clock: %ld\n",
274 __func__, PTR_ERR(priv->mipi_cal));
275 return PTR_ERR(priv->mipi_cal);
276 }
277
278 return 0;
279}
280
281static const struct udevice_id tegra_mipi_ids[] = {
Svyatoslav Ryhel30cdefe2024-11-18 08:58:18 +0200282 { .compatible = "nvidia,tegra114-mipi", .data = T114 },
283 { .compatible = "nvidia,tegra124-mipi", .data = T124 },
Svyatoslav Ryhelba04d192024-01-23 19:16:28 +0200284 { }
285};
286
287U_BOOT_DRIVER(tegra_mipi) = {
288 .name = "tegra_mipi",
289 .id = UCLASS_MISC,
290 .ops = &tegra_mipi_ops,
291 .of_match = tegra_mipi_ids,
292 .probe = tegra_mipi_probe,
293 .priv_auto = sizeof(struct tegra_mipi_priv),
294};