aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm')
-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)