]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/media/platform/msm/camss-8x16/csiphy.c
2b702fbd4eb997f235dcd6519c9e350baab0ea26
[karo-tx-linux.git] / drivers / media / platform / msm / camss-8x16 / csiphy.c
1 /*
2  * csiphy.c
3  *
4  * Qualcomm MSM Camera Subsystem - CSIPHY Module
5  *
6  * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
7  * Copyright (C) 2016 Linaro Ltd.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 and
11  * only version 2 as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  */
18 #include <linux/clk.h>
19 #include <linux/delay.h>
20 #include <linux/interrupt.h>
21 #include <linux/of.h>
22 #include <linux/platform_device.h>
23 #include <media/media-entity.h>
24 #include <media/v4l2-device.h>
25 #include <media/v4l2-subdev.h>
26
27 #include "csiphy.h"
28 #include "camss.h"
29
30 #define MSM_CSIPHY_NAME "msm_csiphy"
31
32 #define CAMSS_CSI_PHY_LNn_CFG2(n)               (0x004 + 0x40 * (n))
33 #define CAMSS_CSI_PHY_LNn_CFG3(n)               (0x008 + 0x40 * (n))
34 #define CAMSS_CSI_PHY_LNn_MISC1(n)              (0x028 + 0x40 * (n))
35 #define CAMSS_CSI_PHY_LNn_TEST_IMP(n)           (0x01c + 0x40 * (n))
36 #define CAMSS_CSI_PHY_GLBL_RESET                0x140
37 #define CAMSS_CSI_PHY_GLBL_PWR_CFG              0x144
38 #define CAMSS_CSI_PHY_GLBL_IRQ_CMD              0x164
39 #define CAMSS_CSI_PHY_HW_VERSION                0x188
40 #define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n)      (0x18c + 0x4 * (n))
41 #define CAMSS_CSI_PHY_INTERRUPT_MASKn(n)        (0x1ac + 0x4 * (n))
42 #define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n)       (0x1cc + 0x4 * (n))
43 #define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0          0x1ec
44 #define CAMSS_CSI_PHY_T_WAKEUP_CFG0             0x1f4
45
46 /*
47  * csiphy_isr - CSIPHY module interrupt handler
48  * @irq: Interrupt line
49  * @dev: CSIPHY device
50  *
51  * Return IRQ_HANDLED on success
52  */
53 static irqreturn_t csiphy_isr(int irq, void *dev)
54 {
55         struct csiphy_device *csiphy = dev;
56         u8 val[8];
57         u8 i;
58
59         for (i = 0; i < 8; i++) {
60                 val[i] = readl_relaxed(csiphy->base +
61                                        CAMSS_CSI_PHY_INTERRUPT_STATUSn(i));
62                 writel_relaxed(val[i], csiphy->base +
63                                CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
64                 writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
65                 writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
66                 writel_relaxed(0x0, csiphy->base +
67                                CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
68         }
69
70         return IRQ_HANDLED;
71 }
72
73 /*
74  * csiphy_reset - Perform software reset on CSIPHY module
75  * @csiphy: CSIPHY device
76  */
77 static void csiphy_reset(struct csiphy_device *csiphy)
78 {
79         writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
80         usleep_range(5000, 8000);
81         writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
82 }
83
84 /*
85  * csiphy_enable_clocks - Enable clocks for CSIPHY module and
86  * set clock rates where needed
87  * @csiphy: CSIPHY device
88  *
89  * Return 0 on success or a negative error code otherwise
90  */
91 static int csiphy_enable_clocks(struct csiphy_device *csiphy)
92 {
93         long clk_rate;
94         int ret;
95         int i;
96
97         for (i = 0; i < csiphy->nclocks; i++) {
98                 if (csiphy->clock_rate[i]) {
99                         clk_rate = clk_round_rate(csiphy->clock[i],
100                                                   csiphy->clock_rate[i]);
101                         if (clk_rate < 0) {
102                                 dev_err(csiphy->camss->dev, "round failed\n");
103                                 ret = clk_rate;
104                                 goto error;
105                         }
106                         ret = clk_set_rate(csiphy->clock[i], clk_rate);
107                         if (ret < 0) {
108                                 dev_err(csiphy->camss->dev, "set rate failed\n");
109                                 goto error;
110                         }
111                 }
112                 ret = clk_prepare_enable(csiphy->clock[i]);
113                 if (ret) {
114                         dev_err(csiphy->camss->dev, "clk enable failed\n");
115                         goto error;
116                 }
117         }
118
119         return 0;
120
121 error:
122         for (i--; i >= 0; i--)
123                 clk_disable_unprepare(csiphy->clock[i]);
124
125         return ret;
126 }
127
128 /*
129  * csiphy_disable_clocks - Disable clocks for CSIPHY module
130  * @csiphy: CSIPHY device
131  */
132 static void csiphy_disable_clocks(struct csiphy_device *csiphy)
133 {
134         int i;
135
136         for (i = csiphy->nclocks - 1; i >= 0; i--)
137                 clk_disable_unprepare(csiphy->clock[i]);
138 }
139
140 /*
141  * csiphy_set_power - Power on/off CSIPHY module
142  * @sd: CSIPHY V4L2 subdevice
143  * @on: Requested power state
144  *
145  * Return 0 on success or a negative error code otherwise
146  */
147 static int csiphy_set_power(struct v4l2_subdev *sd, int on)
148 {
149         struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
150         int ret;
151
152         dev_err(csiphy->camss->dev, "%s: Enter, csiphy%d on = %d\n",
153                 __func__, csiphy->id, on);
154
155         if (on) {
156                 u8 hw_version;
157
158                 ret = csiphy_enable_clocks(csiphy);
159                 if (ret < 0)
160                         return ret;
161
162                 enable_irq(csiphy->irq);
163
164                 csiphy_reset(csiphy);
165
166                 hw_version = readl(csiphy->base + CAMSS_CSI_PHY_HW_VERSION);
167                 dev_err(csiphy->camss->dev, "CSIPHY HW Version = 0x%02x\n",
168                         hw_version);
169         } else {
170                 disable_irq(csiphy->irq);
171
172                 csiphy_disable_clocks(csiphy);
173         }
174
175         dev_err(csiphy->camss->dev, "%s: Exit csiphy%d on = %d\n",
176                 __func__, csiphy->id, on);
177
178         return 0;
179 }
180
181 /*
182  * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter
183  * @lane_cfg - CSI2 lane configuration
184  *
185  * Return lane mask
186  */
187 static int csiphy_get_lane_mask(struct camss_csiphy_lanes_cfg *lane_cfg)
188 {
189         u16 lane_mask;
190         int i;
191
192         lane_mask = 1 << lane_cfg->clk.pos;
193
194         for (i = 0; i < lane_cfg->num_data; i++)
195                 lane_mask |= 1 << lane_cfg->data[i].pos;
196
197         return lane_mask;
198 }
199
200 /*
201  * csiphy_set_stream - Enable/disable streaming on CSIPHY module
202  * @sd: CSIPHY V4L2 subdevice
203  * @enable: Requested streaming state
204  *
205  * Main configuration of CSIPHY module is also done here.
206  *
207  * Return 0 on success or a negative error code otherwise
208  */
209 static int csiphy_set_stream(struct v4l2_subdev *sd, int enable)
210 {
211         struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
212         struct csiphy_config *cfg = &csiphy->cfg;
213         u16 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lanecfg);
214         u8 i;
215         u8 val;
216
217         dev_err(csiphy->camss->dev, "%s: Enter, csiphy%d enable = %d\n",
218                 __func__, csiphy->id, enable);
219
220         if (enable) {
221                 val = readl(csiphy->base_clk_mux);
222                 if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) {
223                         val &= ~0xf0;
224                         val |= cfg->csid_id << 4;
225                 } else {
226                         val &= ~0xf;
227                         val |= cfg->csid_id;
228                 }
229                 writel_relaxed(val, csiphy->base_clk_mux);
230
231                 writel_relaxed(0x1, csiphy->base +
232                                CAMSS_CSI_PHY_GLBL_T_INIT_CFG0);
233                 writel_relaxed(0x1, csiphy->base +
234                                CAMSS_CSI_PHY_T_WAKEUP_CFG0);
235
236                 val = 0x1;
237                 val |= lane_mask << 1;
238                 writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
239
240                 val = cfg->combo_mode << 4;
241                 writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
242
243                 lane_mask &= 0x1f;
244                 i = 0;
245                 while (lane_mask & 0x1f) {
246                         if (!(lane_mask & 0x1)) {
247                                 i++;
248                                 lane_mask >>= 1;
249                                 continue;
250                         }
251
252                         writel_relaxed(0x10, csiphy->base +
253                                        CAMSS_CSI_PHY_LNn_CFG2(i));
254                         writel_relaxed(cfg->csi2->settle_cnt, csiphy->base +
255                                        CAMSS_CSI_PHY_LNn_CFG3(i));
256
257                         writel_relaxed(0x3f, csiphy->base +
258                                        CAMSS_CSI_PHY_INTERRUPT_MASKn(i));
259                         writel_relaxed(0x3f, csiphy->base +
260                                        CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
261
262                         i++;
263                         lane_mask >>= 1;
264                 }
265         } else {
266                 i = 0;
267                 while (lane_mask) {
268                         if (lane_mask & 0x1) {
269                                 writel_relaxed(0x0, csiphy->base +
270                                                CAMSS_CSI_PHY_LNn_CFG2(i));
271                                 writel_relaxed(0x0, csiphy->base +
272                                                CAMSS_CSI_PHY_LNn_MISC1(i));
273                                 writel_relaxed(0x0, csiphy->base +
274                                                CAMSS_CSI_PHY_LNn_TEST_IMP(i));
275                         }
276
277                         lane_mask >>= 1;
278                         i++;
279                 }
280
281                 writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_LNn_CFG2(4));
282                 writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
283         }
284
285         return 0;
286 }
287
288 /*
289  * __csiphy_get_format - Get pointer to format structure
290  * @csiphy: CSIPHY device
291  * @cfg: V4L2 subdev pad configuration
292  * @pad: pad from which format is requested
293  * @which: TRY or ACTIVE format
294  *
295  * Return pointer to TRY or ACTIVE format structure
296  */
297 static struct v4l2_mbus_framefmt *
298 __csiphy_get_format(struct csiphy_device *csiphy,
299                     struct v4l2_subdev_pad_config *cfg,
300                     unsigned int pad,
301                     enum v4l2_subdev_format_whence which)
302 {
303         if (which == V4L2_SUBDEV_FORMAT_TRY)
304                 return v4l2_subdev_get_try_format(&csiphy->subdev, cfg, pad);
305
306         return &csiphy->fmt[pad];
307 }
308
309 /*
310  * csiphy_get_format - Handle get format by pads subdev method
311  * @sd: CSIPHY V4L2 subdevice
312  * @cfg: V4L2 subdev pad configuration
313  * @fmt: pointer to v4l2 subdev format structure
314  *
315  * Return -EINVAL or zero on success
316  */
317 static int csiphy_get_format(struct v4l2_subdev *sd,
318                              struct v4l2_subdev_pad_config *cfg,
319                              struct v4l2_subdev_format *fmt)
320 {
321         struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
322         struct v4l2_mbus_framefmt *format;
323
324         format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which);
325         if (format == NULL)
326                 return -EINVAL;
327
328         fmt->format = *format;
329
330         return 0;
331 }
332
333 /*
334  * csiphy_set_format - Handle set format by pads subdev method
335  * @sd: CSIPHY V4L2 subdevice
336  * @cfg: V4L2 subdev pad configuration
337  * @fmt: pointer to v4l2 subdev format structure
338  *
339  * Return -EINVAL or zero on success
340  */
341 static int csiphy_set_format(struct v4l2_subdev *sd,
342                              struct v4l2_subdev_pad_config *cfg,
343                              struct v4l2_subdev_format *fmt)
344 {
345         struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
346         struct v4l2_mbus_framefmt *format;
347
348         if (fmt->pad == MSM_CSIPHY_PAD_SINK) {
349                 /* Set format on sink pad */
350                 format = __csiphy_get_format(csiphy, cfg, fmt->pad,
351                                              fmt->which);
352                 if (format == NULL)
353                         return -EINVAL;
354
355                 *format = fmt->format;
356
357                 /* Reset format on source pad */
358                 format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC,
359                                              fmt->which);
360                 if (format == NULL)
361                         return -EINVAL;
362
363                 *format = fmt->format;
364         } else {
365                 /* Source pad format must be same as sink pad format, */
366                 /* so just return source pad format */
367                 format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which);
368                 if (format == NULL)
369                         return -EINVAL;
370
371                 fmt->format = *format;
372         }
373
374         return 0;
375 }
376
377 /*
378  * msm_csiphy_subdev_init - Initialize CSIPHY device structure and resources
379  * @csiphy: CSIPHY device
380  * @camss: Camera sub-system structure
381  * @res: CSIPHY module resources table
382  * @id: CSIPHY module id
383  *
384  * Return 0 on success or a negative error code otherwise
385  */
386 int msm_csiphy_subdev_init(struct csiphy_device *csiphy, struct camss *camss,
387                            struct resources *res, u8 id)
388 {
389         struct device *dev = camss->dev;
390         struct platform_device *pdev = container_of(dev, struct platform_device, dev);
391         struct resource *r;
392         int i;
393         int ret;
394
395         csiphy->camss = camss;
396
397         dev_err(csiphy->camss->dev, "%s: Enter\n", __func__);
398
399         csiphy->id = id;
400
401         csiphy->cfg.combo_mode = 0;
402
403         /* Memory */
404
405         r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
406         csiphy->base = devm_ioremap_resource(dev, r);
407         if (IS_ERR(csiphy->base)) {
408                 dev_err(dev, "could not map memory\n");
409                 return PTR_ERR(csiphy->base);
410         }
411
412         r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]);
413         csiphy->base_clk_mux = devm_ioremap_resource(dev, r);
414         if (IS_ERR(csiphy->base_clk_mux)) {
415                 dev_err(dev, "could not map memory\n");
416                 return PTR_ERR(csiphy->base_clk_mux);
417         }
418
419         /* Interrupt */
420
421         r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res->interrupt[0]);
422         csiphy->irq = r->start;
423         if (IS_ERR_VALUE(csiphy->irq))
424                 return csiphy->irq;
425
426         ret = devm_request_irq(dev, csiphy->irq, csiphy_isr,
427                 IRQF_TRIGGER_RISING, dev_name(dev), csiphy);
428         if (ret < 0) {
429                 dev_err(dev, "request_irq failed\n");
430                 return ret;
431         }
432
433         disable_irq(csiphy->irq);
434
435         /* Clocks */
436
437         i = 0;
438         csiphy->nclocks = 0;
439         while (res->clock[i++])
440                 csiphy->nclocks++;
441
442         csiphy->clock = devm_kzalloc(dev, csiphy->nclocks *
443                                      sizeof(*csiphy->clock), GFP_KERNEL);
444         if (!csiphy->clock) {
445                 dev_err(dev, "could not allocate memory\n");
446                 return -ENOMEM;
447         }
448
449         csiphy->clock_rate = devm_kzalloc(dev, csiphy->nclocks *
450                                           sizeof(*csiphy->clock_rate), GFP_KERNEL);
451         if (!csiphy->clock_rate) {
452                 dev_err(dev, "could not allocate memory\n");
453                 return -ENOMEM;
454         }
455
456         for (i = 0; i < csiphy->nclocks; i++) {
457                 csiphy->clock[i] = devm_clk_get(dev, res->clock[i]);
458                 if (IS_ERR(csiphy->clock[i]))
459                         return PTR_ERR(csiphy->clock[i]);
460                 csiphy->clock_rate[i] = res->clock_rate[i];
461         }
462
463         dev_err(csiphy->camss->dev, "%s: Exit\n", __func__);
464
465         return 0;
466 }
467
468 /*
469  * csiphy_link_setup - Setup CSIPHY connections
470  * @entity: Pointer to media entity structure
471  * @local: Pointer to local pad
472  * @remote: Pointer to remote pad
473  * @flags: Link flags
474  *
475  * Rreturn 0 on success
476  */
477 static int csiphy_link_setup(struct media_entity *entity,
478                              const struct media_pad *local,
479                              const struct media_pad *remote, u32 flags)
480 {
481         if ((local->flags & MEDIA_PAD_FL_SOURCE) &&
482             (flags & MEDIA_LNK_FL_ENABLED)) {
483                 struct v4l2_subdev *sd;
484                 struct csiphy_device *csiphy;
485                 struct csid_device *csid;
486
487                 sd = container_of(entity, struct v4l2_subdev, entity);
488                 csiphy = v4l2_get_subdevdata(sd);
489
490                 sd = container_of(remote->entity, struct v4l2_subdev, entity);
491                 csid = v4l2_get_subdevdata(sd);
492
493                 csiphy->cfg.csid_id = csid->id;
494         }
495
496         return 0;
497 }
498
499 static const struct v4l2_subdev_core_ops csiphy_core_ops = {
500         .s_power = csiphy_set_power,
501 };
502
503 static const struct v4l2_subdev_video_ops csiphy_video_ops = {
504         .s_stream = csiphy_set_stream,
505 };
506
507 static const struct v4l2_subdev_pad_ops csiphy_pad_ops = {
508         .get_fmt = csiphy_get_format,
509         .set_fmt = csiphy_set_format,
510 };
511
512 static const struct v4l2_subdev_ops csiphy_v4l2_ops = {
513         .core = &csiphy_core_ops,
514         .video = &csiphy_video_ops,
515         .pad = &csiphy_pad_ops,
516 };
517
518 static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops;
519
520 static const struct media_entity_operations csiphy_media_ops = {
521         .link_setup = csiphy_link_setup,
522         .link_validate = v4l2_subdev_link_validate,
523 };
524
525 /*
526  * msm_csiphy_register_entities - Register subdev node for CSIPHY module
527  * @csiphy: CSIPHY device
528  * @v4l2_dev: V4L2 device
529  *
530  * Return 0 on success or a negative error code otherwise
531  */
532 int msm_csiphy_register_entities(struct csiphy_device *csiphy,
533                                  struct v4l2_device *v4l2_dev)
534 {
535         struct v4l2_subdev *sd = &csiphy->subdev;
536         struct media_pad *pads = csiphy->pads;
537         int ret;
538
539         v4l2_subdev_init(sd, &csiphy_v4l2_ops);
540         sd->internal_ops = &csiphy_v4l2_internal_ops;
541         sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
542         snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
543                  MSM_CSIPHY_NAME, csiphy->id);
544         v4l2_set_subdevdata(sd, csiphy);
545
546         pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
547         pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
548
549         sd->entity.ops = &csiphy_media_ops;
550         ret = media_entity_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads, 0);
551         if (ret < 0) {
552                 pr_err("Fail to init media entity");
553                 return ret;
554         }
555
556         ret = v4l2_device_register_subdev(v4l2_dev, sd);
557         if (ret < 0) {
558                 pr_err("Fail to register subdev");
559                 media_entity_cleanup(&sd->entity);
560         }
561
562         return ret;
563 }
564
565 /*
566  * msm_csiphy_unregister_entities - Unregister CSIPHY module subdev node
567  * @csiphy: CSIPHY device
568  */
569 void msm_csiphy_unregister_entities(struct csiphy_device *csiphy)
570 {
571         v4l2_device_unregister_subdev(&csiphy->subdev);
572 }