blob: 22e20c2399c51a4f3b6453de6f3794c74584c2e3 [file] [log] [blame]
Thierry Reding4de6a2d2013-09-02 09:48:53 +02001/*
2 * Copyright (C) 2013 NVIDIA Corporation
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include <linux/clk.h>
24#include <linux/delay.h>
Thierry Redingaef03d32013-11-08 13:28:34 +010025#include <linux/host1x.h>
Thierry Reding4de6a2d2013-09-02 09:48:53 +020026#include <linux/io.h>
27#include <linux/of_platform.h>
28#include <linux/platform_device.h>
29#include <linux/slab.h>
30
Thierry Redingaef03d32013-11-08 13:28:34 +010031#include "dev.h"
32
Thierry Reding4de6a2d2013-09-02 09:48:53 +020033#define MIPI_CAL_CTRL 0x00
34#define MIPI_CAL_CTRL_START (1 << 0)
35
36#define MIPI_CAL_AUTOCAL_CTRL 0x01
37
38#define MIPI_CAL_STATUS 0x02
39#define MIPI_CAL_STATUS_DONE (1 << 16)
40#define MIPI_CAL_STATUS_ACTIVE (1 << 0)
41
42#define MIPI_CAL_CONFIG_CSIA 0x05
43#define MIPI_CAL_CONFIG_CSIB 0x06
44#define MIPI_CAL_CONFIG_CSIC 0x07
45#define MIPI_CAL_CONFIG_CSID 0x08
46#define MIPI_CAL_CONFIG_CSIE 0x09
47#define MIPI_CAL_CONFIG_DSIA 0x0e
48#define MIPI_CAL_CONFIG_DSIB 0x0f
49#define MIPI_CAL_CONFIG_DSIC 0x10
50#define MIPI_CAL_CONFIG_DSID 0x11
51
Sean Paul08a15cc2014-09-10 10:52:04 -040052#define MIPI_CAL_CONFIG_DSIAB_CLK 0x19
53#define MIPI_CAL_CONFIG_DSICD_CLK 0x1a
54#define MIPI_CAL_CONFIG_CSIAB_CLK 0x1b
55#define MIPI_CAL_CONFIG_CSICD_CLK 0x1c
56#define MIPI_CAL_CONFIG_CSIE_CLK 0x1d
57
58/* for data and clock lanes */
Thierry Reding4de6a2d2013-09-02 09:48:53 +020059#define MIPI_CAL_CONFIG_SELECT (1 << 21)
Sean Paul08a15cc2014-09-10 10:52:04 -040060
61/* for data lanes */
Thierry Reding4de6a2d2013-09-02 09:48:53 +020062#define MIPI_CAL_CONFIG_HSPDOS(x) (((x) & 0x1f) << 16)
63#define MIPI_CAL_CONFIG_HSPUOS(x) (((x) & 0x1f) << 8)
64#define MIPI_CAL_CONFIG_TERMOS(x) (((x) & 0x1f) << 0)
65
Sean Paul08a15cc2014-09-10 10:52:04 -040066/* for clock lanes */
67#define MIPI_CAL_CONFIG_HSCLKPDOSD(x) (((x) & 0x1f) << 8)
68#define MIPI_CAL_CONFIG_HSCLKPUOSD(x) (((x) & 0x1f) << 0)
69
Thierry Reding4de6a2d2013-09-02 09:48:53 +020070#define MIPI_CAL_BIAS_PAD_CFG0 0x16
71#define MIPI_CAL_BIAS_PAD_PDVCLAMP (1 << 1)
72#define MIPI_CAL_BIAS_PAD_E_VCLAMP_REF (1 << 0)
73
74#define MIPI_CAL_BIAS_PAD_CFG1 0x17
75
76#define MIPI_CAL_BIAS_PAD_CFG2 0x18
77#define MIPI_CAL_BIAS_PAD_PDVREG (1 << 1)
78
Sean Paul08a15cc2014-09-10 10:52:04 -040079struct tegra_mipi_pad {
80 unsigned long data;
81 unsigned long clk;
82};
83
84struct tegra_mipi_soc {
85 bool has_clk_lane;
86 const struct tegra_mipi_pad *pads;
87 unsigned int num_pads;
Thierry Reding4de6a2d2013-09-02 09:48:53 +020088};
89
90struct tegra_mipi {
Sean Paul08a15cc2014-09-10 10:52:04 -040091 const struct tegra_mipi_soc *soc;
Thierry Reding4de6a2d2013-09-02 09:48:53 +020092 void __iomem *regs;
93 struct mutex lock;
94 struct clk *clk;
95};
96
97struct tegra_mipi_device {
98 struct platform_device *pdev;
99 struct tegra_mipi *mipi;
100 struct device *device;
101 unsigned long pads;
102};
103
Thierry Reding57b17ae2014-10-02 14:33:31 +0200104static inline u32 tegra_mipi_readl(struct tegra_mipi *mipi,
105 unsigned long offset)
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200106{
Thierry Reding57b17ae2014-10-02 14:33:31 +0200107 return readl(mipi->regs + (offset << 2));
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200108}
109
Thierry Reding57b17ae2014-10-02 14:33:31 +0200110static inline void tegra_mipi_writel(struct tegra_mipi *mipi, u32 value,
111 unsigned long offset)
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200112{
Thierry Reding57b17ae2014-10-02 14:33:31 +0200113 writel(value, mipi->regs + (offset << 2));
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200114}
115
116struct tegra_mipi_device *tegra_mipi_request(struct device *device)
117{
118 struct device_node *np = device->of_node;
119 struct tegra_mipi_device *dev;
120 struct of_phandle_args args;
121 int err;
122
123 err = of_parse_phandle_with_args(np, "nvidia,mipi-calibrate",
124 "#nvidia,mipi-calibrate-cells", 0,
125 &args);
126 if (err < 0)
127 return ERR_PTR(err);
128
129 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
130 if (!dev) {
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200131 err = -ENOMEM;
132 goto out;
133 }
134
135 dev->pdev = of_find_device_by_node(args.np);
136 if (!dev->pdev) {
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200137 err = -ENODEV;
138 goto free;
139 }
140
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200141 dev->mipi = platform_get_drvdata(dev->pdev);
142 if (!dev->mipi) {
143 err = -EPROBE_DEFER;
Sean Paul08a15cc2014-09-10 10:52:04 -0400144 goto put;
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200145 }
146
Sean Paul08a15cc2014-09-10 10:52:04 -0400147 of_node_put(args.np);
148
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200149 dev->pads = args.args[0];
150 dev->device = device;
151
152 return dev;
153
Sean Paul08a15cc2014-09-10 10:52:04 -0400154put:
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200155 platform_device_put(dev->pdev);
156free:
157 kfree(dev);
158out:
Sean Paul08a15cc2014-09-10 10:52:04 -0400159 of_node_put(args.np);
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200160 return ERR_PTR(err);
161}
162EXPORT_SYMBOL(tegra_mipi_request);
163
164void tegra_mipi_free(struct tegra_mipi_device *device)
165{
166 platform_device_put(device->pdev);
167 kfree(device);
168}
169EXPORT_SYMBOL(tegra_mipi_free);
170
171static int tegra_mipi_wait(struct tegra_mipi *mipi)
172{
173 unsigned long timeout = jiffies + msecs_to_jiffies(250);
Thierry Reding57b17ae2014-10-02 14:33:31 +0200174 u32 value;
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200175
176 while (time_before(jiffies, timeout)) {
177 value = tegra_mipi_readl(mipi, MIPI_CAL_STATUS);
178 if ((value & MIPI_CAL_STATUS_ACTIVE) == 0 &&
179 (value & MIPI_CAL_STATUS_DONE) != 0)
180 return 0;
181
182 usleep_range(10, 50);
183 }
184
185 return -ETIMEDOUT;
186}
187
188int tegra_mipi_calibrate(struct tegra_mipi_device *device)
189{
Sean Paul08a15cc2014-09-10 10:52:04 -0400190 const struct tegra_mipi_soc *soc = device->mipi->soc;
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200191 unsigned int i;
Thierry Reding57b17ae2014-10-02 14:33:31 +0200192 u32 value;
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200193 int err;
194
195 err = clk_enable(device->mipi->clk);
196 if (err < 0)
197 return err;
198
199 mutex_lock(&device->mipi->lock);
200
201 value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG0);
202 value &= ~MIPI_CAL_BIAS_PAD_PDVCLAMP;
203 value |= MIPI_CAL_BIAS_PAD_E_VCLAMP_REF;
204 tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG0);
205
206 value = tegra_mipi_readl(device->mipi, MIPI_CAL_BIAS_PAD_CFG2);
207 value &= ~MIPI_CAL_BIAS_PAD_PDVREG;
208 tegra_mipi_writel(device->mipi, value, MIPI_CAL_BIAS_PAD_CFG2);
209
Sean Paul08a15cc2014-09-10 10:52:04 -0400210 for (i = 0; i < soc->num_pads; i++) {
211 u32 clk = 0, data = 0;
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200212
Sean Paul08a15cc2014-09-10 10:52:04 -0400213 if (device->pads & BIT(i)) {
214 data = MIPI_CAL_CONFIG_SELECT |
215 MIPI_CAL_CONFIG_HSPDOS(0) |
216 MIPI_CAL_CONFIG_HSPUOS(4) |
217 MIPI_CAL_CONFIG_TERMOS(5);
218 clk = MIPI_CAL_CONFIG_SELECT |
219 MIPI_CAL_CONFIG_HSCLKPDOSD(0) |
220 MIPI_CAL_CONFIG_HSCLKPUOSD(4);
221 }
222
223 tegra_mipi_writel(device->mipi, data, soc->pads[i].data);
224
225 if (soc->has_clk_lane)
226 tegra_mipi_writel(device->mipi, clk, soc->pads[i].clk);
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200227 }
228
Sean Paul26f7a922014-09-10 10:52:03 -0400229 value = tegra_mipi_readl(device->mipi, MIPI_CAL_CTRL);
230 value |= MIPI_CAL_CTRL_START;
231 tegra_mipi_writel(device->mipi, value, MIPI_CAL_CTRL);
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200232
233 err = tegra_mipi_wait(device->mipi);
234
235 mutex_unlock(&device->mipi->lock);
236 clk_disable(device->mipi->clk);
237
238 return err;
239}
240EXPORT_SYMBOL(tegra_mipi_calibrate);
241
Sean Paul08a15cc2014-09-10 10:52:04 -0400242static const struct tegra_mipi_pad tegra114_mipi_pads[] = {
243 { .data = MIPI_CAL_CONFIG_CSIA },
244 { .data = MIPI_CAL_CONFIG_CSIB },
245 { .data = MIPI_CAL_CONFIG_CSIC },
246 { .data = MIPI_CAL_CONFIG_CSID },
247 { .data = MIPI_CAL_CONFIG_CSIE },
248 { .data = MIPI_CAL_CONFIG_DSIA },
249 { .data = MIPI_CAL_CONFIG_DSIB },
250 { .data = MIPI_CAL_CONFIG_DSIC },
251 { .data = MIPI_CAL_CONFIG_DSID },
252};
253
254static const struct tegra_mipi_soc tegra114_mipi_soc = {
255 .has_clk_lane = false,
256 .pads = tegra114_mipi_pads,
257 .num_pads = ARRAY_SIZE(tegra114_mipi_pads),
258};
259
260static const struct tegra_mipi_pad tegra124_mipi_pads[] = {
261 { .data = MIPI_CAL_CONFIG_CSIA, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
262 { .data = MIPI_CAL_CONFIG_CSIB, .clk = MIPI_CAL_CONFIG_CSIAB_CLK },
263 { .data = MIPI_CAL_CONFIG_CSIC, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
264 { .data = MIPI_CAL_CONFIG_CSID, .clk = MIPI_CAL_CONFIG_CSICD_CLK },
265 { .data = MIPI_CAL_CONFIG_CSIE, .clk = MIPI_CAL_CONFIG_CSIE_CLK },
266 { .data = MIPI_CAL_CONFIG_DSIA, .clk = MIPI_CAL_CONFIG_DSIAB_CLK },
267 { .data = MIPI_CAL_CONFIG_DSIB, .clk = MIPI_CAL_CONFIG_DSIAB_CLK },
268};
269
270static const struct tegra_mipi_soc tegra124_mipi_soc = {
271 .has_clk_lane = true,
272 .pads = tegra124_mipi_pads,
273 .num_pads = ARRAY_SIZE(tegra124_mipi_pads),
274};
275
276static struct of_device_id tegra_mipi_of_match[] = {
277 { .compatible = "nvidia,tegra114-mipi", .data = &tegra114_mipi_soc },
278 { .compatible = "nvidia,tegra124-mipi", .data = &tegra124_mipi_soc },
279 { },
280};
281
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200282static int tegra_mipi_probe(struct platform_device *pdev)
283{
Sean Paul08a15cc2014-09-10 10:52:04 -0400284 const struct of_device_id *match;
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200285 struct tegra_mipi *mipi;
286 struct resource *res;
287 int err;
288
Sean Paul08a15cc2014-09-10 10:52:04 -0400289 match = of_match_node(tegra_mipi_of_match, pdev->dev.of_node);
290 if (!match)
291 return -ENODEV;
292
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200293 mipi = devm_kzalloc(&pdev->dev, sizeof(*mipi), GFP_KERNEL);
294 if (!mipi)
295 return -ENOMEM;
296
Sean Paul08a15cc2014-09-10 10:52:04 -0400297 mipi->soc = match->data;
298
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200299 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
300 mipi->regs = devm_ioremap_resource(&pdev->dev, res);
301 if (IS_ERR(mipi->regs))
302 return PTR_ERR(mipi->regs);
303
304 mutex_init(&mipi->lock);
305
306 mipi->clk = devm_clk_get(&pdev->dev, NULL);
307 if (IS_ERR(mipi->clk)) {
308 dev_err(&pdev->dev, "failed to get clock\n");
309 return PTR_ERR(mipi->clk);
310 }
311
312 err = clk_prepare(mipi->clk);
313 if (err < 0)
314 return err;
315
316 platform_set_drvdata(pdev, mipi);
317
318 return 0;
319}
320
321static int tegra_mipi_remove(struct platform_device *pdev)
322{
323 struct tegra_mipi *mipi = platform_get_drvdata(pdev);
324
325 clk_unprepare(mipi->clk);
326
327 return 0;
328}
329
Thierry Reding4de6a2d2013-09-02 09:48:53 +0200330struct platform_driver tegra_mipi_driver = {
331 .driver = {
332 .name = "tegra-mipi",
333 .of_match_table = tegra_mipi_of_match,
334 },
335 .probe = tegra_mipi_probe,
336 .remove = tegra_mipi_remove,
337};