]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/media/platform/marvell-ccic/mmp-driver.c
Merge tag 'v5.3-rc4' into patchwork
[linux.git] / drivers / media / platform / marvell-ccic / mmp-driver.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Support for the camera device found on Marvell MMP processors; known
4  * to work with the Armada 610 as used in the OLPC 1.75 system.
5  *
6  * Copyright 2011 Jonathan Corbet <corbet@lwn.net>
7  * Copyright 2018 Lubomir Rintel <lkundrak@v3.sk>
8  */
9
10 #include <linux/init.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/interrupt.h>
14 #include <linux/spinlock.h>
15 #include <linux/slab.h>
16 #include <linux/videodev2.h>
17 #include <media/v4l2-device.h>
18 #include <linux/platform_data/media/mmp-camera.h>
19 #include <linux/device.h>
20 #include <linux/of.h>
21 #include <linux/of_platform.h>
22 #include <linux/platform_device.h>
23 #include <linux/io.h>
24 #include <linux/list.h>
25 #include <linux/pm.h>
26 #include <linux/clk.h>
27
28 #include "mcam-core.h"
29
30 MODULE_ALIAS("platform:mmp-camera");
31 MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
32 MODULE_LICENSE("GPL");
33
34 static char *mcam_clks[] = {"axi", "func", "phy"};
35
36 struct mmp_camera {
37         struct platform_device *pdev;
38         struct mcam_camera mcam;
39         struct list_head devlist;
40         struct clk *mipi_clk;
41         int irq;
42 };
43
44 static inline struct mmp_camera *mcam_to_cam(struct mcam_camera *mcam)
45 {
46         return container_of(mcam, struct mmp_camera, mcam);
47 }
48
49 /*
50  * A silly little infrastructure so we can keep track of our devices.
51  * Chances are that we will never have more than one of them, but
52  * the Armada 610 *does* have two controllers...
53  */
54
55 static LIST_HEAD(mmpcam_devices);
56 static struct mutex mmpcam_devices_lock;
57
58 static void mmpcam_add_device(struct mmp_camera *cam)
59 {
60         mutex_lock(&mmpcam_devices_lock);
61         list_add(&cam->devlist, &mmpcam_devices);
62         mutex_unlock(&mmpcam_devices_lock);
63 }
64
65 static void mmpcam_remove_device(struct mmp_camera *cam)
66 {
67         mutex_lock(&mmpcam_devices_lock);
68         list_del(&cam->devlist);
69         mutex_unlock(&mmpcam_devices_lock);
70 }
71
72 /*
73  * Platform dev remove passes us a platform_device, and there's
74  * no handy unused drvdata to stash a backpointer in.  So just
75  * dig it out of our list.
76  */
77 static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev)
78 {
79         struct mmp_camera *cam;
80
81         mutex_lock(&mmpcam_devices_lock);
82         list_for_each_entry(cam, &mmpcam_devices, devlist) {
83                 if (cam->pdev == pdev) {
84                         mutex_unlock(&mmpcam_devices_lock);
85                         return cam;
86                 }
87         }
88         mutex_unlock(&mmpcam_devices_lock);
89         return NULL;
90 }
91
92 /*
93  * calc the dphy register values
94  * There are three dphy registers being used.
95  * dphy[0] - CSI2_DPHY3
96  * dphy[1] - CSI2_DPHY5
97  * dphy[2] - CSI2_DPHY6
98  * CSI2_DPHY3 and CSI2_DPHY6 can be set with a default value
99  * or be calculated dynamically
100  */
101 static void mmpcam_calc_dphy(struct mcam_camera *mcam)
102 {
103         struct mmp_camera *cam = mcam_to_cam(mcam);
104         struct mmp_camera_platform_data *pdata = cam->pdev->dev.platform_data;
105         struct device *dev = &cam->pdev->dev;
106         unsigned long tx_clk_esc;
107
108         /*
109          * If CSI2_DPHY3 is calculated dynamically,
110          * pdata->lane_clk should be already set
111          * either in the board driver statically
112          * or in the sensor driver dynamically.
113          */
114         /*
115          * dphy[0] - CSI2_DPHY3:
116          *  bit 0 ~ bit 7: HS Term Enable.
117          *   defines the time that the DPHY
118          *   wait before enabling the data
119          *   lane termination after detecting
120          *   that the sensor has driven the data
121          *   lanes to the LP00 bridge state.
122          *   The value is calculated by:
123          *   (Max T(D_TERM_EN)/Period(DDR)) - 1
124          *  bit 8 ~ bit 15: HS_SETTLE
125          *   Time interval during which the HS
126          *   receiver shall ignore any Data Lane
127          *   HS transitions.
128          *   The value has been calibrated on
129          *   different boards. It seems to work well.
130          *
131          *  More detail please refer
132          *  MIPI Alliance Spectification for D-PHY
133          *  document for explanation of HS-SETTLE
134          *  and D-TERM-EN.
135          */
136         switch (pdata->dphy3_algo) {
137         case DPHY3_ALGO_PXA910:
138                 /*
139                  * Calculate CSI2_DPHY3 algo for PXA910
140                  */
141                 pdata->dphy[0] =
142                         (((1 + (pdata->lane_clk * 80) / 1000) & 0xff) << 8)
143                         | (1 + pdata->lane_clk * 35 / 1000);
144                 break;
145         case DPHY3_ALGO_PXA2128:
146                 /*
147                  * Calculate CSI2_DPHY3 algo for PXA2128
148                  */
149                 pdata->dphy[0] =
150                         (((2 + (pdata->lane_clk * 110) / 1000) & 0xff) << 8)
151                         | (1 + pdata->lane_clk * 35 / 1000);
152                 break;
153         default:
154                 /*
155                  * Use default CSI2_DPHY3 value for PXA688/PXA988
156                  */
157                 dev_dbg(dev, "camera: use the default CSI2_DPHY3 value\n");
158         }
159
160         /*
161          * mipi_clk will never be changed, it is a fixed value on MMP
162          */
163         if (IS_ERR(cam->mipi_clk))
164                 return;
165
166         /* get the escape clk, this is hard coded */
167         clk_prepare_enable(cam->mipi_clk);
168         tx_clk_esc = (clk_get_rate(cam->mipi_clk) / 1000000) / 12;
169         clk_disable_unprepare(cam->mipi_clk);
170         /*
171          * dphy[2] - CSI2_DPHY6:
172          * bit 0 ~ bit 7: CK Term Enable
173          *  Time for the Clock Lane receiver to enable the HS line
174          *  termination. The value is calculated similarly with
175          *  HS Term Enable
176          * bit 8 ~ bit 15: CK Settle
177          *  Time interval during which the HS receiver shall ignore
178          *  any Clock Lane HS transitions.
179          *  The value is calibrated on the boards.
180          */
181         pdata->dphy[2] =
182                 ((((534 * tx_clk_esc) / 2000 - 1) & 0xff) << 8)
183                 | (((38 * tx_clk_esc) / 1000 - 1) & 0xff);
184
185         dev_dbg(dev, "camera: DPHY sets: dphy3=0x%x, dphy5=0x%x, dphy6=0x%x\n",
186                 pdata->dphy[0], pdata->dphy[1], pdata->dphy[2]);
187 }
188
189 static irqreturn_t mmpcam_irq(int irq, void *data)
190 {
191         struct mcam_camera *mcam = data;
192         unsigned int irqs, handled;
193
194         spin_lock(&mcam->dev_lock);
195         irqs = mcam_reg_read(mcam, REG_IRQSTAT);
196         handled = mccic_irq(mcam, irqs);
197         spin_unlock(&mcam->dev_lock);
198         return IRQ_RETVAL(handled);
199 }
200
201 static void mcam_init_clk(struct mcam_camera *mcam)
202 {
203         unsigned int i;
204
205         for (i = 0; i < NR_MCAM_CLK; i++) {
206                 if (mcam_clks[i] != NULL) {
207                         /* Some clks are not necessary on some boards
208                          * We still try to run even it fails getting clk
209                          */
210                         mcam->clk[i] = devm_clk_get(mcam->dev, mcam_clks[i]);
211                         if (IS_ERR(mcam->clk[i]))
212                                 dev_warn(mcam->dev, "Could not get clk: %s\n",
213                                                 mcam_clks[i]);
214                 }
215         }
216 }
217
218 static int mmpcam_probe(struct platform_device *pdev)
219 {
220         struct mmp_camera *cam;
221         struct mcam_camera *mcam;
222         struct resource *res;
223         struct fwnode_handle *ep;
224         struct mmp_camera_platform_data *pdata;
225         int ret;
226
227         cam = devm_kzalloc(&pdev->dev, sizeof(*cam), GFP_KERNEL);
228         if (cam == NULL)
229                 return -ENOMEM;
230         cam->pdev = pdev;
231         INIT_LIST_HEAD(&cam->devlist);
232
233         mcam = &cam->mcam;
234         mcam->calc_dphy = mmpcam_calc_dphy;
235         mcam->dev = &pdev->dev;
236         pdata = pdev->dev.platform_data;
237         if (pdata) {
238                 mcam->mclk_src = pdata->mclk_src;
239                 mcam->mclk_div = pdata->mclk_div;
240                 mcam->bus_type = pdata->bus_type;
241                 mcam->dphy = pdata->dphy;
242                 mcam->lane = pdata->lane;
243         } else {
244                 /*
245                  * These are values that used to be hardcoded in mcam-core and
246                  * work well on a OLPC XO 1.75 with a parallel bus sensor.
247                  * If it turns out other setups make sense, the values should
248                  * be obtained from the device tree.
249                  */
250                 mcam->mclk_src = 3;
251                 mcam->mclk_div = 2;
252         }
253         if (mcam->bus_type == V4L2_MBUS_CSI2_DPHY) {
254                 cam->mipi_clk = devm_clk_get(mcam->dev, "mipi");
255                 if ((IS_ERR(cam->mipi_clk) && mcam->dphy[2] == 0))
256                         return PTR_ERR(cam->mipi_clk);
257         }
258         mcam->mipi_enabled = false;
259         mcam->chip_id = MCAM_ARMADA610;
260         mcam->buffer_mode = B_DMA_sg;
261         strscpy(mcam->bus_info, "platform:mmp-camera", sizeof(mcam->bus_info));
262         spin_lock_init(&mcam->dev_lock);
263         /*
264          * Get our I/O memory.
265          */
266         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
267         mcam->regs = devm_ioremap_resource(&pdev->dev, res);
268         if (IS_ERR(mcam->regs))
269                 return PTR_ERR(mcam->regs);
270         mcam->regs_size = resource_size(res);
271
272         mcam_init_clk(mcam);
273
274         /*
275          * Create a match of the sensor against its OF node.
276          */
277         ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(pdev->dev.of_node),
278                                             NULL);
279         if (!ep)
280                 return -ENODEV;
281
282         mcam->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
283         mcam->asd.match.fwnode = fwnode_graph_get_remote_port_parent(ep);
284
285         fwnode_handle_put(ep);
286
287         /*
288          * Register the device with the core.
289          */
290         ret = mccic_register(mcam);
291         if (ret)
292                 return ret;
293
294         /*
295          * Add OF clock provider.
296          */
297         ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
298                                                                 mcam->mclk);
299         if (ret) {
300                 dev_err(&pdev->dev, "can't add DT clock provider\n");
301                 goto out;
302         }
303
304         /*
305          * Finally, set up our IRQ now that the core is ready to
306          * deal with it.
307          */
308         res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
309         if (res == NULL) {
310                 ret = -ENODEV;
311                 goto out;
312         }
313         cam->irq = res->start;
314         ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED,
315                                         "mmp-camera", mcam);
316         if (ret == 0) {
317                 mmpcam_add_device(cam);
318                 return 0;
319         }
320
321 out:
322         fwnode_handle_put(mcam->asd.match.fwnode);
323         mccic_shutdown(mcam);
324
325         return ret;
326 }
327
328
329 static int mmpcam_remove(struct mmp_camera *cam)
330 {
331         struct mcam_camera *mcam = &cam->mcam;
332
333         mmpcam_remove_device(cam);
334         mccic_shutdown(mcam);
335         return 0;
336 }
337
338 static int mmpcam_platform_remove(struct platform_device *pdev)
339 {
340         struct mmp_camera *cam = mmpcam_find_device(pdev);
341
342         if (cam == NULL)
343                 return -ENODEV;
344         return mmpcam_remove(cam);
345 }
346
347 /*
348  * Suspend/resume support.
349  */
350 #ifdef CONFIG_PM
351
352 static int mmpcam_suspend(struct platform_device *pdev, pm_message_t state)
353 {
354         struct mmp_camera *cam = mmpcam_find_device(pdev);
355
356         if (state.event != PM_EVENT_SUSPEND)
357                 return 0;
358         mccic_suspend(&cam->mcam);
359         return 0;
360 }
361
362 static int mmpcam_resume(struct platform_device *pdev)
363 {
364         struct mmp_camera *cam = mmpcam_find_device(pdev);
365
366         return mccic_resume(&cam->mcam);
367 }
368
369 #endif
370
371 static const struct of_device_id mmpcam_of_match[] = {
372         { .compatible = "marvell,mmp2-ccic", },
373         {},
374 };
375 MODULE_DEVICE_TABLE(of, mmpcam_of_match);
376
377 static struct platform_driver mmpcam_driver = {
378         .probe          = mmpcam_probe,
379         .remove         = mmpcam_platform_remove,
380 #ifdef CONFIG_PM
381         .suspend        = mmpcam_suspend,
382         .resume         = mmpcam_resume,
383 #endif
384         .driver = {
385                 .name   = "mmp-camera",
386                 .of_match_table = of_match_ptr(mmpcam_of_match),
387         }
388 };
389
390
391 static int __init mmpcam_init_module(void)
392 {
393         mutex_init(&mmpcam_devices_lock);
394         return platform_driver_register(&mmpcam_driver);
395 }
396
397 static void __exit mmpcam_exit_module(void)
398 {
399         platform_driver_unregister(&mmpcam_driver);
400         /*
401          * platform_driver_unregister() should have emptied the list
402          */
403         if (!list_empty(&mmpcam_devices))
404                 printk(KERN_ERR "mmp_camera leaving devices behind\n");
405 }
406
407 module_init(mmpcam_init_module);
408 module_exit(mmpcam_exit_module);