aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2014-12-08 17:24:49 -0500
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2014-12-23 05:01:50 -0500
commit1b30dbde8596ca8de2497c2a50d5381dfe62ee8c (patch)
tree1949b3d8d642ddc371ad0b45a44869147629d56f
parent0c1c877681e73b87ef63634ed7da55a711de40a6 (diff)
drm: rcar-du: Add support for external pixel clock
The DU uses the module functional clock as the default pixel clock, but supports using an externally supplied pixel clock instead. Support this by adding the external pixel clock to the DT bindings, and selecting the clock automatically at runtime based on the requested mode pixel frequency. The input clock pins to DU channels routing is configurable, but currently hardcoded to connect input clock i to channel i. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
-rw-r--r--Documentation/devicetree/bindings/video/renesas,du.txt4
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.c60
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.h1
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_group.c14
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_regs.h4
5 files changed, 74 insertions, 9 deletions
diff --git a/Documentation/devicetree/bindings/video/renesas,du.txt b/Documentation/devicetree/bindings/video/renesas,du.txt
index 5102830f2760..c902323928f7 100644
--- a/Documentation/devicetree/bindings/video/renesas,du.txt
+++ b/Documentation/devicetree/bindings/video/renesas,du.txt
@@ -26,6 +26,10 @@ Required Properties:
26 per LVDS encoder. The functional clocks must be named "du.x" with "x" 26 per LVDS encoder. The functional clocks must be named "du.x" with "x"
27 being the channel numerical index. The LVDS clocks must be named 27 being the channel numerical index. The LVDS clocks must be named
28 "lvds.x" with "x" being the LVDS encoder numerical index. 28 "lvds.x" with "x" being the LVDS encoder numerical index.
29 - In addition to the functional and encoder clocks, all DU versions also
30 support externally supplied pixel clocks. Those clocks are optional.
31 When supplied they must be named "dclkin.x" with "x" being the input
32 clock numerical index.
29 33
30Required nodes: 34Required nodes:
31 35
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index cf0dca13264f..ce280bd390a9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -74,33 +74,71 @@ static int rcar_du_crtc_get(struct rcar_du_crtc *rcrtc)
74 if (ret < 0) 74 if (ret < 0)
75 return ret; 75 return ret;
76 76
77 ret = clk_prepare_enable(rcrtc->extclock);
78 if (ret < 0)
79 goto error_clock;
80
77 ret = rcar_du_group_get(rcrtc->group); 81 ret = rcar_du_group_get(rcrtc->group);
78 if (ret < 0) 82 if (ret < 0)
79 clk_disable_unprepare(rcrtc->clock); 83 goto error_group;
84
85 return 0;
80 86
87error_group:
88 clk_disable_unprepare(rcrtc->extclock);
89error_clock:
90 clk_disable_unprepare(rcrtc->clock);
81 return ret; 91 return ret;
82} 92}
83 93
84static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc) 94static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
85{ 95{
86 rcar_du_group_put(rcrtc->group); 96 rcar_du_group_put(rcrtc->group);
97
98 clk_disable_unprepare(rcrtc->extclock);
87 clk_disable_unprepare(rcrtc->clock); 99 clk_disable_unprepare(rcrtc->clock);
88} 100}
89 101
90static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc) 102static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
91{ 103{
92 const struct drm_display_mode *mode = &rcrtc->crtc.mode; 104 const struct drm_display_mode *mode = &rcrtc->crtc.mode;
105 unsigned long mode_clock = mode->clock * 1000;
93 unsigned long clk; 106 unsigned long clk;
94 u32 value; 107 u32 value;
108 u32 escr;
95 u32 div; 109 u32 div;
96 110
97 /* Dot clock */ 111 /* Compute the clock divisor and select the internal or external dot
112 * clock based on the requested frequency.
113 */
98 clk = clk_get_rate(rcrtc->clock); 114 clk = clk_get_rate(rcrtc->clock);
99 div = DIV_ROUND_CLOSEST(clk, mode->clock * 1000); 115 div = DIV_ROUND_CLOSEST(clk, mode_clock);
100 div = clamp(div, 1U, 64U) - 1; 116 div = clamp(div, 1U, 64U) - 1;
117 escr = div | ESCR_DCLKSEL_CLKS;
118
119 if (rcrtc->extclock) {
120 unsigned long extclk;
121 unsigned long extrate;
122 unsigned long rate;
123 u32 extdiv;
124
125 extclk = clk_get_rate(rcrtc->extclock);
126 extdiv = DIV_ROUND_CLOSEST(extclk, mode_clock);
127 extdiv = clamp(extdiv, 1U, 64U) - 1;
128
129 rate = clk / (div + 1);
130 extrate = extclk / (extdiv + 1);
131
132 if (abs((long)extrate - (long)mode_clock) <
133 abs((long)rate - (long)mode_clock)) {
134 dev_dbg(rcrtc->group->dev->dev,
135 "crtc%u: using external clock\n", rcrtc->index);
136 escr = extdiv | ESCR_DCLKSEL_DCLKIN;
137 }
138 }
101 139
102 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR, 140 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? ESCR2 : ESCR,
103 ESCR_DCLKSEL_CLKS | div); 141 escr);
104 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0); 142 rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
105 143
106 /* Signal polarities */ 144 /* Signal polarities */
@@ -543,12 +581,13 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
543 struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index]; 581 struct rcar_du_crtc *rcrtc = &rcdu->crtcs[index];
544 struct drm_crtc *crtc = &rcrtc->crtc; 582 struct drm_crtc *crtc = &rcrtc->crtc;
545 unsigned int irqflags; 583 unsigned int irqflags;
546 char clk_name[5]; 584 struct clk *clk;
585 char clk_name[9];
547 char *name; 586 char *name;
548 int irq; 587 int irq;
549 int ret; 588 int ret;
550 589
551 /* Get the CRTC clock. */ 590 /* Get the CRTC clock and the optional external clock. */
552 if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) { 591 if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) {
553 sprintf(clk_name, "du.%u", index); 592 sprintf(clk_name, "du.%u", index);
554 name = clk_name; 593 name = clk_name;
@@ -562,6 +601,15 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
562 return PTR_ERR(rcrtc->clock); 601 return PTR_ERR(rcrtc->clock);
563 } 602 }
564 603
604 sprintf(clk_name, "dclkin.%u", index);
605 clk = devm_clk_get(rcdu->dev, clk_name);
606 if (!IS_ERR(clk)) {
607 rcrtc->extclock = clk;
608 } else if (PTR_ERR(rcrtc->clock) == -EPROBE_DEFER) {
609 dev_info(rcdu->dev, "can't get external clock %u\n", index);
610 return -EPROBE_DEFER;
611 }
612
565 rcrtc->group = rgrp; 613 rcrtc->group = rgrp;
566 rcrtc->mmio_offset = mmio_offsets[index]; 614 rcrtc->mmio_offset = mmio_offsets[index];
567 rcrtc->index = index; 615 rcrtc->index = index;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
index 984e6083699f..d2f89f7d2e5e 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.h
@@ -26,6 +26,7 @@ struct rcar_du_crtc {
26 struct drm_crtc crtc; 26 struct drm_crtc crtc;
27 27
28 struct clk *clock; 28 struct clk *clock;
29 struct clk *extclock;
29 unsigned int mmio_offset; 30 unsigned int mmio_offset;
30 unsigned int index; 31 unsigned int index;
31 bool started; 32 bool started;
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_group.c b/drivers/gpu/drm/rcar-du/rcar_du_group.c
index 7b6428234252..1bdc0ee0c248 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
@@ -66,9 +66,21 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
66 rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE); 66 rcar_du_group_write(rgrp, DEFR4, DEFR4_CODE);
67 rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5); 67 rcar_du_group_write(rgrp, DEFR5, DEFR5_CODE | DEFR5_DEFE5);
68 68
69 if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) 69 if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) {
70 rcar_du_group_setup_defr8(rgrp); 70 rcar_du_group_setup_defr8(rgrp);
71 71
72 /* Configure input dot clock routing. We currently hardcode the
73 * configuration to routing DOTCLKINn to DUn.
74 */
75 rcar_du_group_write(rgrp, DIDSR, DIDSR_CODE |
76 DIDSR_LCDS_DCLKIN(2) |
77 DIDSR_LCDS_DCLKIN(1) |
78 DIDSR_LCDS_DCLKIN(0) |
79 DIDSR_PDCS_CLK(2, 0) |
80 DIDSR_PDCS_CLK(1, 0) |
81 DIDSR_PDCS_CLK(0, 0));
82 }
83
72 /* Use DS1PR and DS2PR to configure planes priorities and connects the 84 /* Use DS1PR and DS2PR to configure planes priorities and connects the
73 * superposition 0 to DU0 pins. DU1 pins will be configured dynamically. 85 * superposition 0 to DU0 pins. DU1 pins will be configured dynamically.
74 */ 86 */
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_regs.h b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
index 73f7347f740b..c3639d1db28b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_regs.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_regs.h
@@ -256,8 +256,8 @@
256#define DIDSR_LCDS_LVDS0(n) (2 << (8 + (n) * 2)) 256#define DIDSR_LCDS_LVDS0(n) (2 << (8 + (n) * 2))
257#define DIDSR_LCDS_LVDS1(n) (3 << (8 + (n) * 2)) 257#define DIDSR_LCDS_LVDS1(n) (3 << (8 + (n) * 2))
258#define DIDSR_LCDS_MASK(n) (3 << (8 + (n) * 2)) 258#define DIDSR_LCDS_MASK(n) (3 << (8 + (n) * 2))
259#define DIDSR_PCDS_CLK(n, clk) (clk << ((n) * 2)) 259#define DIDSR_PDCS_CLK(n, clk) (clk << ((n) * 2))
260#define DIDSR_PCDS_MASK(n) (3 << ((n) * 2)) 260#define DIDSR_PDCS_MASK(n) (3 << ((n) * 2))
261 261
262/* ----------------------------------------------------------------------------- 262/* -----------------------------------------------------------------------------
263 * Display Timing Generation Registers 263 * Display Timing Generation Registers