diff options
author | Dave Airlie <airlied@redhat.com> | 2018-09-26 20:54:54 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2018-09-26 20:56:11 -0400 |
commit | 2e240beefe48f011f8aaaeaae27536c0d6baa177 (patch) | |
tree | 342364c18872fe0d8a781dcf58d049504ec05af1 | |
parent | 36c9c3c91128e2b892c9be0dd9ee9bd82cbe82ad (diff) | |
parent | 122702077e4492e02de8a6257e6cb2227c617cf0 (diff) |
Merge tag 'du-next-20180925' of git://linuxtv.org/pinchartl/media into drm-next
R-Car DU support for the D3 and E3 SoCs (v4.20)
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://patchwork.freedesktop.org/patch/msgid/3289904.RCOHkcp7u8@avalon
-rw-r--r-- | Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt | 13 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/display/renesas,du.txt | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/bridge/thc63lvd1024.c | 18 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 136 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_crtc.h | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_drv.c | 63 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_drv.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_group.c | 88 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_du_kms.c | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_lvds.c | 359 | ||||
-rw-r--r-- | drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | 43 |
11 files changed, 595 insertions, 147 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt b/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt index 5a4e379bb414..3aeb0ec06fd0 100644 --- a/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt +++ b/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt | |||
@@ -15,10 +15,21 @@ Required properties: | |||
15 | - "renesas,r8a7796-lvds" for R8A7796 (R-Car M3-W) compatible LVDS encoders | 15 | - "renesas,r8a7796-lvds" for R8A7796 (R-Car M3-W) compatible LVDS encoders |
16 | - "renesas,r8a77970-lvds" for R8A77970 (R-Car V3M) compatible LVDS encoders | 16 | - "renesas,r8a77970-lvds" for R8A77970 (R-Car V3M) compatible LVDS encoders |
17 | - "renesas,r8a77980-lvds" for R8A77980 (R-Car V3H) compatible LVDS encoders | 17 | - "renesas,r8a77980-lvds" for R8A77980 (R-Car V3H) compatible LVDS encoders |
18 | - "renesas,r8a77990-lvds" for R8A77990 (R-Car E3) compatible LVDS encoders | ||
18 | - "renesas,r8a77995-lvds" for R8A77995 (R-Car D3) compatible LVDS encoders | 19 | - "renesas,r8a77995-lvds" for R8A77995 (R-Car D3) compatible LVDS encoders |
19 | 20 | ||
20 | - reg: Base address and length for the memory-mapped registers | 21 | - reg: Base address and length for the memory-mapped registers |
21 | - clocks: A phandle + clock-specifier pair for the functional clock | 22 | - clocks: A list of phandles + clock-specifier pairs, one for each entry in |
23 | the clock-names property. | ||
24 | - clock-names: Name of the clocks. This property is model-dependent. | ||
25 | - The functional clock, which mandatory for all models, shall be listed | ||
26 | first, and shall be named "fck". | ||
27 | - On R8A77990 and R8A77995, the LVDS encoder can use the EXTAL or | ||
28 | DU_DOTCLKINx clocks. Those clocks are optional. When supplied they must be | ||
29 | named "extal" and "dclkin.x" respectively, with "x" being the DU_DOTCLKIN | ||
30 | numerical index. | ||
31 | - When the clocks property only contains the functional clock, the | ||
32 | clock-names property may be omitted. | ||
22 | - resets: A phandle + reset specifier for the module reset | 33 | - resets: A phandle + reset specifier for the module reset |
23 | 34 | ||
24 | Required nodes: | 35 | Required nodes: |
diff --git a/Documentation/devicetree/bindings/display/renesas,du.txt b/Documentation/devicetree/bindings/display/renesas,du.txt index caae2348a292..9de67be632d1 100644 --- a/Documentation/devicetree/bindings/display/renesas,du.txt +++ b/Documentation/devicetree/bindings/display/renesas,du.txt | |||
@@ -16,6 +16,7 @@ Required Properties: | |||
16 | - "renesas,du-r8a77965" for R8A77965 (R-Car M3-N) compatible DU | 16 | - "renesas,du-r8a77965" for R8A77965 (R-Car M3-N) compatible DU |
17 | - "renesas,du-r8a77970" for R8A77970 (R-Car V3M) compatible DU | 17 | - "renesas,du-r8a77970" for R8A77970 (R-Car V3M) compatible DU |
18 | - "renesas,du-r8a77980" for R8A77980 (R-Car V3H) compatible DU | 18 | - "renesas,du-r8a77980" for R8A77980 (R-Car V3H) compatible DU |
19 | - "renesas,du-r8a77990" for R8A77990 (R-Car E3) compatible DU | ||
19 | - "renesas,du-r8a77995" for R8A77995 (R-Car D3) compatible DU | 20 | - "renesas,du-r8a77995" for R8A77995 (R-Car D3) compatible DU |
20 | 21 | ||
21 | - reg: the memory-mapped I/O registers base address and length | 22 | - reg: the memory-mapped I/O registers base address and length |
@@ -63,6 +64,7 @@ corresponding to each DU output. | |||
63 | R8A77965 (R-Car M3-N) DPAD 0 HDMI 0 LVDS 0 - | 64 | R8A77965 (R-Car M3-N) DPAD 0 HDMI 0 LVDS 0 - |
64 | R8A77970 (R-Car V3M) DPAD 0 LVDS 0 - - | 65 | R8A77970 (R-Car V3M) DPAD 0 LVDS 0 - - |
65 | R8A77980 (R-Car V3H) DPAD 0 LVDS 0 - - | 66 | R8A77980 (R-Car V3H) DPAD 0 LVDS 0 - - |
67 | R8A77990 (R-Car E3) DPAD 0 LVDS 0 LVDS 1 - | ||
66 | R8A77995 (R-Car D3) DPAD 0 LVDS 0 LVDS 1 - | 68 | R8A77995 (R-Car D3) DPAD 0 LVDS 0 LVDS 1 - |
67 | 69 | ||
68 | 70 | ||
diff --git a/drivers/gpu/drm/bridge/thc63lvd1024.c b/drivers/gpu/drm/bridge/thc63lvd1024.c index c8b9edd5a7f4..b083a740565c 100644 --- a/drivers/gpu/drm/bridge/thc63lvd1024.c +++ b/drivers/gpu/drm/bridge/thc63lvd1024.c | |||
@@ -45,6 +45,23 @@ static int thc63_attach(struct drm_bridge *bridge) | |||
45 | return drm_bridge_attach(bridge->encoder, thc63->next, bridge); | 45 | return drm_bridge_attach(bridge->encoder, thc63->next, bridge); |
46 | } | 46 | } |
47 | 47 | ||
48 | static enum drm_mode_status thc63_mode_valid(struct drm_bridge *bridge, | ||
49 | const struct drm_display_mode *mode) | ||
50 | { | ||
51 | /* | ||
52 | * The THC63LVD1024 clock frequency range is 8 to 135 MHz in single-in | ||
53 | * mode. Note that the limits are different in dual-in, single-out mode, | ||
54 | * and will need to be adjusted accordingly. | ||
55 | */ | ||
56 | if (mode->clock < 8000) | ||
57 | return MODE_CLOCK_LOW; | ||
58 | |||
59 | if (mode->clock > 135000) | ||
60 | return MODE_CLOCK_HIGH; | ||
61 | |||
62 | return MODE_OK; | ||
63 | } | ||
64 | |||
48 | static void thc63_enable(struct drm_bridge *bridge) | 65 | static void thc63_enable(struct drm_bridge *bridge) |
49 | { | 66 | { |
50 | struct thc63_dev *thc63 = to_thc63(bridge); | 67 | struct thc63_dev *thc63 = to_thc63(bridge); |
@@ -77,6 +94,7 @@ static void thc63_disable(struct drm_bridge *bridge) | |||
77 | 94 | ||
78 | static const struct drm_bridge_funcs thc63_bridge_func = { | 95 | static const struct drm_bridge_funcs thc63_bridge_func = { |
79 | .attach = thc63_attach, | 96 | .attach = thc63_attach, |
97 | .mode_valid = thc63_mode_valid, | ||
80 | .enable = thc63_enable, | 98 | .enable = thc63_enable, |
81 | .disable = thc63_disable, | 99 | .disable = thc63_disable, |
82 | }; | 100 | }; |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 6288b9ad9e24..17741843cf51 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c | |||
@@ -57,46 +57,12 @@ static void rcar_du_crtc_set(struct rcar_du_crtc *rcrtc, u32 reg, u32 set) | |||
57 | rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set); | 57 | rcar_du_read(rcdu, rcrtc->mmio_offset + reg) | set); |
58 | } | 58 | } |
59 | 59 | ||
60 | static void rcar_du_crtc_clr_set(struct rcar_du_crtc *rcrtc, u32 reg, | 60 | void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set) |
61 | u32 clr, u32 set) | ||
62 | { | 61 | { |
63 | struct rcar_du_device *rcdu = rcrtc->group->dev; | 62 | struct rcar_du_device *rcdu = rcrtc->group->dev; |
64 | u32 value = rcar_du_read(rcdu, rcrtc->mmio_offset + reg); | ||
65 | 63 | ||
66 | rcar_du_write(rcdu, rcrtc->mmio_offset + reg, (value & ~clr) | set); | 64 | rcrtc->dsysr = (rcrtc->dsysr & ~clr) | set; |
67 | } | 65 | rcar_du_write(rcdu, rcrtc->mmio_offset + DSYSR, rcrtc->dsysr); |
68 | |||
69 | static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc) | ||
70 | { | ||
71 | int ret; | ||
72 | |||
73 | ret = clk_prepare_enable(rcrtc->clock); | ||
74 | if (ret < 0) | ||
75 | return ret; | ||
76 | |||
77 | ret = clk_prepare_enable(rcrtc->extclock); | ||
78 | if (ret < 0) | ||
79 | goto error_clock; | ||
80 | |||
81 | ret = rcar_du_group_get(rcrtc->group); | ||
82 | if (ret < 0) | ||
83 | goto error_group; | ||
84 | |||
85 | return 0; | ||
86 | |||
87 | error_group: | ||
88 | clk_disable_unprepare(rcrtc->extclock); | ||
89 | error_clock: | ||
90 | clk_disable_unprepare(rcrtc->clock); | ||
91 | return ret; | ||
92 | } | ||
93 | |||
94 | static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) | ||
95 | { | ||
96 | rcar_du_group_put(rcrtc->group); | ||
97 | |||
98 | clk_disable_unprepare(rcrtc->extclock); | ||
99 | clk_disable_unprepare(rcrtc->clock); | ||
100 | } | 66 | } |
101 | 67 | ||
102 | /* ----------------------------------------------------------------------------- | 68 | /* ----------------------------------------------------------------------------- |
@@ -294,6 +260,14 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) | |||
294 | rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr); | 260 | rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr); |
295 | 261 | ||
296 | escr = ESCR_DCLKSEL_DCLKIN | div; | 262 | escr = ESCR_DCLKSEL_DCLKIN | div; |
263 | } else if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) { | ||
264 | /* | ||
265 | * Use the LVDS PLL output as the dot clock when outputting to | ||
266 | * the LVDS encoder on an SoC that supports this clock routing | ||
267 | * option. We use the clock directly in that case, without any | ||
268 | * additional divider. | ||
269 | */ | ||
270 | escr = ESCR_DCLKSEL_DCLKIN; | ||
297 | } else { | 271 | } else { |
298 | struct du_clk_params params = { .diff = (unsigned long)-1 }; | 272 | struct du_clk_params params = { .diff = (unsigned long)-1 }; |
299 | 273 | ||
@@ -546,6 +520,51 @@ static void rcar_du_crtc_setup(struct rcar_du_crtc *rcrtc) | |||
546 | drm_crtc_vblank_on(&rcrtc->crtc); | 520 | drm_crtc_vblank_on(&rcrtc->crtc); |
547 | } | 521 | } |
548 | 522 | ||
523 | static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc) | ||
524 | { | ||
525 | int ret; | ||
526 | |||
527 | /* | ||
528 | * Guard against double-get, as the function is called from both the | ||
529 | * .atomic_enable() and .atomic_begin() handlers. | ||
530 | */ | ||
531 | if (rcrtc->initialized) | ||
532 | return 0; | ||
533 | |||
534 | ret = clk_prepare_enable(rcrtc->clock); | ||
535 | if (ret < 0) | ||
536 | return ret; | ||
537 | |||
538 | ret = clk_prepare_enable(rcrtc->extclock); | ||
539 | if (ret < 0) | ||
540 | goto error_clock; | ||
541 | |||
542 | ret = rcar_du_group_get(rcrtc->group); | ||
543 | if (ret < 0) | ||
544 | goto error_group; | ||
545 | |||
546 | rcar_du_crtc_setup(rcrtc); | ||
547 | rcrtc->initialized = true; | ||
548 | |||
549 | return 0; | ||
550 | |||
551 | error_group: | ||
552 | clk_disable_unprepare(rcrtc->extclock); | ||
553 | error_clock: | ||
554 | clk_disable_unprepare(rcrtc->clock); | ||
555 | return ret; | ||
556 | } | ||
557 | |||
558 | static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) | ||
559 | { | ||
560 | rcar_du_group_put(rcrtc->group); | ||
561 | |||
562 | clk_disable_unprepare(rcrtc->extclock); | ||
563 | clk_disable_unprepare(rcrtc->clock); | ||
564 | |||
565 | rcrtc->initialized = false; | ||
566 | } | ||
567 | |||
549 | static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) | 568 | static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) |
550 | { | 569 | { |
551 | bool interlaced; | 570 | bool interlaced; |
@@ -556,9 +575,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) | |||
556 | * actively driven). | 575 | * actively driven). |
557 | */ | 576 | */ |
558 | interlaced = rcrtc->crtc.mode.flags & DRM_MODE_FLAG_INTERLACE; | 577 | interlaced = rcrtc->crtc.mode.flags & DRM_MODE_FLAG_INTERLACE; |
559 | rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK | DSYSR_SCM_MASK, | 578 | rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK | DSYSR_SCM_MASK, |
560 | (interlaced ? DSYSR_SCM_INT_VIDEO : 0) | | 579 | (interlaced ? DSYSR_SCM_INT_VIDEO : 0) | |
561 | DSYSR_TVM_MASTER); | 580 | DSYSR_TVM_MASTER); |
562 | 581 | ||
563 | rcar_du_group_start_stop(rcrtc->group, true); | 582 | rcar_du_group_start_stop(rcrtc->group, true); |
564 | } | 583 | } |
@@ -624,8 +643,13 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc) | |||
624 | /* | 643 | /* |
625 | * Select switch sync mode. This stops display operation and configures | 644 | * Select switch sync mode. This stops display operation and configures |
626 | * the HSYNC and VSYNC signals as inputs. | 645 | * the HSYNC and VSYNC signals as inputs. |
646 | * | ||
647 | * TODO: Find another way to stop the display for DUs that don't support | ||
648 | * TVM sync. | ||
627 | */ | 649 | */ |
628 | rcar_du_crtc_clr_set(rcrtc, DSYSR, DSYSR_TVM_MASK, DSYSR_TVM_SWITCH); | 650 | if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_TVM_SYNC)) |
651 | rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_TVM_MASK, | ||
652 | DSYSR_TVM_SWITCH); | ||
629 | 653 | ||
630 | rcar_du_group_start_stop(rcrtc->group, false); | 654 | rcar_du_group_start_stop(rcrtc->group, false); |
631 | } | 655 | } |
@@ -639,16 +663,7 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc, | |||
639 | { | 663 | { |
640 | struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); | 664 | struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); |
641 | 665 | ||
642 | /* | 666 | rcar_du_crtc_get(rcrtc); |
643 | * If the CRTC has already been setup by the .atomic_begin() handler we | ||
644 | * can skip the setup stage. | ||
645 | */ | ||
646 | if (!rcrtc->initialized) { | ||
647 | rcar_du_crtc_get(rcrtc); | ||
648 | rcar_du_crtc_setup(rcrtc); | ||
649 | rcrtc->initialized = true; | ||
650 | } | ||
651 | |||
652 | rcar_du_crtc_start(rcrtc); | 667 | rcar_du_crtc_start(rcrtc); |
653 | } | 668 | } |
654 | 669 | ||
@@ -667,7 +682,6 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc, | |||
667 | } | 682 | } |
668 | spin_unlock_irq(&crtc->dev->event_lock); | 683 | spin_unlock_irq(&crtc->dev->event_lock); |
669 | 684 | ||
670 | rcrtc->initialized = false; | ||
671 | rcrtc->outputs = 0; | 685 | rcrtc->outputs = 0; |
672 | } | 686 | } |
673 | 687 | ||
@@ -680,14 +694,17 @@ static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc, | |||
680 | 694 | ||
681 | /* | 695 | /* |
682 | * If a mode set is in progress we can be called with the CRTC disabled. | 696 | * If a mode set is in progress we can be called with the CRTC disabled. |
683 | * We then need to first setup the CRTC in order to configure planes. | 697 | * We thus need to first get and setup the CRTC in order to configure |
684 | * The .atomic_enable() handler will notice and skip the CRTC setup. | 698 | * planes. We must *not* put the CRTC in .atomic_flush(), as it must be |
699 | * kept awake until the .atomic_enable() call that will follow. The get | ||
700 | * operation in .atomic_enable() will in that case be a no-op, and the | ||
701 | * CRTC will be put later in .atomic_disable(). | ||
702 | * | ||
703 | * If a mode set is not in progress the CRTC is enabled, and the | ||
704 | * following get call will be a no-op. There is thus no need to belance | ||
705 | * it in .atomic_flush() either. | ||
685 | */ | 706 | */ |
686 | if (!rcrtc->initialized) { | 707 | rcar_du_crtc_get(rcrtc); |
687 | rcar_du_crtc_get(rcrtc); | ||
688 | rcar_du_crtc_setup(rcrtc); | ||
689 | rcrtc->initialized = true; | ||
690 | } | ||
691 | 708 | ||
692 | if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) | 709 | if (rcar_du_has(rcrtc->group->dev, RCAR_DU_FEATURE_VSP1_SOURCE)) |
693 | rcar_du_vsp_atomic_begin(rcrtc); | 710 | rcar_du_vsp_atomic_begin(rcrtc); |
@@ -1108,6 +1125,7 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int swindex, | |||
1108 | rcrtc->group = rgrp; | 1125 | rcrtc->group = rgrp; |
1109 | rcrtc->mmio_offset = mmio_offsets[hwindex]; | 1126 | rcrtc->mmio_offset = mmio_offsets[hwindex]; |
1110 | rcrtc->index = hwindex; | 1127 | rcrtc->index = hwindex; |
1128 | rcrtc->dsysr = (rcrtc->index % 2 ? 0 : DSYSR_DRES) | DSYSR_TVM_TVSYNC; | ||
1111 | 1129 | ||
1112 | if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) | 1130 | if (rcar_du_has(rcdu, RCAR_DU_FEATURE_VSP1_SOURCE)) |
1113 | primary = &rcrtc->vsp->planes[rcrtc->vsp_pipe].plane; | 1131 | primary = &rcrtc->vsp->planes[rcrtc->vsp_pipe].plane; |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h index 4990bbe9ba26..59ac6e7d22c9 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h | |||
@@ -30,6 +30,7 @@ struct rcar_du_vsp; | |||
30 | * @mmio_offset: offset of the CRTC registers in the DU MMIO block | 30 | * @mmio_offset: offset of the CRTC registers in the DU MMIO block |
31 | * @index: CRTC software and hardware index | 31 | * @index: CRTC software and hardware index |
32 | * @initialized: whether the CRTC has been initialized and clocks enabled | 32 | * @initialized: whether the CRTC has been initialized and clocks enabled |
33 | * @dsysr: cached value of the DSYSR register | ||
33 | * @vblank_enable: whether vblank events are enabled on this CRTC | 34 | * @vblank_enable: whether vblank events are enabled on this CRTC |
34 | * @event: event to post when the pending page flip completes | 35 | * @event: event to post when the pending page flip completes |
35 | * @flip_wait: wait queue used to signal page flip completion | 36 | * @flip_wait: wait queue used to signal page flip completion |
@@ -50,6 +51,8 @@ struct rcar_du_crtc { | |||
50 | unsigned int index; | 51 | unsigned int index; |
51 | bool initialized; | 52 | bool initialized; |
52 | 53 | ||
54 | u32 dsysr; | ||
55 | |||
53 | bool vblank_enable; | 56 | bool vblank_enable; |
54 | struct drm_pending_vblank_event *event; | 57 | struct drm_pending_vblank_event *event; |
55 | wait_queue_head_t flip_wait; | 58 | wait_queue_head_t flip_wait; |
@@ -103,4 +106,6 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc, | |||
103 | enum rcar_du_output output); | 106 | enum rcar_du_output output); |
104 | void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc); | 107 | void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc); |
105 | 108 | ||
109 | void rcar_du_crtc_dsysr_clr_set(struct rcar_du_crtc *rcrtc, u32 clr, u32 set); | ||
110 | |||
106 | #endif /* __RCAR_DU_CRTC_H__ */ | 111 | #endif /* __RCAR_DU_CRTC_H__ */ |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index 0954ecd2f943..084f58df4a8c 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c | |||
@@ -36,7 +36,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7743_info = { | |||
36 | .gen = 2, | 36 | .gen = 2, |
37 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | 37 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK |
38 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | 38 | | RCAR_DU_FEATURE_EXT_CTRL_REGS |
39 | | RCAR_DU_FEATURE_INTERLACED, | 39 | | RCAR_DU_FEATURE_INTERLACED |
40 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
40 | .channels_mask = BIT(1) | BIT(0), | 41 | .channels_mask = BIT(1) | BIT(0), |
41 | .routes = { | 42 | .routes = { |
42 | /* | 43 | /* |
@@ -58,7 +59,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = { | |||
58 | .gen = 2, | 59 | .gen = 2, |
59 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | 60 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK |
60 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | 61 | | RCAR_DU_FEATURE_EXT_CTRL_REGS |
61 | | RCAR_DU_FEATURE_INTERLACED, | 62 | | RCAR_DU_FEATURE_INTERLACED |
63 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
62 | .channels_mask = BIT(1) | BIT(0), | 64 | .channels_mask = BIT(1) | BIT(0), |
63 | .routes = { | 65 | .routes = { |
64 | /* | 66 | /* |
@@ -77,7 +79,8 @@ static const struct rcar_du_device_info rzg1_du_r8a7745_info = { | |||
77 | 79 | ||
78 | static const struct rcar_du_device_info rcar_du_r8a7779_info = { | 80 | static const struct rcar_du_device_info rcar_du_r8a7779_info = { |
79 | .gen = 2, | 81 | .gen = 2, |
80 | .features = RCAR_DU_FEATURE_INTERLACED, | 82 | .features = RCAR_DU_FEATURE_INTERLACED |
83 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
81 | .channels_mask = BIT(1) | BIT(0), | 84 | .channels_mask = BIT(1) | BIT(0), |
82 | .routes = { | 85 | .routes = { |
83 | /* | 86 | /* |
@@ -99,7 +102,8 @@ static const struct rcar_du_device_info rcar_du_r8a7790_info = { | |||
99 | .gen = 2, | 102 | .gen = 2, |
100 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | 103 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK |
101 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | 104 | | RCAR_DU_FEATURE_EXT_CTRL_REGS |
102 | | RCAR_DU_FEATURE_INTERLACED, | 105 | | RCAR_DU_FEATURE_INTERLACED |
106 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
103 | .quirks = RCAR_DU_QUIRK_ALIGN_128B, | 107 | .quirks = RCAR_DU_QUIRK_ALIGN_128B, |
104 | .channels_mask = BIT(2) | BIT(1) | BIT(0), | 108 | .channels_mask = BIT(2) | BIT(1) | BIT(0), |
105 | .routes = { | 109 | .routes = { |
@@ -128,7 +132,8 @@ static const struct rcar_du_device_info rcar_du_r8a7791_info = { | |||
128 | .gen = 2, | 132 | .gen = 2, |
129 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | 133 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK |
130 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | 134 | | RCAR_DU_FEATURE_EXT_CTRL_REGS |
131 | | RCAR_DU_FEATURE_INTERLACED, | 135 | | RCAR_DU_FEATURE_INTERLACED |
136 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
132 | .channels_mask = BIT(1) | BIT(0), | 137 | .channels_mask = BIT(1) | BIT(0), |
133 | .routes = { | 138 | .routes = { |
134 | /* | 139 | /* |
@@ -151,7 +156,8 @@ static const struct rcar_du_device_info rcar_du_r8a7792_info = { | |||
151 | .gen = 2, | 156 | .gen = 2, |
152 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | 157 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK |
153 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | 158 | | RCAR_DU_FEATURE_EXT_CTRL_REGS |
154 | | RCAR_DU_FEATURE_INTERLACED, | 159 | | RCAR_DU_FEATURE_INTERLACED |
160 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
155 | .channels_mask = BIT(1) | BIT(0), | 161 | .channels_mask = BIT(1) | BIT(0), |
156 | .routes = { | 162 | .routes = { |
157 | /* R8A7792 has two RGB outputs. */ | 163 | /* R8A7792 has two RGB outputs. */ |
@@ -170,7 +176,8 @@ static const struct rcar_du_device_info rcar_du_r8a7794_info = { | |||
170 | .gen = 2, | 176 | .gen = 2, |
171 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | 177 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK |
172 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | 178 | | RCAR_DU_FEATURE_EXT_CTRL_REGS |
173 | | RCAR_DU_FEATURE_INTERLACED, | 179 | | RCAR_DU_FEATURE_INTERLACED |
180 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
174 | .channels_mask = BIT(1) | BIT(0), | 181 | .channels_mask = BIT(1) | BIT(0), |
175 | .routes = { | 182 | .routes = { |
176 | /* | 183 | /* |
@@ -193,7 +200,8 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = { | |||
193 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | 200 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK |
194 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | 201 | | RCAR_DU_FEATURE_EXT_CTRL_REGS |
195 | | RCAR_DU_FEATURE_VSP1_SOURCE | 202 | | RCAR_DU_FEATURE_VSP1_SOURCE |
196 | | RCAR_DU_FEATURE_INTERLACED, | 203 | | RCAR_DU_FEATURE_INTERLACED |
204 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
197 | .channels_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0), | 205 | .channels_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0), |
198 | .routes = { | 206 | .routes = { |
199 | /* | 207 | /* |
@@ -226,7 +234,8 @@ static const struct rcar_du_device_info rcar_du_r8a7796_info = { | |||
226 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | 234 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK |
227 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | 235 | | RCAR_DU_FEATURE_EXT_CTRL_REGS |
228 | | RCAR_DU_FEATURE_VSP1_SOURCE | 236 | | RCAR_DU_FEATURE_VSP1_SOURCE |
229 | | RCAR_DU_FEATURE_INTERLACED, | 237 | | RCAR_DU_FEATURE_INTERLACED |
238 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
230 | .channels_mask = BIT(2) | BIT(1) | BIT(0), | 239 | .channels_mask = BIT(2) | BIT(1) | BIT(0), |
231 | .routes = { | 240 | .routes = { |
232 | /* | 241 | /* |
@@ -255,7 +264,8 @@ static const struct rcar_du_device_info rcar_du_r8a77965_info = { | |||
255 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | 264 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK |
256 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | 265 | | RCAR_DU_FEATURE_EXT_CTRL_REGS |
257 | | RCAR_DU_FEATURE_VSP1_SOURCE | 266 | | RCAR_DU_FEATURE_VSP1_SOURCE |
258 | | RCAR_DU_FEATURE_INTERLACED, | 267 | | RCAR_DU_FEATURE_INTERLACED |
268 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
259 | .channels_mask = BIT(3) | BIT(1) | BIT(0), | 269 | .channels_mask = BIT(3) | BIT(1) | BIT(0), |
260 | .routes = { | 270 | .routes = { |
261 | /* | 271 | /* |
@@ -284,7 +294,8 @@ static const struct rcar_du_device_info rcar_du_r8a77970_info = { | |||
284 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | 294 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK |
285 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | 295 | | RCAR_DU_FEATURE_EXT_CTRL_REGS |
286 | | RCAR_DU_FEATURE_VSP1_SOURCE | 296 | | RCAR_DU_FEATURE_VSP1_SOURCE |
287 | | RCAR_DU_FEATURE_INTERLACED, | 297 | | RCAR_DU_FEATURE_INTERLACED |
298 | | RCAR_DU_FEATURE_TVM_SYNC, | ||
288 | .channels_mask = BIT(0), | 299 | .channels_mask = BIT(0), |
289 | .routes = { | 300 | .routes = { |
290 | /* R8A77970 has one RGB output and one LVDS output. */ | 301 | /* R8A77970 has one RGB output and one LVDS output. */ |
@@ -300,6 +311,34 @@ static const struct rcar_du_device_info rcar_du_r8a77970_info = { | |||
300 | .num_lvds = 1, | 311 | .num_lvds = 1, |
301 | }; | 312 | }; |
302 | 313 | ||
314 | static const struct rcar_du_device_info rcar_du_r8a7799x_info = { | ||
315 | .gen = 3, | ||
316 | .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK | ||
317 | | RCAR_DU_FEATURE_EXT_CTRL_REGS | ||
318 | | RCAR_DU_FEATURE_VSP1_SOURCE, | ||
319 | .channels_mask = BIT(1) | BIT(0), | ||
320 | .routes = { | ||
321 | /* | ||
322 | * R8A77990 and R8A77995 have one RGB output and two LVDS | ||
323 | * outputs. | ||
324 | */ | ||
325 | [RCAR_DU_OUTPUT_DPAD0] = { | ||
326 | .possible_crtcs = BIT(0) | BIT(1), | ||
327 | .port = 0, | ||
328 | }, | ||
329 | [RCAR_DU_OUTPUT_LVDS0] = { | ||
330 | .possible_crtcs = BIT(0), | ||
331 | .port = 1, | ||
332 | }, | ||
333 | [RCAR_DU_OUTPUT_LVDS1] = { | ||
334 | .possible_crtcs = BIT(1), | ||
335 | .port = 2, | ||
336 | }, | ||
337 | }, | ||
338 | .num_lvds = 2, | ||
339 | .lvds_clk_mask = BIT(1) | BIT(0), | ||
340 | }; | ||
341 | |||
303 | static const struct of_device_id rcar_du_of_table[] = { | 342 | static const struct of_device_id rcar_du_of_table[] = { |
304 | { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info }, | 343 | { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info }, |
305 | { .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info }, | 344 | { .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info }, |
@@ -313,6 +352,8 @@ static const struct of_device_id rcar_du_of_table[] = { | |||
313 | { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info }, | 352 | { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info }, |
314 | { .compatible = "renesas,du-r8a77965", .data = &rcar_du_r8a77965_info }, | 353 | { .compatible = "renesas,du-r8a77965", .data = &rcar_du_r8a77965_info }, |
315 | { .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info }, | 354 | { .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info }, |
355 | { .compatible = "renesas,du-r8a77990", .data = &rcar_du_r8a7799x_info }, | ||
356 | { .compatible = "renesas,du-r8a77995", .data = &rcar_du_r8a7799x_info }, | ||
316 | { } | 357 | { } |
317 | }; | 358 | }; |
318 | 359 | ||
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index fef9ea5c22f3..143c037e2c0f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h | |||
@@ -27,6 +27,7 @@ struct rcar_du_device; | |||
27 | #define RCAR_DU_FEATURE_EXT_CTRL_REGS BIT(1) /* Has extended control registers */ | 27 | #define RCAR_DU_FEATURE_EXT_CTRL_REGS BIT(1) /* Has extended control registers */ |
28 | #define RCAR_DU_FEATURE_VSP1_SOURCE BIT(2) /* Has inputs from VSP1 */ | 28 | #define RCAR_DU_FEATURE_VSP1_SOURCE BIT(2) /* Has inputs from VSP1 */ |
29 | #define RCAR_DU_FEATURE_INTERLACED BIT(3) /* HW supports interlaced */ | 29 | #define RCAR_DU_FEATURE_INTERLACED BIT(3) /* HW supports interlaced */ |
30 | #define RCAR_DU_FEATURE_TVM_SYNC BIT(4) /* Has TV switch/sync modes */ | ||
30 | 31 | ||
31 | #define RCAR_DU_QUIRK_ALIGN_128B BIT(0) /* Align pitches to 128 bytes */ | 32 | #define RCAR_DU_QUIRK_ALIGN_128B BIT(0) /* Align pitches to 128 bytes */ |
32 | 33 | ||
@@ -53,6 +54,7 @@ struct rcar_du_output_routing { | |||
53 | * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*) | 54 | * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*) |
54 | * @num_lvds: number of internal LVDS encoders | 55 | * @num_lvds: number of internal LVDS encoders |
55 | * @dpll_mask: bit mask of DU channels equipped with a DPLL | 56 | * @dpll_mask: bit mask of DU channels equipped with a DPLL |
57 | * @lvds_clk_mask: bitmask of channels that can use the LVDS clock as dot clock | ||
56 | */ | 58 | */ |
57 | struct rcar_du_device_info { | 59 | struct rcar_du_device_info { |
58 | unsigned int gen; | 60 | unsigned int gen; |
@@ -62,6 +64,7 @@ struct rcar_du_device_info { | |||
62 | struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX]; | 64 | struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX]; |
63 | unsigned int num_lvds; | 65 | unsigned int num_lvds; |
64 | unsigned int dpll_mask; | 66 | unsigned int dpll_mask; |
67 | unsigned int lvds_clk_mask; | ||
65 | }; | 68 | }; |
66 | 69 | ||
67 | #define RCAR_DU_MAX_CRTCS 4 | 70 | #define RCAR_DU_MAX_CRTCS 4 |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c index ef2c177afb6d..d85f0a1c1581 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_group.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c | |||
@@ -56,8 +56,6 @@ static void rcar_du_group_setup_pins(struct rcar_du_group *rgrp) | |||
56 | static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp) | 56 | static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp) |
57 | { | 57 | { |
58 | struct rcar_du_device *rcdu = rgrp->dev; | 58 | struct rcar_du_device *rcdu = rgrp->dev; |
59 | unsigned int possible_crtcs = | ||
60 | rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs; | ||
61 | u32 defr8 = DEFR8_CODE; | 59 | u32 defr8 = DEFR8_CODE; |
62 | 60 | ||
63 | if (rcdu->info->gen < 3) { | 61 | if (rcdu->info->gen < 3) { |
@@ -69,26 +67,71 @@ static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp) | |||
69 | * DU instances that support it. | 67 | * DU instances that support it. |
70 | */ | 68 | */ |
71 | if (rgrp->index == 0) { | 69 | if (rgrp->index == 0) { |
72 | if (possible_crtcs > 1) | 70 | defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source); |
73 | defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source); | ||
74 | if (rgrp->dev->vspd1_sink == 2) | 71 | if (rgrp->dev->vspd1_sink == 2) |
75 | defr8 |= DEFR8_VSCS; | 72 | defr8 |= DEFR8_VSCS; |
76 | } | 73 | } |
77 | } else { | 74 | } else { |
78 | /* | 75 | /* |
79 | * On Gen3 VSPD routing can't be configured, but DPAD routing | 76 | * On Gen3 VSPD routing can't be configured, and DPAD routing |
80 | * needs to be set despite having a single option available. | 77 | * is set in the group corresponding to the DPAD output (no Gen3 |
78 | * SoC has multiple DPAD sources belonging to separate groups). | ||
81 | */ | 79 | */ |
82 | unsigned int rgb_crtc = ffs(possible_crtcs) - 1; | 80 | if (rgrp->index == rcdu->dpad0_source / 2) |
83 | struct rcar_du_crtc *crtc = &rcdu->crtcs[rgb_crtc]; | 81 | defr8 |= DEFR8_DRGBS_DU(rcdu->dpad0_source); |
84 | |||
85 | if (crtc->index / 2 == rgrp->index) | ||
86 | defr8 |= DEFR8_DRGBS_DU(crtc->index); | ||
87 | } | 82 | } |
88 | 83 | ||
89 | rcar_du_group_write(rgrp, DEFR8, defr8); | 84 | rcar_du_group_write(rgrp, DEFR8, defr8); |
90 | } | 85 | } |
91 | 86 | ||
87 | static void rcar_du_group_setup_didsr(struct rcar_du_group *rgrp) | ||
88 | { | ||
89 | struct rcar_du_device *rcdu = rgrp->dev; | ||
90 | struct rcar_du_crtc *rcrtc; | ||
91 | unsigned int num_crtcs = 0; | ||
92 | unsigned int i; | ||
93 | u32 didsr; | ||
94 | |||
95 | /* | ||
96 | * Configure input dot clock routing with a hardcoded configuration. If | ||
97 | * the DU channel can use the LVDS encoder output clock as the dot | ||
98 | * clock, do so. Otherwise route DU_DOTCLKINn signal to DUn. | ||
99 | * | ||
100 | * Each channel can then select between the dot clock configured here | ||
101 | * and the clock provided by the CPG through the ESCR register. | ||
102 | */ | ||
103 | if (rcdu->info->gen < 3 && rgrp->index == 0) { | ||
104 | /* | ||
105 | * On Gen2 a single register in the first group controls dot | ||
106 | * clock selection for all channels. | ||
107 | */ | ||
108 | rcrtc = rcdu->crtcs; | ||
109 | num_crtcs = rcdu->num_crtcs; | ||
110 | } else if (rcdu->info->gen == 3 && rgrp->num_crtcs > 1) { | ||
111 | /* | ||
112 | * On Gen3 dot clocks are setup through per-group registers, | ||
113 | * only available when the group has two channels. | ||
114 | */ | ||
115 | rcrtc = &rcdu->crtcs[rgrp->index * 2]; | ||
116 | num_crtcs = rgrp->num_crtcs; | ||
117 | } | ||
118 | |||
119 | if (!num_crtcs) | ||
120 | return; | ||
121 | |||
122 | didsr = DIDSR_CODE; | ||
123 | for (i = 0; i < num_crtcs; ++i, ++rcrtc) { | ||
124 | if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) | ||
125 | didsr |= DIDSR_LCDS_LVDS0(i) | ||
126 | | DIDSR_PDCS_CLK(i, 0); | ||
127 | else | ||
128 | didsr |= DIDSR_LCDS_DCLKIN(i) | ||
129 | | DIDSR_PDCS_CLK(i, 0); | ||
130 | } | ||
131 | |||
132 | rcar_du_group_write(rgrp, DIDSR, didsr); | ||
133 | } | ||
134 | |||
92 | static void rcar_du_group_setup(struct rcar_du_group *rgrp) | 135 | static void rcar_du_group_setup(struct rcar_du_group *rgrp) |
93 | { | 136 | { |
94 | struct rcar_du_device *rcdu = rgrp->dev; | 137 | struct rcar_du_device *rcdu = rgrp->dev; |
@@ -106,21 +149,7 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp) | |||
106 | 149 | ||
107 | if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) { | 150 | if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) { |
108 | rcar_du_group_setup_defr8(rgrp); | 151 | rcar_du_group_setup_defr8(rgrp); |
109 | 152 | rcar_du_group_setup_didsr(rgrp); | |
110 | /* | ||
111 | * Configure input dot clock routing. We currently hardcode the | ||
112 | * configuration to routing DOTCLKINn to DUn. Register fields | ||
113 | * depend on the DU generation, but the resulting value is 0 in | ||
114 | * all cases. | ||
115 | * | ||
116 | * On Gen2 a single register in the first group controls dot | ||
117 | * clock selection for all channels, while on Gen3 dot clocks | ||
118 | * are setup through per-group registers, only available when | ||
119 | * the group has two channels. | ||
120 | */ | ||
121 | if ((rcdu->info->gen < 3 && rgrp->index == 0) || | ||
122 | (rcdu->info->gen == 3 && rgrp->num_crtcs > 1)) | ||
123 | rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE); | ||
124 | } | 153 | } |
125 | 154 | ||
126 | if (rcdu->info->gen >= 3) | 155 | if (rcdu->info->gen >= 3) |
@@ -173,9 +202,10 @@ void rcar_du_group_put(struct rcar_du_group *rgrp) | |||
173 | 202 | ||
174 | static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) | 203 | static void __rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) |
175 | { | 204 | { |
176 | rcar_du_group_write(rgrp, DSYSR, | 205 | struct rcar_du_crtc *rcrtc = &rgrp->dev->crtcs[rgrp->index * 2]; |
177 | (rcar_du_group_read(rgrp, DSYSR) & ~(DSYSR_DRES | DSYSR_DEN)) | | 206 | |
178 | (start ? DSYSR_DEN : DSYSR_DRES)); | 207 | rcar_du_crtc_dsysr_clr_set(rcrtc, DSYSR_DRES | DSYSR_DEN, |
208 | start ? DSYSR_DEN : DSYSR_DRES); | ||
179 | } | 209 | } |
180 | 210 | ||
181 | void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) | 211 | void rcar_du_group_start_stop(struct rcar_du_group *rgrp, bool start) |
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index b5d79ecd25ea..4ebd61ecbee1 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c | |||
@@ -544,6 +544,7 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) | |||
544 | struct drm_device *dev = rcdu->ddev; | 544 | struct drm_device *dev = rcdu->ddev; |
545 | struct drm_encoder *encoder; | 545 | struct drm_encoder *encoder; |
546 | struct drm_fbdev_cma *fbdev; | 546 | struct drm_fbdev_cma *fbdev; |
547 | unsigned int dpad0_sources; | ||
547 | unsigned int num_encoders; | 548 | unsigned int num_encoders; |
548 | unsigned int num_groups; | 549 | unsigned int num_groups; |
549 | unsigned int swindex; | 550 | unsigned int swindex; |
@@ -666,6 +667,17 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu) | |||
666 | encoder->possible_clones = (1 << num_encoders) - 1; | 667 | encoder->possible_clones = (1 << num_encoders) - 1; |
667 | } | 668 | } |
668 | 669 | ||
670 | /* | ||
671 | * Initialize the default DPAD0 source to the index of the first DU | ||
672 | * channel that can be connected to DPAD0. The exact value doesn't | ||
673 | * matter as it should be overwritten by mode setting for the RGB | ||
674 | * output, but it is nonetheless required to ensure a valid initial | ||
675 | * hardware configuration on Gen3 where DU0 can't always be connected to | ||
676 | * DPAD0. | ||
677 | */ | ||
678 | dpad0_sources = rcdu->info->routes[RCAR_DU_OUTPUT_DPAD0].possible_crtcs; | ||
679 | rcdu->dpad0_source = ffs(dpad0_sources) - 1; | ||
680 | |||
669 | drm_mode_config_reset(dev); | 681 | drm_mode_config_reset(dev); |
670 | 682 | ||
671 | drm_kms_helper_poll_init(dev); | 683 | drm_kms_helper_poll_init(dev); |
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index ce0eb68c3416..173d7ad0b991 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c | |||
@@ -24,6 +24,8 @@ | |||
24 | 24 | ||
25 | #include "rcar_lvds_regs.h" | 25 | #include "rcar_lvds_regs.h" |
26 | 26 | ||
27 | struct rcar_lvds; | ||
28 | |||
27 | /* Keep in sync with the LVDCR0.LVMD hardware register values. */ | 29 | /* Keep in sync with the LVDCR0.LVMD hardware register values. */ |
28 | enum rcar_lvds_mode { | 30 | enum rcar_lvds_mode { |
29 | RCAR_LVDS_MODE_JEIDA = 0, | 31 | RCAR_LVDS_MODE_JEIDA = 0, |
@@ -31,14 +33,16 @@ enum rcar_lvds_mode { | |||
31 | RCAR_LVDS_MODE_VESA = 4, | 33 | RCAR_LVDS_MODE_VESA = 4, |
32 | }; | 34 | }; |
33 | 35 | ||
34 | #define RCAR_LVDS_QUIRK_LANES (1 << 0) /* LVDS lanes 1 and 3 inverted */ | 36 | #define RCAR_LVDS_QUIRK_LANES BIT(0) /* LVDS lanes 1 and 3 inverted */ |
35 | #define RCAR_LVDS_QUIRK_GEN2_PLLCR (1 << 1) /* LVDPLLCR has gen2 layout */ | 37 | #define RCAR_LVDS_QUIRK_GEN3_LVEN BIT(1) /* LVEN bit needs to be set on R8A77970/R8A7799x */ |
36 | #define RCAR_LVDS_QUIRK_GEN3_LVEN (1 << 2) /* LVEN bit needs to be set */ | 38 | #define RCAR_LVDS_QUIRK_PWD BIT(2) /* PWD bit available (all of Gen3 but E3) */ |
37 | /* on R8A77970/R8A7799x */ | 39 | #define RCAR_LVDS_QUIRK_EXT_PLL BIT(3) /* Has extended PLL */ |
40 | #define RCAR_LVDS_QUIRK_DUAL_LINK BIT(4) /* Supports dual-link operation */ | ||
38 | 41 | ||
39 | struct rcar_lvds_device_info { | 42 | struct rcar_lvds_device_info { |
40 | unsigned int gen; | 43 | unsigned int gen; |
41 | unsigned int quirks; | 44 | unsigned int quirks; |
45 | void (*pll_setup)(struct rcar_lvds *lvds, unsigned int freq); | ||
42 | }; | 46 | }; |
43 | 47 | ||
44 | struct rcar_lvds { | 48 | struct rcar_lvds { |
@@ -52,7 +56,11 @@ struct rcar_lvds { | |||
52 | struct drm_panel *panel; | 56 | struct drm_panel *panel; |
53 | 57 | ||
54 | void __iomem *mmio; | 58 | void __iomem *mmio; |
55 | struct clk *clock; | 59 | struct { |
60 | struct clk *mod; /* CPG module clock */ | ||
61 | struct clk *extal; /* External clock */ | ||
62 | struct clk *dotclkin[2]; /* External DU clocks */ | ||
63 | } clocks; | ||
56 | bool enabled; | 64 | bool enabled; |
57 | 65 | ||
58 | struct drm_display_mode display_mode; | 66 | struct drm_display_mode display_mode; |
@@ -128,33 +136,216 @@ static const struct drm_connector_funcs rcar_lvds_conn_funcs = { | |||
128 | }; | 136 | }; |
129 | 137 | ||
130 | /* ----------------------------------------------------------------------------- | 138 | /* ----------------------------------------------------------------------------- |
131 | * Bridge | 139 | * PLL Setup |
132 | */ | 140 | */ |
133 | 141 | ||
134 | static u32 rcar_lvds_lvdpllcr_gen2(unsigned int freq) | 142 | static void rcar_lvds_pll_setup_gen2(struct rcar_lvds *lvds, unsigned int freq) |
135 | { | 143 | { |
136 | if (freq < 39000) | 144 | u32 val; |
137 | return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; | 145 | |
138 | else if (freq < 61000) | 146 | if (freq < 39000000) |
139 | return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; | 147 | val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_38M; |
140 | else if (freq < 121000) | 148 | else if (freq < 61000000) |
141 | return LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; | 149 | val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_60M; |
150 | else if (freq < 121000000) | ||
151 | val = LVDPLLCR_CEEN | LVDPLLCR_COSEL | LVDPLLCR_PLLDLYCNT_121M; | ||
142 | else | 152 | else |
143 | return LVDPLLCR_PLLDLYCNT_150M; | 153 | val = LVDPLLCR_PLLDLYCNT_150M; |
154 | |||
155 | rcar_lvds_write(lvds, LVDPLLCR, val); | ||
144 | } | 156 | } |
145 | 157 | ||
146 | static u32 rcar_lvds_lvdpllcr_gen3(unsigned int freq) | 158 | static void rcar_lvds_pll_setup_gen3(struct rcar_lvds *lvds, unsigned int freq) |
147 | { | 159 | { |
148 | if (freq < 42000) | 160 | u32 val; |
149 | return LVDPLLCR_PLLDIVCNT_42M; | 161 | |
150 | else if (freq < 85000) | 162 | if (freq < 42000000) |
151 | return LVDPLLCR_PLLDIVCNT_85M; | 163 | val = LVDPLLCR_PLLDIVCNT_42M; |
152 | else if (freq < 128000) | 164 | else if (freq < 85000000) |
153 | return LVDPLLCR_PLLDIVCNT_128M; | 165 | val = LVDPLLCR_PLLDIVCNT_85M; |
166 | else if (freq < 128000000) | ||
167 | val = LVDPLLCR_PLLDIVCNT_128M; | ||
154 | else | 168 | else |
155 | return LVDPLLCR_PLLDIVCNT_148M; | 169 | val = LVDPLLCR_PLLDIVCNT_148M; |
170 | |||
171 | rcar_lvds_write(lvds, LVDPLLCR, val); | ||
156 | } | 172 | } |
157 | 173 | ||
174 | struct pll_info { | ||
175 | unsigned long diff; | ||
176 | unsigned int pll_m; | ||
177 | unsigned int pll_n; | ||
178 | unsigned int pll_e; | ||
179 | unsigned int div; | ||
180 | u32 clksel; | ||
181 | }; | ||
182 | |||
183 | static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, | ||
184 | unsigned long target, struct pll_info *pll, | ||
185 | u32 clksel) | ||
186 | { | ||
187 | unsigned long output; | ||
188 | unsigned long fin; | ||
189 | unsigned int m_min; | ||
190 | unsigned int m_max; | ||
191 | unsigned int m; | ||
192 | int error; | ||
193 | |||
194 | if (!clk) | ||
195 | return; | ||
196 | |||
197 | /* | ||
198 | * The LVDS PLL is made of a pre-divider and a multiplier (strangely | ||
199 | * enough called M and N respectively), followed by a post-divider E. | ||
200 | * | ||
201 | * ,-----. ,-----. ,-----. ,-----. | ||
202 | * Fin --> | 1/M | -Fpdf-> | PFD | --> | VCO | -Fvco-> | 1/E | --> Fout | ||
203 | * `-----' ,-> | | `-----' | `-----' | ||
204 | * | `-----' | | ||
205 | * | ,-----. | | ||
206 | * `-------- | 1/N | <-------' | ||
207 | * `-----' | ||
208 | * | ||
209 | * The clock output by the PLL is then further divided by a programmable | ||
210 | * divider DIV to achieve the desired target frequency. Finally, an | ||
211 | * optional fixed /7 divider is used to convert the bit clock to a pixel | ||
212 | * clock (as LVDS transmits 7 bits per lane per clock sample). | ||
213 | * | ||
214 | * ,-------. ,-----. |\ | ||
215 | * Fout --> | 1/DIV | --> | 1/7 | --> | | | ||
216 | * `-------' | `-----' | | --> dot clock | ||
217 | * `------------> | | | ||
218 | * |/ | ||
219 | * | ||
220 | * The /7 divider is optional when the LVDS PLL is used to generate a | ||
221 | * dot clock for the DU RGB output, without using the LVDS encoder. We | ||
222 | * don't support this configuration yet. | ||
223 | * | ||
224 | * The PLL allowed input frequency range is 12 MHz to 192 MHz. | ||
225 | */ | ||
226 | |||
227 | fin = clk_get_rate(clk); | ||
228 | if (fin < 12000000 || fin > 192000000) | ||
229 | return; | ||
230 | |||
231 | /* | ||
232 | * The comparison frequency range is 12 MHz to 24 MHz, which limits the | ||
233 | * allowed values for the pre-divider M (normal range 1-8). | ||
234 | * | ||
235 | * Fpfd = Fin / M | ||
236 | */ | ||
237 | m_min = max_t(unsigned int, 1, DIV_ROUND_UP(fin, 24000000)); | ||
238 | m_max = min_t(unsigned int, 8, fin / 12000000); | ||
239 | |||
240 | for (m = m_min; m <= m_max; ++m) { | ||
241 | unsigned long fpfd; | ||
242 | unsigned int n_min; | ||
243 | unsigned int n_max; | ||
244 | unsigned int n; | ||
245 | |||
246 | /* | ||
247 | * The VCO operating range is 900 Mhz to 1800 MHz, which limits | ||
248 | * the allowed values for the multiplier N (normal range | ||
249 | * 60-120). | ||
250 | * | ||
251 | * Fvco = Fin * N / M | ||
252 | */ | ||
253 | fpfd = fin / m; | ||
254 | n_min = max_t(unsigned int, 60, DIV_ROUND_UP(900000000, fpfd)); | ||
255 | n_max = min_t(unsigned int, 120, 1800000000 / fpfd); | ||
256 | |||
257 | for (n = n_min; n < n_max; ++n) { | ||
258 | unsigned long fvco; | ||
259 | unsigned int e_min; | ||
260 | unsigned int e; | ||
261 | |||
262 | /* | ||
263 | * The output frequency is limited to 1039.5 MHz, | ||
264 | * limiting again the allowed values for the | ||
265 | * post-divider E (normal value 1, 2 or 4). | ||
266 | * | ||
267 | * Fout = Fvco / E | ||
268 | */ | ||
269 | fvco = fpfd * n; | ||
270 | e_min = fvco > 1039500000 ? 1 : 0; | ||
271 | |||
272 | for (e = e_min; e < 3; ++e) { | ||
273 | unsigned long fout; | ||
274 | unsigned long diff; | ||
275 | unsigned int div; | ||
276 | |||
277 | /* | ||
278 | * Finally we have a programable divider after | ||
279 | * the PLL, followed by a an optional fixed /7 | ||
280 | * divider. | ||
281 | */ | ||
282 | fout = fvco / (1 << e) / 7; | ||
283 | div = DIV_ROUND_CLOSEST(fout, target); | ||
284 | diff = abs(fout / div - target); | ||
285 | |||
286 | if (diff < pll->diff) { | ||
287 | pll->diff = diff; | ||
288 | pll->pll_m = m; | ||
289 | pll->pll_n = n; | ||
290 | pll->pll_e = e; | ||
291 | pll->div = div; | ||
292 | pll->clksel = clksel; | ||
293 | |||
294 | if (diff == 0) | ||
295 | goto done; | ||
296 | } | ||
297 | } | ||
298 | } | ||
299 | } | ||
300 | |||
301 | done: | ||
302 | output = fin * pll->pll_n / pll->pll_m / (1 << pll->pll_e) | ||
303 | / 7 / pll->div; | ||
304 | error = (long)(output - target) * 10000 / (long)target; | ||
305 | |||
306 | dev_dbg(lvds->dev, | ||
307 | "%pC %lu Hz -> Fout %lu Hz (target %lu Hz, error %d.%02u%%), PLL M/N/E/DIV %u/%u/%u/%u\n", | ||
308 | clk, fin, output, target, error / 100, | ||
309 | error < 0 ? -error % 100 : error % 100, | ||
310 | pll->pll_m, pll->pll_n, pll->pll_e, pll->div); | ||
311 | } | ||
312 | |||
313 | static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) | ||
314 | { | ||
315 | struct pll_info pll = { .diff = (unsigned long)-1 }; | ||
316 | u32 lvdpllcr; | ||
317 | |||
318 | rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], freq, &pll, | ||
319 | LVDPLLCR_CKSEL_DU_DOTCLKIN(0)); | ||
320 | rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], freq, &pll, | ||
321 | LVDPLLCR_CKSEL_DU_DOTCLKIN(1)); | ||
322 | rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, freq, &pll, | ||
323 | LVDPLLCR_CKSEL_EXTAL); | ||
324 | |||
325 | lvdpllcr = LVDPLLCR_PLLON | pll.clksel | LVDPLLCR_CLKOUT | ||
326 | | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1); | ||
327 | |||
328 | if (pll.pll_e > 0) | ||
329 | lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL | ||
330 | | LVDPLLCR_PLLE(pll.pll_e - 1); | ||
331 | |||
332 | rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); | ||
333 | |||
334 | if (pll.div > 1) | ||
335 | /* | ||
336 | * The DIVRESET bit is a misnomer, setting it to 1 deasserts the | ||
337 | * divisor reset. | ||
338 | */ | ||
339 | rcar_lvds_write(lvds, LVDDIV, LVDDIV_DIVSEL | | ||
340 | LVDDIV_DIVRESET | LVDDIV_DIV(pll.div - 1)); | ||
341 | else | ||
342 | rcar_lvds_write(lvds, LVDDIV, 0); | ||
343 | } | ||
344 | |||
345 | /* ----------------------------------------------------------------------------- | ||
346 | * Bridge | ||
347 | */ | ||
348 | |||
158 | static void rcar_lvds_enable(struct drm_bridge *bridge) | 349 | static void rcar_lvds_enable(struct drm_bridge *bridge) |
159 | { | 350 | { |
160 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); | 351 | struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); |
@@ -164,14 +355,13 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) | |||
164 | * do we get a state pointer? | 355 | * do we get a state pointer? |
165 | */ | 356 | */ |
166 | struct drm_crtc *crtc = lvds->bridge.encoder->crtc; | 357 | struct drm_crtc *crtc = lvds->bridge.encoder->crtc; |
167 | u32 lvdpllcr; | ||
168 | u32 lvdhcr; | 358 | u32 lvdhcr; |
169 | u32 lvdcr0; | 359 | u32 lvdcr0; |
170 | int ret; | 360 | int ret; |
171 | 361 | ||
172 | WARN_ON(lvds->enabled); | 362 | WARN_ON(lvds->enabled); |
173 | 363 | ||
174 | ret = clk_prepare_enable(lvds->clock); | 364 | ret = clk_prepare_enable(lvds->clocks.mod); |
175 | if (ret < 0) | 365 | if (ret < 0) |
176 | return; | 366 | return; |
177 | 367 | ||
@@ -196,12 +386,13 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) | |||
196 | 386 | ||
197 | rcar_lvds_write(lvds, LVDCHCR, lvdhcr); | 387 | rcar_lvds_write(lvds, LVDCHCR, lvdhcr); |
198 | 388 | ||
389 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_DUAL_LINK) { | ||
390 | /* Disable dual-link mode. */ | ||
391 | rcar_lvds_write(lvds, LVDSTRIPE, 0); | ||
392 | } | ||
393 | |||
199 | /* PLL clock configuration. */ | 394 | /* PLL clock configuration. */ |
200 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_GEN2_PLLCR) | 395 | lvds->info->pll_setup(lvds, mode->clock * 1000); |
201 | lvdpllcr = rcar_lvds_lvdpllcr_gen2(mode->clock); | ||
202 | else | ||
203 | lvdpllcr = rcar_lvds_lvdpllcr_gen3(mode->clock); | ||
204 | rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); | ||
205 | 396 | ||
206 | /* Set the LVDS mode and select the input. */ | 397 | /* Set the LVDS mode and select the input. */ |
207 | lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT; | 398 | lvdcr0 = lvds->mode << LVDCR0_LVMD_SHIFT; |
@@ -220,11 +411,16 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) | |||
220 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | 411 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); |
221 | } | 412 | } |
222 | 413 | ||
223 | /* Turn the PLL on. */ | 414 | if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { |
224 | lvdcr0 |= LVDCR0_PLLON; | 415 | /* |
225 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | 416 | * Turn the PLL on (simple PLL only, extended PLL is fully |
417 | * controlled through LVDPLLCR). | ||
418 | */ | ||
419 | lvdcr0 |= LVDCR0_PLLON; | ||
420 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | ||
421 | } | ||
226 | 422 | ||
227 | if (lvds->info->gen > 2) { | 423 | if (lvds->info->quirks & RCAR_LVDS_QUIRK_PWD) { |
228 | /* Set LVDS normal mode. */ | 424 | /* Set LVDS normal mode. */ |
229 | lvdcr0 |= LVDCR0_PWD; | 425 | lvdcr0 |= LVDCR0_PWD; |
230 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | 426 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); |
@@ -236,8 +432,10 @@ static void rcar_lvds_enable(struct drm_bridge *bridge) | |||
236 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); | 432 | rcar_lvds_write(lvds, LVDCR0, lvdcr0); |
237 | } | 433 | } |
238 | 434 | ||
239 | /* Wait for the startup delay. */ | 435 | if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) { |
240 | usleep_range(100, 150); | 436 | /* Wait for the PLL startup delay (simple PLL only). */ |
437 | usleep_range(100, 150); | ||
438 | } | ||
241 | 439 | ||
242 | /* Turn the output on. */ | 440 | /* Turn the output on. */ |
243 | lvdcr0 |= LVDCR0_LVRES; | 441 | lvdcr0 |= LVDCR0_LVRES; |
@@ -264,8 +462,9 @@ static void rcar_lvds_disable(struct drm_bridge *bridge) | |||
264 | 462 | ||
265 | rcar_lvds_write(lvds, LVDCR0, 0); | 463 | rcar_lvds_write(lvds, LVDCR0, 0); |
266 | rcar_lvds_write(lvds, LVDCR1, 0); | 464 | rcar_lvds_write(lvds, LVDCR1, 0); |
465 | rcar_lvds_write(lvds, LVDPLLCR, 0); | ||
267 | 466 | ||
268 | clk_disable_unprepare(lvds->clock); | 467 | clk_disable_unprepare(lvds->clocks.mod); |
269 | 468 | ||
270 | lvds->enabled = false; | 469 | lvds->enabled = false; |
271 | } | 470 | } |
@@ -446,6 +645,60 @@ done: | |||
446 | return ret; | 645 | return ret; |
447 | } | 646 | } |
448 | 647 | ||
648 | static struct clk *rcar_lvds_get_clock(struct rcar_lvds *lvds, const char *name, | ||
649 | bool optional) | ||
650 | { | ||
651 | struct clk *clk; | ||
652 | |||
653 | clk = devm_clk_get(lvds->dev, name); | ||
654 | if (!IS_ERR(clk)) | ||
655 | return clk; | ||
656 | |||
657 | if (PTR_ERR(clk) == -ENOENT && optional) | ||
658 | return NULL; | ||
659 | |||
660 | if (PTR_ERR(clk) != -EPROBE_DEFER) | ||
661 | dev_err(lvds->dev, "failed to get %s clock\n", | ||
662 | name ? name : "module"); | ||
663 | |||
664 | return clk; | ||
665 | } | ||
666 | |||
667 | static int rcar_lvds_get_clocks(struct rcar_lvds *lvds) | ||
668 | { | ||
669 | lvds->clocks.mod = rcar_lvds_get_clock(lvds, NULL, false); | ||
670 | if (IS_ERR(lvds->clocks.mod)) | ||
671 | return PTR_ERR(lvds->clocks.mod); | ||
672 | |||
673 | /* | ||
674 | * LVDS encoders without an extended PLL have no external clock inputs. | ||
675 | */ | ||
676 | if (!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL)) | ||
677 | return 0; | ||
678 | |||
679 | lvds->clocks.extal = rcar_lvds_get_clock(lvds, "extal", true); | ||
680 | if (IS_ERR(lvds->clocks.extal)) | ||
681 | return PTR_ERR(lvds->clocks.extal); | ||
682 | |||
683 | lvds->clocks.dotclkin[0] = rcar_lvds_get_clock(lvds, "dclkin.0", true); | ||
684 | if (IS_ERR(lvds->clocks.dotclkin[0])) | ||
685 | return PTR_ERR(lvds->clocks.dotclkin[0]); | ||
686 | |||
687 | lvds->clocks.dotclkin[1] = rcar_lvds_get_clock(lvds, "dclkin.1", true); | ||
688 | if (IS_ERR(lvds->clocks.dotclkin[1])) | ||
689 | return PTR_ERR(lvds->clocks.dotclkin[1]); | ||
690 | |||
691 | /* At least one input to the PLL must be available. */ | ||
692 | if (!lvds->clocks.extal && !lvds->clocks.dotclkin[0] && | ||
693 | !lvds->clocks.dotclkin[1]) { | ||
694 | dev_err(lvds->dev, | ||
695 | "no input clock (extal, dclkin.0 or dclkin.1)\n"); | ||
696 | return -EINVAL; | ||
697 | } | ||
698 | |||
699 | return 0; | ||
700 | } | ||
701 | |||
449 | static int rcar_lvds_probe(struct platform_device *pdev) | 702 | static int rcar_lvds_probe(struct platform_device *pdev) |
450 | { | 703 | { |
451 | struct rcar_lvds *lvds; | 704 | struct rcar_lvds *lvds; |
@@ -475,11 +728,9 @@ static int rcar_lvds_probe(struct platform_device *pdev) | |||
475 | if (IS_ERR(lvds->mmio)) | 728 | if (IS_ERR(lvds->mmio)) |
476 | return PTR_ERR(lvds->mmio); | 729 | return PTR_ERR(lvds->mmio); |
477 | 730 | ||
478 | lvds->clock = devm_clk_get(&pdev->dev, NULL); | 731 | ret = rcar_lvds_get_clocks(lvds); |
479 | if (IS_ERR(lvds->clock)) { | 732 | if (ret < 0) |
480 | dev_err(&pdev->dev, "failed to get clock\n"); | 733 | return ret; |
481 | return PTR_ERR(lvds->clock); | ||
482 | } | ||
483 | 734 | ||
484 | drm_bridge_add(&lvds->bridge); | 735 | drm_bridge_add(&lvds->bridge); |
485 | 736 | ||
@@ -497,21 +748,39 @@ static int rcar_lvds_remove(struct platform_device *pdev) | |||
497 | 748 | ||
498 | static const struct rcar_lvds_device_info rcar_lvds_gen2_info = { | 749 | static const struct rcar_lvds_device_info rcar_lvds_gen2_info = { |
499 | .gen = 2, | 750 | .gen = 2, |
500 | .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR, | 751 | .pll_setup = rcar_lvds_pll_setup_gen2, |
501 | }; | 752 | }; |
502 | 753 | ||
503 | static const struct rcar_lvds_device_info rcar_lvds_r8a7790_info = { | 754 | static const struct rcar_lvds_device_info rcar_lvds_r8a7790_info = { |
504 | .gen = 2, | 755 | .gen = 2, |
505 | .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR | RCAR_LVDS_QUIRK_LANES, | 756 | .quirks = RCAR_LVDS_QUIRK_LANES, |
757 | .pll_setup = rcar_lvds_pll_setup_gen2, | ||
506 | }; | 758 | }; |
507 | 759 | ||
508 | static const struct rcar_lvds_device_info rcar_lvds_gen3_info = { | 760 | static const struct rcar_lvds_device_info rcar_lvds_gen3_info = { |
509 | .gen = 3, | 761 | .gen = 3, |
762 | .quirks = RCAR_LVDS_QUIRK_PWD, | ||
763 | .pll_setup = rcar_lvds_pll_setup_gen3, | ||
510 | }; | 764 | }; |
511 | 765 | ||
512 | static const struct rcar_lvds_device_info rcar_lvds_r8a77970_info = { | 766 | static const struct rcar_lvds_device_info rcar_lvds_r8a77970_info = { |
513 | .gen = 3, | 767 | .gen = 3, |
514 | .quirks = RCAR_LVDS_QUIRK_GEN2_PLLCR | RCAR_LVDS_QUIRK_GEN3_LVEN, | 768 | .quirks = RCAR_LVDS_QUIRK_PWD | RCAR_LVDS_QUIRK_GEN3_LVEN, |
769 | .pll_setup = rcar_lvds_pll_setup_gen2, | ||
770 | }; | ||
771 | |||
772 | static const struct rcar_lvds_device_info rcar_lvds_r8a77990_info = { | ||
773 | .gen = 3, | ||
774 | .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_EXT_PLL | ||
775 | | RCAR_LVDS_QUIRK_DUAL_LINK, | ||
776 | .pll_setup = rcar_lvds_pll_setup_d3_e3, | ||
777 | }; | ||
778 | |||
779 | static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = { | ||
780 | .gen = 3, | ||
781 | .quirks = RCAR_LVDS_QUIRK_GEN3_LVEN | RCAR_LVDS_QUIRK_PWD | ||
782 | | RCAR_LVDS_QUIRK_EXT_PLL | RCAR_LVDS_QUIRK_DUAL_LINK, | ||
783 | .pll_setup = rcar_lvds_pll_setup_d3_e3, | ||
515 | }; | 784 | }; |
516 | 785 | ||
517 | static const struct of_device_id rcar_lvds_of_table[] = { | 786 | static const struct of_device_id rcar_lvds_of_table[] = { |
@@ -523,6 +792,8 @@ static const struct of_device_id rcar_lvds_of_table[] = { | |||
523 | { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info }, | 792 | { .compatible = "renesas,r8a7796-lvds", .data = &rcar_lvds_gen3_info }, |
524 | { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info }, | 793 | { .compatible = "renesas,r8a77970-lvds", .data = &rcar_lvds_r8a77970_info }, |
525 | { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info }, | 794 | { .compatible = "renesas,r8a77980-lvds", .data = &rcar_lvds_gen3_info }, |
795 | { .compatible = "renesas,r8a77990-lvds", .data = &rcar_lvds_r8a77990_info }, | ||
796 | { .compatible = "renesas,r8a77995-lvds", .data = &rcar_lvds_r8a77995_info }, | ||
526 | { } | 797 | { } |
527 | }; | 798 | }; |
528 | 799 | ||
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h index 4870f50d9bec..87149f2f8056 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h +++ b/drivers/gpu/drm/rcar-du/rcar_lvds_regs.h | |||
@@ -18,7 +18,7 @@ | |||
18 | #define LVDCR0_PLLON (1 << 4) | 18 | #define LVDCR0_PLLON (1 << 4) |
19 | #define LVDCR0_PWD (1 << 2) /* Gen3 only */ | 19 | #define LVDCR0_PWD (1 << 2) /* Gen3 only */ |
20 | #define LVDCR0_BEN (1 << 2) /* Gen2 only */ | 20 | #define LVDCR0_BEN (1 << 2) /* Gen2 only */ |
21 | #define LVDCR0_LVEN (1 << 1) /* Gen2 only */ | 21 | #define LVDCR0_LVEN (1 << 1) |
22 | #define LVDCR0_LVRES (1 << 0) | 22 | #define LVDCR0_LVRES (1 << 0) |
23 | 23 | ||
24 | #define LVDCR1 0x0004 | 24 | #define LVDCR1 0x0004 |
@@ -27,21 +27,36 @@ | |||
27 | #define LVDCR1_CLKSTBY (3 << 0) | 27 | #define LVDCR1_CLKSTBY (3 << 0) |
28 | 28 | ||
29 | #define LVDPLLCR 0x0008 | 29 | #define LVDPLLCR 0x0008 |
30 | /* Gen2 & V3M */ | ||
30 | #define LVDPLLCR_CEEN (1 << 14) | 31 | #define LVDPLLCR_CEEN (1 << 14) |
31 | #define LVDPLLCR_FBEN (1 << 13) | 32 | #define LVDPLLCR_FBEN (1 << 13) |
32 | #define LVDPLLCR_COSEL (1 << 12) | 33 | #define LVDPLLCR_COSEL (1 << 12) |
33 | /* Gen2 */ | ||
34 | #define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) | 34 | #define LVDPLLCR_PLLDLYCNT_150M (0x1bf << 0) |
35 | #define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) | 35 | #define LVDPLLCR_PLLDLYCNT_121M (0x22c << 0) |
36 | #define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) | 36 | #define LVDPLLCR_PLLDLYCNT_60M (0x77b << 0) |
37 | #define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) | 37 | #define LVDPLLCR_PLLDLYCNT_38M (0x69a << 0) |
38 | #define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) | 38 | #define LVDPLLCR_PLLDLYCNT_MASK (0x7ff << 0) |
39 | /* Gen3 */ | 39 | /* Gen3 but V3M,D3 and E3 */ |
40 | #define LVDPLLCR_PLLDIVCNT_42M (0x014cb << 0) | 40 | #define LVDPLLCR_PLLDIVCNT_42M (0x014cb << 0) |
41 | #define LVDPLLCR_PLLDIVCNT_85M (0x00a45 << 0) | 41 | #define LVDPLLCR_PLLDIVCNT_85M (0x00a45 << 0) |
42 | #define LVDPLLCR_PLLDIVCNT_128M (0x006c3 << 0) | 42 | #define LVDPLLCR_PLLDIVCNT_128M (0x006c3 << 0) |
43 | #define LVDPLLCR_PLLDIVCNT_148M (0x046c1 << 0) | 43 | #define LVDPLLCR_PLLDIVCNT_148M (0x046c1 << 0) |
44 | #define LVDPLLCR_PLLDIVCNT_MASK (0x7ffff << 0) | 44 | #define LVDPLLCR_PLLDIVCNT_MASK (0x7ffff << 0) |
45 | /* D3 and E3 */ | ||
46 | #define LVDPLLCR_PLLON (1 << 22) | ||
47 | #define LVDPLLCR_PLLSEL_PLL0 (0 << 20) | ||
48 | #define LVDPLLCR_PLLSEL_LVX (1 << 20) | ||
49 | #define LVDPLLCR_PLLSEL_PLL1 (2 << 20) | ||
50 | #define LVDPLLCR_CKSEL_LVX (1 << 17) | ||
51 | #define LVDPLLCR_CKSEL_EXTAL (3 << 17) | ||
52 | #define LVDPLLCR_CKSEL_DU_DOTCLKIN(n) ((5 + (n) * 2) << 17) | ||
53 | #define LVDPLLCR_OCKSEL (1 << 16) | ||
54 | #define LVDPLLCR_STP_CLKOUTE (1 << 14) | ||
55 | #define LVDPLLCR_OUTCLKSEL (1 << 12) | ||
56 | #define LVDPLLCR_CLKOUT (1 << 11) | ||
57 | #define LVDPLLCR_PLLE(n) ((n) << 10) | ||
58 | #define LVDPLLCR_PLLN(n) ((n) << 3) | ||
59 | #define LVDPLLCR_PLLM(n) ((n) << 0) | ||
45 | 60 | ||
46 | #define LVDCTRCR 0x000c | 61 | #define LVDCTRCR 0x000c |
47 | #define LVDCTRCR_CTR3SEL_ZERO (0 << 12) | 62 | #define LVDCTRCR_CTR3SEL_ZERO (0 << 12) |
@@ -71,4 +86,26 @@ | |||
71 | #define LVDCHCR_CHSEL_CH(n, c) ((((c) - (n)) & 3) << ((n) * 4)) | 86 | #define LVDCHCR_CHSEL_CH(n, c) ((((c) - (n)) & 3) << ((n) * 4)) |
72 | #define LVDCHCR_CHSEL_MASK(n) (3 << ((n) * 4)) | 87 | #define LVDCHCR_CHSEL_MASK(n) (3 << ((n) * 4)) |
73 | 88 | ||
89 | /* All registers below are specific to D3 and E3 */ | ||
90 | #define LVDSTRIPE 0x0014 | ||
91 | #define LVDSTRIPE_ST_TRGSEL_DISP (0 << 2) | ||
92 | #define LVDSTRIPE_ST_TRGSEL_HSYNC_R (1 << 2) | ||
93 | #define LVDSTRIPE_ST_TRGSEL_HSYNC_F (2 << 2) | ||
94 | #define LVDSTRIPE_ST_SWAP (1 << 1) | ||
95 | #define LVDSTRIPE_ST_ON (1 << 0) | ||
96 | |||
97 | #define LVDSCR 0x0018 | ||
98 | #define LVDSCR_DEPTH(n) (((n) - 1) << 29) | ||
99 | #define LVDSCR_BANDSET (1 << 28) | ||
100 | #define LVDSCR_TWGCNT(n) ((((n) - 256) / 16) << 24) | ||
101 | #define LVDSCR_SDIV(n) ((n) << 22) | ||
102 | #define LVDSCR_MODE (1 << 21) | ||
103 | #define LVDSCR_RSTN (1 << 20) | ||
104 | |||
105 | #define LVDDIV 0x001c | ||
106 | #define LVDDIV_DIVSEL (1 << 8) | ||
107 | #define LVDDIV_DIVRESET (1 << 7) | ||
108 | #define LVDDIV_DIVSTP (1 << 6) | ||
109 | #define LVDDIV_DIV(n) ((n) << 0) | ||
110 | |||
74 | #endif /* __RCAR_LVDS_REGS_H__ */ | 111 | #endif /* __RCAR_LVDS_REGS_H__ */ |