aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2018-08-21 14:31:04 -0400
committerLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2018-09-24 17:41:03 -0400
commitb4734f43f3cadfaa423ce6aceb1e9faea07b8eb8 (patch)
treedcb2fdc065ba418c0aed2b37958fe53feeca7552 /drivers/gpu
parent0bb63534fdf3bc9a82bcfe9f5c6a9653b8b2a3f1 (diff)
drm: rcar-du: Use LVDS PLL clock as dot clock when possible
On selected SoCs, the DU can use the clock output by the LVDS encoder PLL as its input dot clock. This feature is optional, but on the D3 and E3 SoC it is often the only way to obtain a precise dot clock frequency, as the other available clocks (CPG-generated clock and external clock) usually have fixed rates. Add a DU model information field to describe which DU channels can use the LVDS PLL output clock as their input clock, and configure clock routing accordingly. This feature is available on H2, M2-W, M2-N, D3 and E3 SoCs, with D3 and E3 being the primary targets. It is left disabled in this commit, and will be enabled per-SoC after careful testing. At the hardware level, clock routing is configured at runtime in two steps, first selecting an internal dot clock between the LVDS PLL clock and the external DOTCLKIN clock, and then selecting between the internal dot clock and the CPG-generated clock. The first part requires stopping the whole DU group in order for the change to take effect, thus causing flickering on the screen. For this reason we currently hardcode the clock source to the LVDS PLL clock if available, and allow flicker-free selection of the external DOTCLKIN clock or CPG-generated clock otherwise. A more dynamic clock selection process can be implemented later if the need arises. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Tested-by: Jacopo Mondi <jacopo+renesas@jmondi.org> Reviewed-by: Jacopo Mondi <jacopo+renesas@jmondi.org>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_crtc.c8
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_drv.h2
-rw-r--r--drivers/gpu/drm/rcar-du/rcar_du_group.c64
3 files changed, 59 insertions, 15 deletions
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index c89751c26f9c..2f8776c1ec8f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -261,6 +261,14 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
261 rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr); 261 rcar_du_group_write(rcrtc->group, DPLLCR, dpllcr);
262 262
263 escr = ESCR_DCLKSEL_DCLKIN | div; 263 escr = ESCR_DCLKSEL_DCLKIN | div;
264 } else if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index)) {
265 /*
266 * Use the LVDS PLL output as the dot clock when outputting to
267 * the LVDS encoder on an SoC that supports this clock routing
268 * option. We use the clock directly in that case, without any
269 * additional divider.
270 */
271 escr = ESCR_DCLKSEL_DCLKIN;
264 } else { 272 } else {
265 struct du_clk_params params = { .diff = (unsigned long)-1 }; 273 struct du_clk_params params = { .diff = (unsigned long)-1 };
266 274
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
index fef9ea5c22f3..ebba9aefba6a 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h
@@ -53,6 +53,7 @@ struct rcar_du_output_routing {
53 * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*) 53 * @routes: array of CRTC to output routes, indexed by output (RCAR_DU_OUTPUT_*)
54 * @num_lvds: number of internal LVDS encoders 54 * @num_lvds: number of internal LVDS encoders
55 * @dpll_mask: bit mask of DU channels equipped with a DPLL 55 * @dpll_mask: bit mask of DU channels equipped with a DPLL
56 * @lvds_clk_mask: bitmask of channels that can use the LVDS clock as dot clock
56 */ 57 */
57struct rcar_du_device_info { 58struct rcar_du_device_info {
58 unsigned int gen; 59 unsigned int gen;
@@ -62,6 +63,7 @@ struct rcar_du_device_info {
62 struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX]; 63 struct rcar_du_output_routing routes[RCAR_DU_OUTPUT_MAX];
63 unsigned int num_lvds; 64 unsigned int num_lvds;
64 unsigned int dpll_mask; 65 unsigned int dpll_mask;
66 unsigned int lvds_clk_mask;
65}; 67};
66 68
67#define RCAR_DU_MAX_CRTCS 4 69#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..4c62841eff2f 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_group.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_group.c
@@ -89,6 +89,54 @@ static void rcar_du_group_setup_defr8(struct rcar_du_group *rgrp)
89 rcar_du_group_write(rgrp, DEFR8, defr8); 89 rcar_du_group_write(rgrp, DEFR8, defr8);
90} 90}
91 91
92static void rcar_du_group_setup_didsr(struct rcar_du_group *rgrp)
93{
94 struct rcar_du_device *rcdu = rgrp->dev;
95 struct rcar_du_crtc *rcrtc;
96 unsigned int num_crtcs = 0;
97 unsigned int i;
98 u32 didsr;
99
100 /*
101 * Configure input dot clock routing with a hardcoded configuration. If
102 * the DU channel can use the LVDS encoder output clock as the dot
103 * clock, do so. Otherwise route DU_DOTCLKINn signal to DUn.
104 *
105 * Each channel can then select between the dot clock configured here
106 * and the clock provided by the CPG through the ESCR register.
107 */
108 if (rcdu->info->gen < 3 && rgrp->index == 0) {
109 /*
110 * On Gen2 a single register in the first group controls dot
111 * clock selection for all channels.
112 */
113 rcrtc = rcdu->crtcs;
114 num_crtcs = rcdu->num_crtcs;
115 } else if (rcdu->info->gen == 3 && rgrp->num_crtcs > 1) {
116 /*
117 * On Gen3 dot clocks are setup through per-group registers,
118 * only available when the group has two channels.
119 */
120 rcrtc = &rcdu->crtcs[rgrp->index * 2];
121 num_crtcs = rgrp->num_crtcs;
122 }
123
124 if (!num_crtcs)
125 return;
126
127 didsr = DIDSR_CODE;
128 for (i = 0; i < num_crtcs; ++i, ++rcrtc) {
129 if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index))
130 didsr |= DIDSR_LCDS_LVDS0(i)
131 | DIDSR_PDCS_CLK(i, 0);
132 else
133 didsr |= DIDSR_LCDS_DCLKIN(i)
134 | DIDSR_PDCS_CLK(i, 0);
135 }
136
137 rcar_du_group_write(rgrp, DIDSR, didsr);
138}
139
92static void rcar_du_group_setup(struct rcar_du_group *rgrp) 140static void rcar_du_group_setup(struct rcar_du_group *rgrp)
93{ 141{
94 struct rcar_du_device *rcdu = rgrp->dev; 142 struct rcar_du_device *rcdu = rgrp->dev;
@@ -106,21 +154,7 @@ static void rcar_du_group_setup(struct rcar_du_group *rgrp)
106 154
107 if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) { 155 if (rcar_du_has(rgrp->dev, RCAR_DU_FEATURE_EXT_CTRL_REGS)) {
108 rcar_du_group_setup_defr8(rgrp); 156 rcar_du_group_setup_defr8(rgrp);
109 157 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 } 158 }
125 159
126 if (rcdu->info->gen >= 3) 160 if (rcdu->info->gen >= 3)