.. | .. |
---|
2 | 2 | /* |
---|
3 | 3 | * Driver for Cadence MIPI-CSI2 TX Controller |
---|
4 | 4 | * |
---|
5 | | - * Copyright (C) 2017-2018 Cadence Design Systems Inc. |
---|
| 5 | + * Copyright (C) 2017-2019 Cadence Design Systems Inc. |
---|
6 | 6 | */ |
---|
7 | 7 | |
---|
8 | 8 | #include <linux/clk.h> |
---|
.. | .. |
---|
52 | 52 | #define CSI2TX_STREAM_IF_CFG_REG(n) (0x100 + (n) * 4) |
---|
53 | 53 | #define CSI2TX_STREAM_IF_CFG_FILL_LEVEL(n) ((n) & 0x1f) |
---|
54 | 54 | |
---|
| 55 | +/* CSI2TX V2 Registers */ |
---|
| 56 | +#define CSI2TX_V2_DPHY_CFG_REG 0x28 |
---|
| 57 | +#define CSI2TX_V2_DPHY_CFG_RESET BIT(16) |
---|
| 58 | +#define CSI2TX_V2_DPHY_CFG_CLOCK_MODE BIT(10) |
---|
| 59 | +#define CSI2TX_V2_DPHY_CFG_MODE_MASK GENMASK(9, 8) |
---|
| 60 | +#define CSI2TX_V2_DPHY_CFG_MODE_LPDT (2 << 8) |
---|
| 61 | +#define CSI2TX_V2_DPHY_CFG_MODE_HS (1 << 8) |
---|
| 62 | +#define CSI2TX_V2_DPHY_CFG_MODE_ULPS (0 << 8) |
---|
| 63 | +#define CSI2TX_V2_DPHY_CFG_CLK_ENABLE BIT(4) |
---|
| 64 | +#define CSI2TX_V2_DPHY_CFG_LANE_ENABLE(n) BIT(n) |
---|
| 65 | + |
---|
55 | 66 | #define CSI2TX_LANES_MAX 4 |
---|
56 | 67 | #define CSI2TX_STREAMS_MAX 4 |
---|
57 | 68 | |
---|
.. | .. |
---|
70 | 81 | u32 bpp; |
---|
71 | 82 | }; |
---|
72 | 83 | |
---|
| 84 | +struct csi2tx_priv; |
---|
| 85 | + |
---|
| 86 | +/* CSI2TX Variant Operations */ |
---|
| 87 | +struct csi2tx_vops { |
---|
| 88 | + void (*dphy_setup)(struct csi2tx_priv *csi2tx); |
---|
| 89 | +}; |
---|
| 90 | + |
---|
73 | 91 | struct csi2tx_priv { |
---|
74 | 92 | struct device *dev; |
---|
75 | 93 | unsigned int count; |
---|
.. | .. |
---|
81 | 99 | struct mutex lock; |
---|
82 | 100 | |
---|
83 | 101 | void __iomem *base; |
---|
| 102 | + |
---|
| 103 | + struct csi2tx_vops *vops; |
---|
84 | 104 | |
---|
85 | 105 | struct clk *esc_clk; |
---|
86 | 106 | struct clk *p_clk; |
---|
.. | .. |
---|
209 | 229 | .set_fmt = csi2tx_set_pad_format, |
---|
210 | 230 | }; |
---|
211 | 231 | |
---|
| 232 | +/* Set Wake Up value in the D-PHY */ |
---|
| 233 | +static void csi2tx_dphy_set_wakeup(struct csi2tx_priv *csi2tx) |
---|
| 234 | +{ |
---|
| 235 | + writel(CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(32), |
---|
| 236 | + csi2tx->base + CSI2TX_DPHY_CLK_WAKEUP_REG); |
---|
| 237 | +} |
---|
| 238 | + |
---|
| 239 | +/* |
---|
| 240 | + * Finishes the D-PHY initialization |
---|
| 241 | + * reg dphy cfg value to be used |
---|
| 242 | + */ |
---|
| 243 | +static void csi2tx_dphy_init_finish(struct csi2tx_priv *csi2tx, u32 reg) |
---|
| 244 | +{ |
---|
| 245 | + unsigned int i; |
---|
| 246 | + |
---|
| 247 | + udelay(10); |
---|
| 248 | + |
---|
| 249 | + /* Enable our (clock and data) lanes */ |
---|
| 250 | + reg |= CSI2TX_DPHY_CFG_CLK_ENABLE; |
---|
| 251 | + for (i = 0; i < csi2tx->num_lanes; i++) |
---|
| 252 | + reg |= CSI2TX_DPHY_CFG_LANE_ENABLE(csi2tx->lanes[i] - 1); |
---|
| 253 | + writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); |
---|
| 254 | + |
---|
| 255 | + udelay(10); |
---|
| 256 | + |
---|
| 257 | + /* Switch to HS mode */ |
---|
| 258 | + reg &= ~CSI2TX_DPHY_CFG_MODE_MASK; |
---|
| 259 | + writel(reg | CSI2TX_DPHY_CFG_MODE_HS, |
---|
| 260 | + csi2tx->base + CSI2TX_DPHY_CFG_REG); |
---|
| 261 | +} |
---|
| 262 | + |
---|
| 263 | +/* Configures D-PHY in CSIv1.3 */ |
---|
| 264 | +static void csi2tx_dphy_setup(struct csi2tx_priv *csi2tx) |
---|
| 265 | +{ |
---|
| 266 | + u32 reg; |
---|
| 267 | + unsigned int i; |
---|
| 268 | + |
---|
| 269 | + csi2tx_dphy_set_wakeup(csi2tx); |
---|
| 270 | + |
---|
| 271 | + /* Put our lanes (clock and data) out of reset */ |
---|
| 272 | + reg = CSI2TX_DPHY_CFG_CLK_RESET | CSI2TX_DPHY_CFG_MODE_LPDT; |
---|
| 273 | + for (i = 0; i < csi2tx->num_lanes; i++) |
---|
| 274 | + reg |= CSI2TX_DPHY_CFG_LANE_RESET(csi2tx->lanes[i] - 1); |
---|
| 275 | + writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); |
---|
| 276 | + |
---|
| 277 | + csi2tx_dphy_init_finish(csi2tx, reg); |
---|
| 278 | +} |
---|
| 279 | + |
---|
| 280 | +/* Configures D-PHY in CSIv2 */ |
---|
| 281 | +static void csi2tx_v2_dphy_setup(struct csi2tx_priv *csi2tx) |
---|
| 282 | +{ |
---|
| 283 | + u32 reg; |
---|
| 284 | + |
---|
| 285 | + csi2tx_dphy_set_wakeup(csi2tx); |
---|
| 286 | + |
---|
| 287 | + /* Put our lanes (clock and data) out of reset */ |
---|
| 288 | + reg = CSI2TX_V2_DPHY_CFG_RESET | CSI2TX_V2_DPHY_CFG_MODE_LPDT; |
---|
| 289 | + writel(reg, csi2tx->base + CSI2TX_V2_DPHY_CFG_REG); |
---|
| 290 | + |
---|
| 291 | + csi2tx_dphy_init_finish(csi2tx, reg); |
---|
| 292 | +} |
---|
| 293 | + |
---|
212 | 294 | static void csi2tx_reset(struct csi2tx_priv *csi2tx) |
---|
213 | 295 | { |
---|
214 | 296 | writel(CSI2TX_CONFIG_SRST_REQ, csi2tx->base + CSI2TX_CONFIG_REG); |
---|
.. | .. |
---|
221 | 303 | struct media_entity *entity = &csi2tx->subdev.entity; |
---|
222 | 304 | struct media_link *link; |
---|
223 | 305 | unsigned int i; |
---|
224 | | - u32 reg; |
---|
225 | 306 | |
---|
226 | 307 | csi2tx_reset(csi2tx); |
---|
227 | 308 | |
---|
.. | .. |
---|
229 | 310 | |
---|
230 | 311 | udelay(10); |
---|
231 | 312 | |
---|
232 | | - /* Configure our PPI interface with the D-PHY */ |
---|
233 | | - writel(CSI2TX_DPHY_CLK_WAKEUP_ULPS_CYCLES(32), |
---|
234 | | - csi2tx->base + CSI2TX_DPHY_CLK_WAKEUP_REG); |
---|
235 | | - |
---|
236 | | - /* Put our lanes (clock and data) out of reset */ |
---|
237 | | - reg = CSI2TX_DPHY_CFG_CLK_RESET | CSI2TX_DPHY_CFG_MODE_LPDT; |
---|
238 | | - for (i = 0; i < csi2tx->num_lanes; i++) |
---|
239 | | - reg |= CSI2TX_DPHY_CFG_LANE_RESET(csi2tx->lanes[i]); |
---|
240 | | - writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); |
---|
241 | | - |
---|
242 | | - udelay(10); |
---|
243 | | - |
---|
244 | | - /* Enable our (clock and data) lanes */ |
---|
245 | | - reg |= CSI2TX_DPHY_CFG_CLK_ENABLE; |
---|
246 | | - for (i = 0; i < csi2tx->num_lanes; i++) |
---|
247 | | - reg |= CSI2TX_DPHY_CFG_LANE_ENABLE(csi2tx->lanes[i]); |
---|
248 | | - writel(reg, csi2tx->base + CSI2TX_DPHY_CFG_REG); |
---|
249 | | - |
---|
250 | | - udelay(10); |
---|
251 | | - |
---|
252 | | - /* Switch to HS mode */ |
---|
253 | | - reg &= ~CSI2TX_DPHY_CFG_MODE_MASK; |
---|
254 | | - writel(reg | CSI2TX_DPHY_CFG_MODE_HS, |
---|
255 | | - csi2tx->base + CSI2TX_DPHY_CFG_REG); |
---|
256 | | - |
---|
257 | | - udelay(10); |
---|
| 313 | + if (csi2tx->vops && csi2tx->vops->dphy_setup) { |
---|
| 314 | + csi2tx->vops->dphy_setup(csi2tx); |
---|
| 315 | + udelay(10); |
---|
| 316 | + } |
---|
258 | 317 | |
---|
259 | 318 | /* |
---|
260 | 319 | * Create a static mapping between the CSI virtual channels |
---|
.. | .. |
---|
432 | 491 | |
---|
433 | 492 | static int csi2tx_check_lanes(struct csi2tx_priv *csi2tx) |
---|
434 | 493 | { |
---|
435 | | - struct v4l2_fwnode_endpoint v4l2_ep; |
---|
| 494 | + struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; |
---|
436 | 495 | struct device_node *ep; |
---|
437 | | - int ret; |
---|
| 496 | + int ret, i; |
---|
438 | 497 | |
---|
439 | 498 | ep = of_graph_get_endpoint_by_regs(csi2tx->dev->of_node, 0, 0); |
---|
440 | 499 | if (!ep) |
---|
.. | .. |
---|
446 | 505 | goto out; |
---|
447 | 506 | } |
---|
448 | 507 | |
---|
449 | | - if (v4l2_ep.bus_type != V4L2_MBUS_CSI2) { |
---|
| 508 | + if (v4l2_ep.bus_type != V4L2_MBUS_CSI2_DPHY) { |
---|
450 | 509 | dev_err(csi2tx->dev, "Unsupported media bus type: 0x%x\n", |
---|
451 | 510 | v4l2_ep.bus_type); |
---|
452 | 511 | ret = -EINVAL; |
---|
.. | .. |
---|
461 | 520 | goto out; |
---|
462 | 521 | } |
---|
463 | 522 | |
---|
| 523 | + for (i = 0; i < csi2tx->num_lanes; i++) { |
---|
| 524 | + if (v4l2_ep.bus.mipi_csi2.data_lanes[i] < 1) { |
---|
| 525 | + dev_err(csi2tx->dev, "Invalid lane[%d] number: %u\n", |
---|
| 526 | + i, v4l2_ep.bus.mipi_csi2.data_lanes[i]); |
---|
| 527 | + ret = -EINVAL; |
---|
| 528 | + goto out; |
---|
| 529 | + } |
---|
| 530 | + } |
---|
| 531 | + |
---|
464 | 532 | memcpy(csi2tx->lanes, v4l2_ep.bus.mipi_csi2.data_lanes, |
---|
465 | 533 | sizeof(csi2tx->lanes)); |
---|
466 | 534 | |
---|
.. | .. |
---|
469 | 537 | return ret; |
---|
470 | 538 | } |
---|
471 | 539 | |
---|
| 540 | +static const struct csi2tx_vops csi2tx_vops = { |
---|
| 541 | + .dphy_setup = csi2tx_dphy_setup, |
---|
| 542 | +}; |
---|
| 543 | + |
---|
| 544 | +static const struct csi2tx_vops csi2tx_v2_vops = { |
---|
| 545 | + .dphy_setup = csi2tx_v2_dphy_setup, |
---|
| 546 | +}; |
---|
| 547 | + |
---|
| 548 | +static const struct of_device_id csi2tx_of_table[] = { |
---|
| 549 | + { |
---|
| 550 | + .compatible = "cdns,csi2tx", |
---|
| 551 | + .data = &csi2tx_vops |
---|
| 552 | + }, |
---|
| 553 | + { |
---|
| 554 | + .compatible = "cdns,csi2tx-1.3", |
---|
| 555 | + .data = &csi2tx_vops |
---|
| 556 | + }, |
---|
| 557 | + { |
---|
| 558 | + .compatible = "cdns,csi2tx-2.1", |
---|
| 559 | + .data = &csi2tx_v2_vops |
---|
| 560 | + }, |
---|
| 561 | + { } |
---|
| 562 | +}; |
---|
| 563 | +MODULE_DEVICE_TABLE(of, csi2tx_of_table); |
---|
| 564 | + |
---|
472 | 565 | static int csi2tx_probe(struct platform_device *pdev) |
---|
473 | 566 | { |
---|
474 | 567 | struct csi2tx_priv *csi2tx; |
---|
| 568 | + const struct of_device_id *of_id; |
---|
475 | 569 | unsigned int i; |
---|
476 | 570 | int ret; |
---|
477 | 571 | |
---|
.. | .. |
---|
485 | 579 | ret = csi2tx_get_resources(csi2tx, pdev); |
---|
486 | 580 | if (ret) |
---|
487 | 581 | goto err_free_priv; |
---|
| 582 | + |
---|
| 583 | + of_id = of_match_node(csi2tx_of_table, pdev->dev.of_node); |
---|
| 584 | + csi2tx->vops = (struct csi2tx_vops *)of_id->data; |
---|
488 | 585 | |
---|
489 | 586 | v4l2_subdev_init(&csi2tx->subdev, &csi2tx_subdev_ops); |
---|
490 | 587 | csi2tx->subdev.owner = THIS_MODULE; |
---|
.. | .. |
---|
542 | 639 | |
---|
543 | 640 | return 0; |
---|
544 | 641 | } |
---|
545 | | - |
---|
546 | | -static const struct of_device_id csi2tx_of_table[] = { |
---|
547 | | - { .compatible = "cdns,csi2tx" }, |
---|
548 | | - { }, |
---|
549 | | -}; |
---|
550 | | -MODULE_DEVICE_TABLE(of, csi2tx_of_table); |
---|
551 | 642 | |
---|
552 | 643 | static struct platform_driver csi2tx_driver = { |
---|
553 | 644 | .probe = csi2tx_probe, |
---|