4 * Qualcomm MSM Camera Subsystem - CSIPHY Module
6 * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
7 * Copyright (C) 2016 Linaro Ltd.
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.
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.
18 #include <linux/clk.h>
19 #include <linux/delay.h>
20 #include <linux/interrupt.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>
30 #define MSM_CSIPHY_NAME "msm_csiphy"
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
47 * csiphy_isr - CSIPHY module interrupt handler
48 * @irq: Interrupt line
51 * Return IRQ_HANDLED on success
53 static irqreturn_t csiphy_isr(int irq, void *dev)
55 struct csiphy_device *csiphy = dev;
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));
74 * csiphy_reset - Perform software reset on CSIPHY module
75 * @csiphy: CSIPHY device
77 static void csiphy_reset(struct csiphy_device *csiphy)
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);
85 * csiphy_enable_clocks - Enable clocks for CSIPHY module and
86 * set clock rates where needed
87 * @csiphy: CSIPHY device
89 * Return 0 on success or a negative error code otherwise
91 static int csiphy_enable_clocks(struct csiphy_device *csiphy)
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]);
102 dev_err(csiphy->camss->dev, "round failed\n");
106 ret = clk_set_rate(csiphy->clock[i], clk_rate);
108 dev_err(csiphy->camss->dev, "set rate failed\n");
112 ret = clk_prepare_enable(csiphy->clock[i]);
114 dev_err(csiphy->camss->dev, "clk enable failed\n");
122 for (i--; i >= 0; i--)
123 clk_disable_unprepare(csiphy->clock[i]);
129 * csiphy_disable_clocks - Disable clocks for CSIPHY module
130 * @csiphy: CSIPHY device
132 static void csiphy_disable_clocks(struct csiphy_device *csiphy)
136 for (i = csiphy->nclocks - 1; i >= 0; i--)
137 clk_disable_unprepare(csiphy->clock[i]);
141 * csiphy_set_power - Power on/off CSIPHY module
142 * @sd: CSIPHY V4L2 subdevice
143 * @on: Requested power state
145 * Return 0 on success or a negative error code otherwise
147 static int csiphy_set_power(struct v4l2_subdev *sd, int on)
149 struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
152 dev_err(csiphy->camss->dev, "%s: Enter, csiphy%d on = %d\n",
153 __func__, csiphy->id, on);
158 ret = csiphy_enable_clocks(csiphy);
162 enable_irq(csiphy->irq);
164 csiphy_reset(csiphy);
166 hw_version = readl(csiphy->base + CAMSS_CSI_PHY_HW_VERSION);
167 dev_err(csiphy->camss->dev, "CSIPHY HW Version = 0x%02x\n",
170 disable_irq(csiphy->irq);
172 csiphy_disable_clocks(csiphy);
175 dev_err(csiphy->camss->dev, "%s: Exit csiphy%d on = %d\n",
176 __func__, csiphy->id, on);
182 * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter
183 * @lane_cfg - CSI2 lane configuration
187 static int csiphy_get_lane_mask(struct camss_csiphy_lanes_cfg *lane_cfg)
192 lane_mask = 1 << lane_cfg->clk.pos;
194 for (i = 0; i < lane_cfg->num_data; i++)
195 lane_mask |= 1 << lane_cfg->data[i].pos;
201 * csiphy_set_stream - Enable/disable streaming on CSIPHY module
202 * @sd: CSIPHY V4L2 subdevice
203 * @enable: Requested streaming state
205 * Main configuration of CSIPHY module is also done here.
207 * Return 0 on success or a negative error code otherwise
209 static int csiphy_set_stream(struct v4l2_subdev *sd, int enable)
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);
217 dev_err(csiphy->camss->dev, "%s: Enter, csiphy%d enable = %d\n",
218 __func__, csiphy->id, enable);
221 val = readl(csiphy->base_clk_mux);
222 if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) {
224 val |= cfg->csid_id << 4;
229 writel_relaxed(val, csiphy->base_clk_mux);
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);
237 val |= lane_mask << 1;
238 writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
240 val = cfg->combo_mode << 4;
241 writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
245 while (lane_mask & 0x1f) {
246 if (!(lane_mask & 0x1)) {
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));
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));
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));
281 writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_LNn_CFG2(4));
282 writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
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
295 * Return pointer to TRY or ACTIVE format structure
297 static struct v4l2_mbus_framefmt *
298 __csiphy_get_format(struct csiphy_device *csiphy,
299 struct v4l2_subdev_pad_config *cfg,
301 enum v4l2_subdev_format_whence which)
303 if (which == V4L2_SUBDEV_FORMAT_TRY)
304 return v4l2_subdev_get_try_format(&csiphy->subdev, cfg, pad);
306 return &csiphy->fmt[pad];
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
315 * Return -EINVAL or zero on success
317 static int csiphy_get_format(struct v4l2_subdev *sd,
318 struct v4l2_subdev_pad_config *cfg,
319 struct v4l2_subdev_format *fmt)
321 struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
322 struct v4l2_mbus_framefmt *format;
324 format = __csiphy_get_format(csiphy, cfg, fmt->pad, fmt->which);
328 fmt->format = *format;
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
339 * Return -EINVAL or zero on success
341 static int csiphy_set_format(struct v4l2_subdev *sd,
342 struct v4l2_subdev_pad_config *cfg,
343 struct v4l2_subdev_format *fmt)
345 struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
346 struct v4l2_mbus_framefmt *format;
348 if (fmt->pad == MSM_CSIPHY_PAD_SINK) {
349 /* Set format on sink pad */
350 format = __csiphy_get_format(csiphy, cfg, fmt->pad,
355 *format = fmt->format;
357 /* Reset format on source pad */
358 format = __csiphy_get_format(csiphy, cfg, MSM_CSIPHY_PAD_SRC,
363 *format = fmt->format;
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);
371 fmt->format = *format;
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
384 * Return 0 on success or a negative error code otherwise
386 int msm_csiphy_subdev_init(struct csiphy_device *csiphy, struct camss *camss,
387 struct resources *res, u8 id)
389 struct device *dev = camss->dev;
390 struct platform_device *pdev = container_of(dev, struct platform_device, dev);
395 csiphy->camss = camss;
397 dev_err(csiphy->camss->dev, "%s: Enter\n", __func__);
401 csiphy->cfg.combo_mode = 0;
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);
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);
421 r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res->interrupt[0]);
422 csiphy->irq = r->start;
423 if (IS_ERR_VALUE(csiphy->irq))
426 ret = devm_request_irq(dev, csiphy->irq, csiphy_isr,
427 IRQF_TRIGGER_RISING, dev_name(dev), csiphy);
429 dev_err(dev, "request_irq failed\n");
433 disable_irq(csiphy->irq);
439 while (res->clock[i++])
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");
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");
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];
463 dev_err(csiphy->camss->dev, "%s: Exit\n", __func__);
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
475 * Rreturn 0 on success
477 static int csiphy_link_setup(struct media_entity *entity,
478 const struct media_pad *local,
479 const struct media_pad *remote, u32 flags)
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;
487 sd = container_of(entity, struct v4l2_subdev, entity);
488 csiphy = v4l2_get_subdevdata(sd);
490 sd = container_of(remote->entity, struct v4l2_subdev, entity);
491 csid = v4l2_get_subdevdata(sd);
493 csiphy->cfg.csid_id = csid->id;
499 static const struct v4l2_subdev_core_ops csiphy_core_ops = {
500 .s_power = csiphy_set_power,
503 static const struct v4l2_subdev_video_ops csiphy_video_ops = {
504 .s_stream = csiphy_set_stream,
507 static const struct v4l2_subdev_pad_ops csiphy_pad_ops = {
508 .get_fmt = csiphy_get_format,
509 .set_fmt = csiphy_set_format,
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,
518 static const struct v4l2_subdev_internal_ops csiphy_v4l2_internal_ops;
520 static const struct media_entity_operations csiphy_media_ops = {
521 .link_setup = csiphy_link_setup,
522 .link_validate = v4l2_subdev_link_validate,
526 * msm_csiphy_register_entities - Register subdev node for CSIPHY module
527 * @csiphy: CSIPHY device
528 * @v4l2_dev: V4L2 device
530 * Return 0 on success or a negative error code otherwise
532 int msm_csiphy_register_entities(struct csiphy_device *csiphy,
533 struct v4l2_device *v4l2_dev)
535 struct v4l2_subdev *sd = &csiphy->subdev;
536 struct media_pad *pads = csiphy->pads;
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);
546 pads[MSM_CSIPHY_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
547 pads[MSM_CSIPHY_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
549 sd->entity.ops = &csiphy_media_ops;
550 ret = media_entity_init(&sd->entity, MSM_CSIPHY_PADS_NUM, pads, 0);
552 pr_err("Fail to init media entity");
556 ret = v4l2_device_register_subdev(v4l2_dev, sd);
558 pr_err("Fail to register subdev");
559 media_entity_cleanup(&sd->entity);
566 * msm_csiphy_unregister_entities - Unregister CSIPHY module subdev node
567 * @csiphy: CSIPHY device
569 void msm_csiphy_unregister_entities(struct csiphy_device *csiphy)
571 v4l2_device_unregister_subdev(&csiphy->subdev);