| .. | .. |
|---|
| 14 | 14 | #include <linux/of_graph.h> |
|---|
| 15 | 15 | #include <linux/platform_device.h> |
|---|
| 16 | 16 | #include <linux/pm_runtime.h> |
|---|
| 17 | +#include <linux/reset.h> |
|---|
| 17 | 18 | #include <linux/sys_soc.h> |
|---|
| 18 | 19 | |
|---|
| 19 | 20 | #include <media/v4l2-ctrls.h> |
|---|
| .. | .. |
|---|
| 51 | 52 | |
|---|
| 52 | 53 | /* |
|---|
| 53 | 54 | * Channel Data Type Select |
|---|
| 54 | | - * VCDT[0-15]: Channel 1 VCDT[16-31]: Channel 2 |
|---|
| 55 | | - * VCDT2[0-15]: Channel 3 VCDT2[16-31]: Channel 4 |
|---|
| 55 | + * VCDT[0-15]: Channel 0 VCDT[16-31]: Channel 1 |
|---|
| 56 | + * VCDT2[0-15]: Channel 2 VCDT2[16-31]: Channel 3 |
|---|
| 56 | 57 | */ |
|---|
| 57 | 58 | #define VCDT_REG 0x10 |
|---|
| 58 | 59 | #define VCDT2_REG 0x14 |
|---|
| .. | .. |
|---|
| 67 | 68 | /* Field Detection Control */ |
|---|
| 68 | 69 | #define FLD_REG 0x1c |
|---|
| 69 | 70 | #define FLD_FLD_NUM(n) (((n) & 0xff) << 16) |
|---|
| 71 | +#define FLD_DET_SEL(n) (((n) & 0x3) << 4) |
|---|
| 70 | 72 | #define FLD_FLD_EN4 BIT(3) |
|---|
| 71 | 73 | #define FLD_FLD_EN3 BIT(2) |
|---|
| 72 | 74 | #define FLD_FLD_EN2 BIT(1) |
|---|
| .. | .. |
|---|
| 83 | 85 | |
|---|
| 84 | 86 | /* Interrupt Enable */ |
|---|
| 85 | 87 | #define INTEN_REG 0x30 |
|---|
| 88 | +#define INTEN_INT_AFIFO_OF BIT(27) |
|---|
| 89 | +#define INTEN_INT_ERRSOTHS BIT(4) |
|---|
| 90 | +#define INTEN_INT_ERRSOTSYNCHS BIT(3) |
|---|
| 86 | 91 | |
|---|
| 87 | 92 | /* Interrupt Source Mask */ |
|---|
| 88 | 93 | #define INTCLOSE_REG 0x34 |
|---|
| .. | .. |
|---|
| 315 | 320 | { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 }, |
|---|
| 316 | 321 | { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 }, |
|---|
| 317 | 322 | { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 }, |
|---|
| 323 | + { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .datatype = 0x2a, .bpp = 8 }, |
|---|
| 324 | + { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .datatype = 0x2a, .bpp = 8 }, |
|---|
| 325 | + { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .datatype = 0x2a, .bpp = 8 }, |
|---|
| 326 | + { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .datatype = 0x2a, .bpp = 8 }, |
|---|
| 318 | 327 | }; |
|---|
| 319 | 328 | |
|---|
| 320 | 329 | static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code) |
|---|
| .. | .. |
|---|
| 339 | 348 | |
|---|
| 340 | 349 | struct rcar_csi2_info { |
|---|
| 341 | 350 | int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps); |
|---|
| 342 | | - int (*confirm_start)(struct rcar_csi2 *priv); |
|---|
| 351 | + int (*phy_post_init)(struct rcar_csi2 *priv); |
|---|
| 343 | 352 | const struct rcsi2_mbps_reg *hsfreqrange; |
|---|
| 344 | 353 | unsigned int csi0clkfreqrange; |
|---|
| 354 | + unsigned int num_channels; |
|---|
| 345 | 355 | bool clear_ulps; |
|---|
| 346 | 356 | }; |
|---|
| 347 | 357 | |
|---|
| .. | .. |
|---|
| 349 | 359 | struct device *dev; |
|---|
| 350 | 360 | void __iomem *base; |
|---|
| 351 | 361 | const struct rcar_csi2_info *info; |
|---|
| 362 | + struct reset_control *rstc; |
|---|
| 352 | 363 | |
|---|
| 353 | 364 | struct v4l2_subdev subdev; |
|---|
| 354 | 365 | struct media_pad pads[NR_OF_RCAR_CSI2_PAD]; |
|---|
| 355 | 366 | |
|---|
| 356 | 367 | struct v4l2_async_notifier notifier; |
|---|
| 357 | | - struct v4l2_async_subdev asd; |
|---|
| 358 | 368 | struct v4l2_subdev *remote; |
|---|
| 369 | + unsigned int remote_pad; |
|---|
| 359 | 370 | |
|---|
| 360 | 371 | struct v4l2_mbus_framefmt mf; |
|---|
| 361 | 372 | |
|---|
| .. | .. |
|---|
| 386 | 397 | iowrite32(data, priv->base + reg); |
|---|
| 387 | 398 | } |
|---|
| 388 | 399 | |
|---|
| 389 | | -static void rcsi2_reset(struct rcar_csi2 *priv) |
|---|
| 400 | +static void rcsi2_enter_standby(struct rcar_csi2 *priv) |
|---|
| 390 | 401 | { |
|---|
| 391 | | - rcsi2_write(priv, SRST_REG, SRST_SRST); |
|---|
| 402 | + rcsi2_write(priv, PHYCNT_REG, 0); |
|---|
| 403 | + rcsi2_write(priv, PHTC_REG, PHTC_TESTCLR); |
|---|
| 404 | + reset_control_assert(priv->rstc); |
|---|
| 392 | 405 | usleep_range(100, 150); |
|---|
| 393 | | - rcsi2_write(priv, SRST_REG, 0); |
|---|
| 406 | + pm_runtime_put(priv->dev); |
|---|
| 394 | 407 | } |
|---|
| 395 | 408 | |
|---|
| 396 | | -static int rcsi2_wait_phy_start(struct rcar_csi2 *priv) |
|---|
| 409 | +static void rcsi2_exit_standby(struct rcar_csi2 *priv) |
|---|
| 410 | +{ |
|---|
| 411 | + pm_runtime_get_sync(priv->dev); |
|---|
| 412 | + reset_control_deassert(priv->rstc); |
|---|
| 413 | +} |
|---|
| 414 | + |
|---|
| 415 | +static int rcsi2_wait_phy_start(struct rcar_csi2 *priv, |
|---|
| 416 | + unsigned int lanes) |
|---|
| 397 | 417 | { |
|---|
| 398 | 418 | unsigned int timeout; |
|---|
| 399 | 419 | |
|---|
| 400 | 420 | /* Wait for the clock and data lanes to enter LP-11 state. */ |
|---|
| 401 | 421 | for (timeout = 0; timeout <= 20; timeout++) { |
|---|
| 402 | | - const u32 lane_mask = (1 << priv->lanes) - 1; |
|---|
| 422 | + const u32 lane_mask = (1 << lanes) - 1; |
|---|
| 403 | 423 | |
|---|
| 404 | 424 | if ((rcsi2_read(priv, PHCLM_REG) & PHCLM_STOPSTATECKL) && |
|---|
| 405 | 425 | (rcsi2_read(priv, PHDLM_REG) & lane_mask) == lane_mask) |
|---|
| .. | .. |
|---|
| 438 | 458 | return 0; |
|---|
| 439 | 459 | } |
|---|
| 440 | 460 | |
|---|
| 441 | | -static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp) |
|---|
| 461 | +static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp, |
|---|
| 462 | + unsigned int lanes) |
|---|
| 442 | 463 | { |
|---|
| 443 | 464 | struct v4l2_subdev *source; |
|---|
| 444 | 465 | struct v4l2_ctrl *ctrl; |
|---|
| .. | .. |
|---|
| 463 | 484 | * bps = link_freq * 2 |
|---|
| 464 | 485 | */ |
|---|
| 465 | 486 | mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp; |
|---|
| 466 | | - do_div(mbps, priv->lanes * 1000000); |
|---|
| 487 | + do_div(mbps, lanes * 1000000); |
|---|
| 467 | 488 | |
|---|
| 468 | 489 | return mbps; |
|---|
| 469 | 490 | } |
|---|
| 470 | 491 | |
|---|
| 471 | | -static int rcsi2_start(struct rcar_csi2 *priv) |
|---|
| 492 | +static int rcsi2_get_active_lanes(struct rcar_csi2 *priv, |
|---|
| 493 | + unsigned int *lanes) |
|---|
| 494 | +{ |
|---|
| 495 | + struct v4l2_mbus_config mbus_config = { 0 }; |
|---|
| 496 | + unsigned int num_lanes = UINT_MAX; |
|---|
| 497 | + int ret; |
|---|
| 498 | + |
|---|
| 499 | + *lanes = priv->lanes; |
|---|
| 500 | + |
|---|
| 501 | + ret = v4l2_subdev_call(priv->remote, pad, get_mbus_config, |
|---|
| 502 | + priv->remote_pad, &mbus_config); |
|---|
| 503 | + if (ret == -ENOIOCTLCMD) { |
|---|
| 504 | + dev_dbg(priv->dev, "No remote mbus configuration available\n"); |
|---|
| 505 | + return 0; |
|---|
| 506 | + } |
|---|
| 507 | + |
|---|
| 508 | + if (ret) { |
|---|
| 509 | + dev_err(priv->dev, "Failed to get remote mbus configuration\n"); |
|---|
| 510 | + return ret; |
|---|
| 511 | + } |
|---|
| 512 | + |
|---|
| 513 | + if (mbus_config.type != V4L2_MBUS_CSI2_DPHY) { |
|---|
| 514 | + dev_err(priv->dev, "Unsupported media bus type %u\n", |
|---|
| 515 | + mbus_config.type); |
|---|
| 516 | + return -EINVAL; |
|---|
| 517 | + } |
|---|
| 518 | + |
|---|
| 519 | + if (mbus_config.flags & V4L2_MBUS_CSI2_1_LANE) |
|---|
| 520 | + num_lanes = 1; |
|---|
| 521 | + else if (mbus_config.flags & V4L2_MBUS_CSI2_2_LANE) |
|---|
| 522 | + num_lanes = 2; |
|---|
| 523 | + else if (mbus_config.flags & V4L2_MBUS_CSI2_3_LANE) |
|---|
| 524 | + num_lanes = 3; |
|---|
| 525 | + else if (mbus_config.flags & V4L2_MBUS_CSI2_4_LANE) |
|---|
| 526 | + num_lanes = 4; |
|---|
| 527 | + |
|---|
| 528 | + if (num_lanes > priv->lanes) { |
|---|
| 529 | + dev_err(priv->dev, |
|---|
| 530 | + "Unsupported mbus config: too many data lanes %u\n", |
|---|
| 531 | + num_lanes); |
|---|
| 532 | + return -EINVAL; |
|---|
| 533 | + } |
|---|
| 534 | + |
|---|
| 535 | + *lanes = num_lanes; |
|---|
| 536 | + |
|---|
| 537 | + return 0; |
|---|
| 538 | +} |
|---|
| 539 | + |
|---|
| 540 | +static int rcsi2_start_receiver(struct rcar_csi2 *priv) |
|---|
| 472 | 541 | { |
|---|
| 473 | 542 | const struct rcar_csi2_format *format; |
|---|
| 474 | | - u32 phycnt, vcdt = 0, vcdt2 = 0; |
|---|
| 543 | + u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0; |
|---|
| 544 | + unsigned int lanes; |
|---|
| 475 | 545 | unsigned int i; |
|---|
| 476 | 546 | int mbps, ret; |
|---|
| 477 | 547 | |
|---|
| .. | .. |
|---|
| 485 | 555 | return -EINVAL; |
|---|
| 486 | 556 | |
|---|
| 487 | 557 | /* |
|---|
| 488 | | - * Enable all Virtual Channels. |
|---|
| 558 | + * Enable all supported CSI-2 channels with virtual channel and |
|---|
| 559 | + * data type matching. |
|---|
| 489 | 560 | * |
|---|
| 490 | 561 | * NOTE: It's not possible to get individual datatype for each |
|---|
| 491 | 562 | * source virtual channel. Once this is possible in V4L2 |
|---|
| 492 | 563 | * it should be used here. |
|---|
| 493 | 564 | */ |
|---|
| 494 | | - for (i = 0; i < 4; i++) { |
|---|
| 565 | + for (i = 0; i < priv->info->num_channels; i++) { |
|---|
| 495 | 566 | u32 vcdt_part; |
|---|
| 496 | 567 | |
|---|
| 497 | 568 | vcdt_part = VCDT_SEL_VC(i) | VCDT_VCDTN_EN | VCDT_SEL_DTN_ON | |
|---|
| .. | .. |
|---|
| 504 | 575 | vcdt2 |= vcdt_part << ((i % 2) * 16); |
|---|
| 505 | 576 | } |
|---|
| 506 | 577 | |
|---|
| 507 | | - phycnt = PHYCNT_ENABLECLK; |
|---|
| 508 | | - phycnt |= (1 << priv->lanes) - 1; |
|---|
| 578 | + if (priv->mf.field == V4L2_FIELD_ALTERNATE) { |
|---|
| 579 | + fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2 |
|---|
| 580 | + | FLD_FLD_EN; |
|---|
| 509 | 581 | |
|---|
| 510 | | - mbps = rcsi2_calc_mbps(priv, format->bpp); |
|---|
| 582 | + if (priv->mf.height == 240) |
|---|
| 583 | + fld |= FLD_FLD_NUM(0); |
|---|
| 584 | + else |
|---|
| 585 | + fld |= FLD_FLD_NUM(1); |
|---|
| 586 | + } |
|---|
| 587 | + |
|---|
| 588 | + /* |
|---|
| 589 | + * Get the number of active data lanes inspecting the remote mbus |
|---|
| 590 | + * configuration. |
|---|
| 591 | + */ |
|---|
| 592 | + ret = rcsi2_get_active_lanes(priv, &lanes); |
|---|
| 593 | + if (ret) |
|---|
| 594 | + return ret; |
|---|
| 595 | + |
|---|
| 596 | + phycnt = PHYCNT_ENABLECLK; |
|---|
| 597 | + phycnt |= (1 << lanes) - 1; |
|---|
| 598 | + |
|---|
| 599 | + mbps = rcsi2_calc_mbps(priv, format->bpp, lanes); |
|---|
| 511 | 600 | if (mbps < 0) |
|---|
| 512 | 601 | return mbps; |
|---|
| 513 | 602 | |
|---|
| 603 | + /* Enable interrupts. */ |
|---|
| 604 | + rcsi2_write(priv, INTEN_REG, INTEN_INT_AFIFO_OF | INTEN_INT_ERRSOTHS |
|---|
| 605 | + | INTEN_INT_ERRSOTSYNCHS); |
|---|
| 606 | + |
|---|
| 514 | 607 | /* Init */ |
|---|
| 515 | 608 | rcsi2_write(priv, TREF_REG, TREF_TREF); |
|---|
| 516 | | - rcsi2_reset(priv); |
|---|
| 517 | 609 | rcsi2_write(priv, PHTC_REG, 0); |
|---|
| 518 | 610 | |
|---|
| 519 | 611 | /* Configure */ |
|---|
| 520 | | - rcsi2_write(priv, FLD_REG, FLD_FLD_NUM(2) | FLD_FLD_EN4 | |
|---|
| 521 | | - FLD_FLD_EN3 | FLD_FLD_EN2 | FLD_FLD_EN); |
|---|
| 522 | 612 | rcsi2_write(priv, VCDT_REG, vcdt); |
|---|
| 523 | | - rcsi2_write(priv, VCDT2_REG, vcdt2); |
|---|
| 613 | + if (vcdt2) |
|---|
| 614 | + rcsi2_write(priv, VCDT2_REG, vcdt2); |
|---|
| 524 | 615 | /* Lanes are zero indexed. */ |
|---|
| 525 | 616 | rcsi2_write(priv, LSWAP_REG, |
|---|
| 526 | 617 | LSWAP_L0SEL(priv->lane_swap[0] - 1) | |
|---|
| .. | .. |
|---|
| 548 | 639 | rcsi2_write(priv, PHYCNT_REG, phycnt); |
|---|
| 549 | 640 | rcsi2_write(priv, LINKCNT_REG, LINKCNT_MONITOR_EN | |
|---|
| 550 | 641 | LINKCNT_REG_MONI_PACT_EN | LINKCNT_ICLK_NONSTOP); |
|---|
| 642 | + rcsi2_write(priv, FLD_REG, fld); |
|---|
| 551 | 643 | rcsi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ); |
|---|
| 552 | 644 | rcsi2_write(priv, PHYCNT_REG, phycnt | PHYCNT_SHUTDOWNZ | PHYCNT_RSTZ); |
|---|
| 553 | 645 | |
|---|
| 554 | | - ret = rcsi2_wait_phy_start(priv); |
|---|
| 646 | + ret = rcsi2_wait_phy_start(priv, lanes); |
|---|
| 555 | 647 | if (ret) |
|---|
| 556 | 648 | return ret; |
|---|
| 557 | 649 | |
|---|
| 558 | | - /* Confirm start */ |
|---|
| 559 | | - if (priv->info->confirm_start) { |
|---|
| 560 | | - ret = priv->info->confirm_start(priv); |
|---|
| 650 | + /* Run post PHY start initialization, if needed. */ |
|---|
| 651 | + if (priv->info->phy_post_init) { |
|---|
| 652 | + ret = priv->info->phy_post_init(priv); |
|---|
| 561 | 653 | if (ret) |
|---|
| 562 | 654 | return ret; |
|---|
| 563 | 655 | } |
|---|
| .. | .. |
|---|
| 570 | 662 | return 0; |
|---|
| 571 | 663 | } |
|---|
| 572 | 664 | |
|---|
| 665 | +static int rcsi2_start(struct rcar_csi2 *priv) |
|---|
| 666 | +{ |
|---|
| 667 | + int ret; |
|---|
| 668 | + |
|---|
| 669 | + rcsi2_exit_standby(priv); |
|---|
| 670 | + |
|---|
| 671 | + ret = rcsi2_start_receiver(priv); |
|---|
| 672 | + if (ret) { |
|---|
| 673 | + rcsi2_enter_standby(priv); |
|---|
| 674 | + return ret; |
|---|
| 675 | + } |
|---|
| 676 | + |
|---|
| 677 | + ret = v4l2_subdev_call(priv->remote, video, s_stream, 1); |
|---|
| 678 | + if (ret) { |
|---|
| 679 | + rcsi2_enter_standby(priv); |
|---|
| 680 | + return ret; |
|---|
| 681 | + } |
|---|
| 682 | + |
|---|
| 683 | + return 0; |
|---|
| 684 | +} |
|---|
| 685 | + |
|---|
| 573 | 686 | static void rcsi2_stop(struct rcar_csi2 *priv) |
|---|
| 574 | 687 | { |
|---|
| 575 | | - rcsi2_write(priv, PHYCNT_REG, 0); |
|---|
| 576 | | - |
|---|
| 577 | | - rcsi2_reset(priv); |
|---|
| 578 | | - |
|---|
| 579 | | - rcsi2_write(priv, PHTC_REG, PHTC_TESTCLR); |
|---|
| 688 | + rcsi2_enter_standby(priv); |
|---|
| 689 | + v4l2_subdev_call(priv->remote, video, s_stream, 0); |
|---|
| 580 | 690 | } |
|---|
| 581 | 691 | |
|---|
| 582 | 692 | static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable) |
|---|
| 583 | 693 | { |
|---|
| 584 | 694 | struct rcar_csi2 *priv = sd_to_csi2(sd); |
|---|
| 585 | | - struct v4l2_subdev *nextsd; |
|---|
| 586 | 695 | int ret = 0; |
|---|
| 587 | 696 | |
|---|
| 588 | 697 | mutex_lock(&priv->lock); |
|---|
| .. | .. |
|---|
| 592 | 701 | goto out; |
|---|
| 593 | 702 | } |
|---|
| 594 | 703 | |
|---|
| 595 | | - nextsd = priv->remote; |
|---|
| 596 | | - |
|---|
| 597 | 704 | if (enable && priv->stream_count == 0) { |
|---|
| 598 | | - pm_runtime_get_sync(priv->dev); |
|---|
| 599 | | - |
|---|
| 600 | 705 | ret = rcsi2_start(priv); |
|---|
| 601 | | - if (ret) { |
|---|
| 602 | | - pm_runtime_put(priv->dev); |
|---|
| 706 | + if (ret) |
|---|
| 603 | 707 | goto out; |
|---|
| 604 | | - } |
|---|
| 605 | | - |
|---|
| 606 | | - ret = v4l2_subdev_call(nextsd, video, s_stream, 1); |
|---|
| 607 | | - if (ret) { |
|---|
| 608 | | - rcsi2_stop(priv); |
|---|
| 609 | | - pm_runtime_put(priv->dev); |
|---|
| 610 | | - goto out; |
|---|
| 611 | | - } |
|---|
| 612 | 708 | } else if (!enable && priv->stream_count == 1) { |
|---|
| 613 | 709 | rcsi2_stop(priv); |
|---|
| 614 | | - v4l2_subdev_call(nextsd, video, s_stream, 0); |
|---|
| 615 | | - pm_runtime_put(priv->dev); |
|---|
| 616 | 710 | } |
|---|
| 617 | 711 | |
|---|
| 618 | 712 | priv->stream_count += enable ? 1 : -1; |
|---|
| .. | .. |
|---|
| 670 | 764 | .pad = &rcar_csi2_pad_ops, |
|---|
| 671 | 765 | }; |
|---|
| 672 | 766 | |
|---|
| 767 | +static irqreturn_t rcsi2_irq(int irq, void *data) |
|---|
| 768 | +{ |
|---|
| 769 | + struct rcar_csi2 *priv = data; |
|---|
| 770 | + u32 status, err_status; |
|---|
| 771 | + |
|---|
| 772 | + status = rcsi2_read(priv, INTSTATE_REG); |
|---|
| 773 | + err_status = rcsi2_read(priv, INTERRSTATE_REG); |
|---|
| 774 | + |
|---|
| 775 | + if (!status) |
|---|
| 776 | + return IRQ_HANDLED; |
|---|
| 777 | + |
|---|
| 778 | + rcsi2_write(priv, INTSTATE_REG, status); |
|---|
| 779 | + |
|---|
| 780 | + if (!err_status) |
|---|
| 781 | + return IRQ_HANDLED; |
|---|
| 782 | + |
|---|
| 783 | + rcsi2_write(priv, INTERRSTATE_REG, err_status); |
|---|
| 784 | + |
|---|
| 785 | + dev_info(priv->dev, "Transfer error, restarting CSI-2 receiver\n"); |
|---|
| 786 | + |
|---|
| 787 | + return IRQ_WAKE_THREAD; |
|---|
| 788 | +} |
|---|
| 789 | + |
|---|
| 790 | +static irqreturn_t rcsi2_irq_thread(int irq, void *data) |
|---|
| 791 | +{ |
|---|
| 792 | + struct rcar_csi2 *priv = data; |
|---|
| 793 | + |
|---|
| 794 | + mutex_lock(&priv->lock); |
|---|
| 795 | + rcsi2_stop(priv); |
|---|
| 796 | + usleep_range(1000, 2000); |
|---|
| 797 | + if (rcsi2_start(priv)) |
|---|
| 798 | + dev_warn(priv->dev, "Failed to restart CSI-2 receiver\n"); |
|---|
| 799 | + mutex_unlock(&priv->lock); |
|---|
| 800 | + |
|---|
| 801 | + return IRQ_HANDLED; |
|---|
| 802 | +} |
|---|
| 803 | + |
|---|
| 673 | 804 | /* ----------------------------------------------------------------------------- |
|---|
| 674 | 805 | * Async handling and registration of subdevices and links. |
|---|
| 675 | 806 | */ |
|---|
| .. | .. |
|---|
| 689 | 820 | } |
|---|
| 690 | 821 | |
|---|
| 691 | 822 | priv->remote = subdev; |
|---|
| 823 | + priv->remote_pad = pad; |
|---|
| 692 | 824 | |
|---|
| 693 | 825 | dev_dbg(priv->dev, "Bound %s pad: %d\n", subdev->name, pad); |
|---|
| 694 | 826 | |
|---|
| .. | .. |
|---|
| 723 | 855 | if (vep->base.port || vep->base.id) |
|---|
| 724 | 856 | return -ENOTCONN; |
|---|
| 725 | 857 | |
|---|
| 726 | | - if (vep->bus_type != V4L2_MBUS_CSI2) { |
|---|
| 858 | + if (vep->bus_type != V4L2_MBUS_CSI2_DPHY) { |
|---|
| 727 | 859 | dev_err(priv->dev, "Unsupported bus: %u\n", vep->bus_type); |
|---|
| 728 | 860 | return -EINVAL; |
|---|
| 729 | 861 | } |
|---|
| .. | .. |
|---|
| 751 | 883 | |
|---|
| 752 | 884 | static int rcsi2_parse_dt(struct rcar_csi2 *priv) |
|---|
| 753 | 885 | { |
|---|
| 886 | + struct v4l2_async_subdev *asd; |
|---|
| 887 | + struct fwnode_handle *fwnode; |
|---|
| 754 | 888 | struct device_node *ep; |
|---|
| 755 | | - struct v4l2_fwnode_endpoint v4l2_ep; |
|---|
| 889 | + struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 }; |
|---|
| 756 | 890 | int ret; |
|---|
| 757 | 891 | |
|---|
| 758 | 892 | ep = of_graph_get_endpoint_by_regs(priv->dev->of_node, 0, 0); |
|---|
| .. | .. |
|---|
| 774 | 908 | return ret; |
|---|
| 775 | 909 | } |
|---|
| 776 | 910 | |
|---|
| 777 | | - priv->asd.match.fwnode = |
|---|
| 778 | | - fwnode_graph_get_remote_endpoint(of_fwnode_handle(ep)); |
|---|
| 779 | | - priv->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; |
|---|
| 780 | | - |
|---|
| 911 | + fwnode = fwnode_graph_get_remote_endpoint(of_fwnode_handle(ep)); |
|---|
| 781 | 912 | of_node_put(ep); |
|---|
| 782 | 913 | |
|---|
| 783 | | - priv->notifier.subdevs = devm_kzalloc(priv->dev, |
|---|
| 784 | | - sizeof(*priv->notifier.subdevs), |
|---|
| 785 | | - GFP_KERNEL); |
|---|
| 786 | | - if (!priv->notifier.subdevs) |
|---|
| 787 | | - return -ENOMEM; |
|---|
| 914 | + dev_dbg(priv->dev, "Found '%pOF'\n", to_of_node(fwnode)); |
|---|
| 788 | 915 | |
|---|
| 789 | | - priv->notifier.num_subdevs = 1; |
|---|
| 790 | | - priv->notifier.subdevs[0] = &priv->asd; |
|---|
| 916 | + v4l2_async_notifier_init(&priv->notifier); |
|---|
| 791 | 917 | priv->notifier.ops = &rcar_csi2_notify_ops; |
|---|
| 792 | 918 | |
|---|
| 793 | | - dev_dbg(priv->dev, "Found '%pOF'\n", |
|---|
| 794 | | - to_of_node(priv->asd.match.fwnode)); |
|---|
| 919 | + asd = v4l2_async_notifier_add_fwnode_subdev(&priv->notifier, fwnode, |
|---|
| 920 | + sizeof(*asd)); |
|---|
| 921 | + fwnode_handle_put(fwnode); |
|---|
| 922 | + if (IS_ERR(asd)) |
|---|
| 923 | + return PTR_ERR(asd); |
|---|
| 795 | 924 | |
|---|
| 796 | | - return v4l2_async_subdev_notifier_register(&priv->subdev, |
|---|
| 797 | | - &priv->notifier); |
|---|
| 925 | + ret = v4l2_async_subdev_notifier_register(&priv->subdev, |
|---|
| 926 | + &priv->notifier); |
|---|
| 927 | + if (ret) |
|---|
| 928 | + v4l2_async_notifier_cleanup(&priv->notifier); |
|---|
| 929 | + |
|---|
| 930 | + return ret; |
|---|
| 798 | 931 | } |
|---|
| 799 | 932 | |
|---|
| 800 | 933 | /* ----------------------------------------------------------------------------- |
|---|
| .. | .. |
|---|
| 863 | 996 | return rcsi2_phtw_write(priv, value->reg, code); |
|---|
| 864 | 997 | } |
|---|
| 865 | 998 | |
|---|
| 866 | | -static int rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, unsigned int mbps) |
|---|
| 999 | +static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, |
|---|
| 1000 | + unsigned int mbps) |
|---|
| 867 | 1001 | { |
|---|
| 868 | 1002 | static const struct phtw_value step1[] = { |
|---|
| 869 | 1003 | { .data = 0xcc, .code = 0xe2 }, |
|---|
| .. | .. |
|---|
| 889 | 1023 | if (ret) |
|---|
| 890 | 1024 | return ret; |
|---|
| 891 | 1025 | |
|---|
| 892 | | - if (mbps <= 250) { |
|---|
| 1026 | + if (mbps != 0 && mbps <= 250) { |
|---|
| 893 | 1027 | ret = rcsi2_phtw_write(priv, 0x39, 0x05); |
|---|
| 894 | 1028 | if (ret) |
|---|
| 895 | 1029 | return ret; |
|---|
| .. | .. |
|---|
| 903 | 1037 | return rcsi2_phtw_write_array(priv, step2); |
|---|
| 904 | 1038 | } |
|---|
| 905 | 1039 | |
|---|
| 1040 | +static int rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, unsigned int mbps) |
|---|
| 1041 | +{ |
|---|
| 1042 | + return __rcsi2_init_phtw_h3_v3h_m3n(priv, mbps); |
|---|
| 1043 | +} |
|---|
| 1044 | + |
|---|
| 1045 | +static int rcsi2_init_phtw_h3es2(struct rcar_csi2 *priv, unsigned int mbps) |
|---|
| 1046 | +{ |
|---|
| 1047 | + return __rcsi2_init_phtw_h3_v3h_m3n(priv, 0); |
|---|
| 1048 | +} |
|---|
| 1049 | + |
|---|
| 906 | 1050 | static int rcsi2_init_phtw_v3m_e3(struct rcar_csi2 *priv, unsigned int mbps) |
|---|
| 907 | 1051 | { |
|---|
| 908 | 1052 | return rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3m_e3, 0x44); |
|---|
| 909 | 1053 | } |
|---|
| 910 | 1054 | |
|---|
| 911 | | -static int rcsi2_confirm_start_v3m_e3(struct rcar_csi2 *priv) |
|---|
| 1055 | +static int rcsi2_phy_post_init_v3m_e3(struct rcar_csi2 *priv) |
|---|
| 912 | 1056 | { |
|---|
| 913 | 1057 | static const struct phtw_value step1[] = { |
|---|
| 914 | | - { .data = 0xed, .code = 0x34 }, |
|---|
| 915 | | - { .data = 0xed, .code = 0x44 }, |
|---|
| 916 | | - { .data = 0xed, .code = 0x54 }, |
|---|
| 917 | | - { .data = 0xed, .code = 0x84 }, |
|---|
| 918 | | - { .data = 0xed, .code = 0x94 }, |
|---|
| 1058 | + { .data = 0xee, .code = 0x34 }, |
|---|
| 1059 | + { .data = 0xee, .code = 0x44 }, |
|---|
| 1060 | + { .data = 0xee, .code = 0x54 }, |
|---|
| 1061 | + { .data = 0xee, .code = 0x84 }, |
|---|
| 1062 | + { .data = 0xee, .code = 0x94 }, |
|---|
| 919 | 1063 | { /* sentinel */ }, |
|---|
| 920 | 1064 | }; |
|---|
| 921 | 1065 | |
|---|
| .. | .. |
|---|
| 934 | 1078 | struct platform_device *pdev) |
|---|
| 935 | 1079 | { |
|---|
| 936 | 1080 | struct resource *res; |
|---|
| 937 | | - int irq; |
|---|
| 1081 | + int irq, ret; |
|---|
| 938 | 1082 | |
|---|
| 939 | 1083 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
|---|
| 940 | 1084 | priv->base = devm_ioremap_resource(&pdev->dev, res); |
|---|
| .. | .. |
|---|
| 945 | 1089 | if (irq < 0) |
|---|
| 946 | 1090 | return irq; |
|---|
| 947 | 1091 | |
|---|
| 948 | | - return 0; |
|---|
| 1092 | + ret = devm_request_threaded_irq(&pdev->dev, irq, rcsi2_irq, |
|---|
| 1093 | + rcsi2_irq_thread, IRQF_SHARED, |
|---|
| 1094 | + KBUILD_MODNAME, priv); |
|---|
| 1095 | + if (ret) |
|---|
| 1096 | + return ret; |
|---|
| 1097 | + |
|---|
| 1098 | + priv->rstc = devm_reset_control_get(&pdev->dev, NULL); |
|---|
| 1099 | + |
|---|
| 1100 | + return PTR_ERR_OR_ZERO(priv->rstc); |
|---|
| 949 | 1101 | } |
|---|
| 950 | 1102 | |
|---|
| 951 | 1103 | static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = { |
|---|
| 952 | 1104 | .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, |
|---|
| 953 | 1105 | .hsfreqrange = hsfreqrange_h3_v3h_m3n, |
|---|
| 954 | 1106 | .csi0clkfreqrange = 0x20, |
|---|
| 1107 | + .num_channels = 4, |
|---|
| 955 | 1108 | .clear_ulps = true, |
|---|
| 956 | 1109 | }; |
|---|
| 957 | 1110 | |
|---|
| 958 | 1111 | static const struct rcar_csi2_info rcar_csi2_info_r8a7795es1 = { |
|---|
| 959 | 1112 | .hsfreqrange = hsfreqrange_m3w_h3es1, |
|---|
| 1113 | + .num_channels = 4, |
|---|
| 1114 | +}; |
|---|
| 1115 | + |
|---|
| 1116 | +static const struct rcar_csi2_info rcar_csi2_info_r8a7795es2 = { |
|---|
| 1117 | + .init_phtw = rcsi2_init_phtw_h3es2, |
|---|
| 1118 | + .hsfreqrange = hsfreqrange_h3_v3h_m3n, |
|---|
| 1119 | + .csi0clkfreqrange = 0x20, |
|---|
| 1120 | + .num_channels = 4, |
|---|
| 1121 | + .clear_ulps = true, |
|---|
| 960 | 1122 | }; |
|---|
| 961 | 1123 | |
|---|
| 962 | 1124 | static const struct rcar_csi2_info rcar_csi2_info_r8a7796 = { |
|---|
| 963 | 1125 | .hsfreqrange = hsfreqrange_m3w_h3es1, |
|---|
| 1126 | + .num_channels = 4, |
|---|
| 964 | 1127 | }; |
|---|
| 965 | 1128 | |
|---|
| 966 | 1129 | static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = { |
|---|
| 967 | 1130 | .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, |
|---|
| 968 | 1131 | .hsfreqrange = hsfreqrange_h3_v3h_m3n, |
|---|
| 969 | 1132 | .csi0clkfreqrange = 0x20, |
|---|
| 1133 | + .num_channels = 4, |
|---|
| 970 | 1134 | .clear_ulps = true, |
|---|
| 971 | 1135 | }; |
|---|
| 972 | 1136 | |
|---|
| 973 | 1137 | static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = { |
|---|
| 974 | 1138 | .init_phtw = rcsi2_init_phtw_v3m_e3, |
|---|
| 975 | | - .confirm_start = rcsi2_confirm_start_v3m_e3, |
|---|
| 1139 | + .phy_post_init = rcsi2_phy_post_init_v3m_e3, |
|---|
| 1140 | + .num_channels = 4, |
|---|
| 1141 | +}; |
|---|
| 1142 | + |
|---|
| 1143 | +static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = { |
|---|
| 1144 | + .init_phtw = rcsi2_init_phtw_h3_v3h_m3n, |
|---|
| 1145 | + .hsfreqrange = hsfreqrange_h3_v3h_m3n, |
|---|
| 1146 | + .csi0clkfreqrange = 0x20, |
|---|
| 1147 | + .clear_ulps = true, |
|---|
| 1148 | +}; |
|---|
| 1149 | + |
|---|
| 1150 | +static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = { |
|---|
| 1151 | + .init_phtw = rcsi2_init_phtw_v3m_e3, |
|---|
| 1152 | + .phy_post_init = rcsi2_phy_post_init_v3m_e3, |
|---|
| 1153 | + .num_channels = 2, |
|---|
| 976 | 1154 | }; |
|---|
| 977 | 1155 | |
|---|
| 978 | 1156 | static const struct of_device_id rcar_csi2_of_table[] = { |
|---|
| 1157 | + { |
|---|
| 1158 | + .compatible = "renesas,r8a774a1-csi2", |
|---|
| 1159 | + .data = &rcar_csi2_info_r8a7796, |
|---|
| 1160 | + }, |
|---|
| 1161 | + { |
|---|
| 1162 | + .compatible = "renesas,r8a774b1-csi2", |
|---|
| 1163 | + .data = &rcar_csi2_info_r8a77965, |
|---|
| 1164 | + }, |
|---|
| 1165 | + { |
|---|
| 1166 | + .compatible = "renesas,r8a774c0-csi2", |
|---|
| 1167 | + .data = &rcar_csi2_info_r8a77990, |
|---|
| 1168 | + }, |
|---|
| 1169 | + { |
|---|
| 1170 | + .compatible = "renesas,r8a774e1-csi2", |
|---|
| 1171 | + .data = &rcar_csi2_info_r8a7795, |
|---|
| 1172 | + }, |
|---|
| 979 | 1173 | { |
|---|
| 980 | 1174 | .compatible = "renesas,r8a7795-csi2", |
|---|
| 981 | 1175 | .data = &rcar_csi2_info_r8a7795, |
|---|
| .. | .. |
|---|
| 992 | 1186 | .compatible = "renesas,r8a77970-csi2", |
|---|
| 993 | 1187 | .data = &rcar_csi2_info_r8a77970, |
|---|
| 994 | 1188 | }, |
|---|
| 1189 | + { |
|---|
| 1190 | + .compatible = "renesas,r8a77980-csi2", |
|---|
| 1191 | + .data = &rcar_csi2_info_r8a77980, |
|---|
| 1192 | + }, |
|---|
| 1193 | + { |
|---|
| 1194 | + .compatible = "renesas,r8a77990-csi2", |
|---|
| 1195 | + .data = &rcar_csi2_info_r8a77990, |
|---|
| 1196 | + }, |
|---|
| 995 | 1197 | { /* sentinel */ }, |
|---|
| 996 | 1198 | }; |
|---|
| 997 | 1199 | MODULE_DEVICE_TABLE(of, rcar_csi2_of_table); |
|---|
| 998 | 1200 | |
|---|
| 999 | | -static const struct soc_device_attribute r8a7795es1[] = { |
|---|
| 1201 | +static const struct soc_device_attribute r8a7795[] = { |
|---|
| 1000 | 1202 | { |
|---|
| 1001 | 1203 | .soc_id = "r8a7795", .revision = "ES1.*", |
|---|
| 1002 | 1204 | .data = &rcar_csi2_info_r8a7795es1, |
|---|
| 1205 | + }, |
|---|
| 1206 | + { |
|---|
| 1207 | + .soc_id = "r8a7795", .revision = "ES2.*", |
|---|
| 1208 | + .data = &rcar_csi2_info_r8a7795es2, |
|---|
| 1003 | 1209 | }, |
|---|
| 1004 | 1210 | { /* sentinel */ }, |
|---|
| 1005 | 1211 | }; |
|---|
| .. | .. |
|---|
| 1018 | 1224 | priv->info = of_device_get_match_data(&pdev->dev); |
|---|
| 1019 | 1225 | |
|---|
| 1020 | 1226 | /* |
|---|
| 1021 | | - * r8a7795 ES1.x behaves differently than the ES2.0+ but doesn't |
|---|
| 1022 | | - * have it's own compatible string. |
|---|
| 1227 | + * The different ES versions of r8a7795 (H3) behave differently but |
|---|
| 1228 | + * share the same compatible string. |
|---|
| 1023 | 1229 | */ |
|---|
| 1024 | | - attr = soc_device_match(r8a7795es1); |
|---|
| 1230 | + attr = soc_device_match(r8a7795); |
|---|
| 1025 | 1231 | if (attr) |
|---|
| 1026 | 1232 | priv->info = attr->data; |
|---|
| 1027 | 1233 | |
|---|