| .. | .. |
|---|
| 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, |
|---|