1
+ // SPDX-License-Identifier: GPL-2.0+
1
2
/*
2
3
* rcar_du_crtc.c -- R-Car Display Unit CRTCs
3
4
*
4
5
* Copyright (C) 2013-2015 Renesas Electronics Corporation
5
6
*
6
7
* Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com)
7
- *
8
- * This program is free software; you can redistribute it and/or modify
9
- * it under the terms of the GNU General Public License as published by
10
- * the Free Software Foundation; either version 2 of the License, or
11
- * (at your option) any later version.
12
8
*/
13
9
14
10
#include <linux/clk.h>
@@ -198,6 +194,47 @@ static void rcar_du_dpll_divider(struct rcar_du_crtc *rcrtc,
198
194
best_diff );
199
195
}
200
196
197
+ struct du_clk_params {
198
+ struct clk * clk ;
199
+ unsigned long rate ;
200
+ unsigned long diff ;
201
+ u32 escr ;
202
+ };
203
+
204
+ static void rcar_du_escr_divider (struct clk * clk , unsigned long target ,
205
+ u32 escr , struct du_clk_params * params )
206
+ {
207
+ unsigned long rate ;
208
+ unsigned long diff ;
209
+ u32 div ;
210
+
211
+ /*
212
+ * If the target rate has already been achieved perfectly we can't do
213
+ * better.
214
+ */
215
+ if (params -> diff == 0 )
216
+ return ;
217
+
218
+ /*
219
+ * Compute the input clock rate and internal divisor values to obtain
220
+ * the clock rate closest to the target frequency.
221
+ */
222
+ rate = clk_round_rate (clk , target );
223
+ div = clamp (DIV_ROUND_CLOSEST (rate , target ), 1UL , 64UL ) - 1 ;
224
+ diff = abs (rate / (div + 1 ) - target );
225
+
226
+ /*
227
+ * Store the parameters if the resulting frequency is better than any
228
+ * previously calculated value.
229
+ */
230
+ if (diff < params -> diff ) {
231
+ params -> clk = clk ;
232
+ params -> rate = rate ;
233
+ params -> diff = diff ;
234
+ params -> escr = escr | div ;
235
+ }
236
+ }
237
+
201
238
static const struct soc_device_attribute rcar_du_r8a7795_es1 [] = {
202
239
{ .soc_id = "r8a7795" , .revision = "ES1.*" },
203
240
{ /* sentinel */ }
@@ -208,89 +245,83 @@ static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
208
245
const struct drm_display_mode * mode = & rcrtc -> crtc .state -> adjusted_mode ;
209
246
struct rcar_du_device * rcdu = rcrtc -> group -> dev ;
210
247
unsigned long mode_clock = mode -> clock * 1000 ;
211
- unsigned long clk ;
212
- u32 value ;
248
+ u32 dsmr ;
213
249
u32 escr ;
214
- u32 div ;
215
250
216
- /*
217
- * Compute the clock divisor and select the internal or external dot
218
- * clock based on the requested frequency.
219
- */
220
- clk = clk_get_rate (rcrtc -> clock );
221
- div = DIV_ROUND_CLOSEST (clk , mode_clock );
222
- div = clamp (div , 1U , 64U ) - 1 ;
223
- escr = div | ESCR_DCLKSEL_CLKS ;
224
-
225
- if (rcrtc -> extclock ) {
251
+ if (rcdu -> info -> dpll_mask & (1 << rcrtc -> index )) {
252
+ unsigned long target = mode_clock ;
226
253
struct dpll_info dpll = { 0 };
227
254
unsigned long extclk ;
228
- unsigned long extrate ;
229
- unsigned long rate ;
230
- u32 extdiv ;
255
+ u32 dpllcr ;
256
+ u32 div = 0 ;
231
257
232
- extclk = clk_get_rate (rcrtc -> extclock );
233
- if (rcdu -> info -> dpll_ch & (1 << rcrtc -> index )) {
234
- unsigned long target = mode_clock ;
258
+ /*
259
+ * DU channels that have a display PLL can't use the internal
260
+ * system clock, and have no internal clock divider.
261
+ */
235
262
236
- /*
237
- * The H3 ES1.x exhibits dot clock duty cycle stability
238
- * issues. We can work around them by configuring the
239
- * DPLL to twice the desired frequency, coupled with a
240
- * /2 post-divider. This isn't needed on other SoCs and
241
- * breaks HDMI output on M3-W for a currently unknown
242
- * reason, so restrict the workaround to H3 ES1.x.
243
- */
244
- if (soc_device_match (rcar_du_r8a7795_es1 ))
245
- target *= 2 ;
263
+ if (WARN_ON (!rcrtc -> extclock ))
264
+ return ;
246
265
247
- rcar_du_dpll_divider (rcrtc , & dpll , extclk , target );
248
- extclk = dpll .output ;
266
+ /*
267
+ * The H3 ES1.x exhibits dot clock duty cycle stability issues.
268
+ * We can work around them by configuring the DPLL to twice the
269
+ * desired frequency, coupled with a /2 post-divider. Restrict
270
+ * the workaround to H3 ES1.x as ES2.0 and all other SoCs have
271
+ * no post-divider when a display PLL is present (as shown by
272
+ * the workaround breaking HDMI output on M3-W during testing).
273
+ */
274
+ if (soc_device_match (rcar_du_r8a7795_es1 )) {
275
+ target *= 2 ;
276
+ div = 1 ;
249
277
}
250
278
251
- extdiv = DIV_ROUND_CLOSEST ( extclk , mode_clock );
252
- extdiv = clamp ( extdiv , 1U , 64U ) - 1 ;
279
+ extclk = clk_get_rate ( rcrtc -> extclock );
280
+ rcar_du_dpll_divider ( rcrtc , & dpll , extclk , target ) ;
253
281
254
- rate = clk / (div + 1 );
255
- extrate = extclk / (extdiv + 1 );
282
+ dpllcr = DPLLCR_CODE | DPLLCR_CLKE
283
+ | DPLLCR_FDPLL (dpll .fdpll )
284
+ | DPLLCR_N (dpll .n ) | DPLLCR_M (dpll .m )
285
+ | DPLLCR_STBY ;
256
286
257
- if (abs ((long )extrate - (long )mode_clock ) <
258
- abs ((long )rate - (long )mode_clock )) {
287
+ if (rcrtc -> index == 1 )
288
+ dpllcr |= DPLLCR_PLCS1
289
+ | DPLLCR_INCS_DOTCLKIN1 ;
290
+ else
291
+ dpllcr |= DPLLCR_PLCS0
292
+ | DPLLCR_INCS_DOTCLKIN0 ;
259
293
260
- if (rcdu -> info -> dpll_ch & (1 << rcrtc -> index )) {
261
- u32 dpllcr = DPLLCR_CODE | DPLLCR_CLKE
262
- | DPLLCR_FDPLL (dpll .fdpll )
263
- | DPLLCR_N (dpll .n ) | DPLLCR_M (dpll .m )
264
- | DPLLCR_STBY ;
294
+ rcar_du_group_write (rcrtc -> group , DPLLCR , dpllcr );
265
295
266
- if (rcrtc -> index == 1 )
267
- dpllcr |= DPLLCR_PLCS1
268
- | DPLLCR_INCS_DOTCLKIN1 ;
269
- else
270
- dpllcr |= DPLLCR_PLCS0
271
- | DPLLCR_INCS_DOTCLKIN0 ;
296
+ escr = ESCR_DCLKSEL_DCLKIN | div ;
297
+ } else {
298
+ struct du_clk_params params = { .diff = (unsigned long )-1 };
272
299
273
- rcar_du_group_write (rcrtc -> group , DPLLCR ,
274
- dpllcr );
275
- }
300
+ rcar_du_escr_divider (rcrtc -> clock , mode_clock ,
301
+ ESCR_DCLKSEL_CLKS , & params );
302
+ if (rcrtc -> extclock )
303
+ rcar_du_escr_divider (rcrtc -> extclock , mode_clock ,
304
+ ESCR_DCLKSEL_DCLKIN , & params );
276
305
277
- escr = ESCR_DCLKSEL_DCLKIN | extdiv ;
278
- }
306
+ dev_dbg (rcrtc -> group -> dev -> dev , "mode clock %lu %s rate %lu\n" ,
307
+ mode_clock , params .clk == rcrtc -> clock ? "cpg" : "ext" ,
308
+ params .rate );
279
309
280
- dev_dbg (rcrtc -> group -> dev -> dev ,
281
- "mode clock %lu extrate %lu rate %lu ESCR 0x%08x\n" ,
282
- mode_clock , extrate , rate , escr );
310
+ clk_set_rate (params .clk , params .rate );
311
+ escr = params .escr ;
283
312
}
284
313
285
- rcar_du_group_write (rcrtc -> group , rcrtc -> index % 2 ? ESCR2 : ESCR ,
286
- escr );
287
- rcar_du_group_write (rcrtc -> group , rcrtc -> index % 2 ? OTAR2 : OTAR , 0 );
314
+ dev_dbg (rcrtc -> group -> dev -> dev , "%s: ESCR 0x%08x\n" , __func__ , escr );
315
+
316
+ rcar_du_crtc_write (rcrtc , rcrtc -> index % 2 ? ESCR13 : ESCR02 , escr );
317
+ rcar_du_crtc_write (rcrtc , rcrtc -> index % 2 ? OTAR13 : OTAR02 , 0 );
288
318
289
319
/* Signal polarities */
290
- value = ((mode -> flags & DRM_MODE_FLAG_PVSYNC ) ? DSMR_VSL : 0 )
291
- | ((mode -> flags & DRM_MODE_FLAG_PHSYNC ) ? DSMR_HSL : 0 )
292
- | DSMR_DIPM_DISP | DSMR_CSPM ;
293
- rcar_du_crtc_write (rcrtc , DSMR , value );
320
+ dsmr = ((mode -> flags & DRM_MODE_FLAG_PVSYNC ) ? DSMR_VSL : 0 )
321
+ | ((mode -> flags & DRM_MODE_FLAG_PHSYNC ) ? DSMR_HSL : 0 )
322
+ | ((mode -> flags & DRM_MODE_FLAG_INTERLACE ) ? DSMR_ODEV : 0 )
323
+ | DSMR_DIPM_DISP | DSMR_CSPM ;
324
+ rcar_du_crtc_write (rcrtc , DSMR , dsmr );
294
325
295
326
/* Display timings */
296
327
rcar_du_crtc_write (rcrtc , HDSR , mode -> htotal - mode -> hsync_start - 19 );
@@ -684,11 +715,25 @@ static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc,
684
715
rcar_du_vsp_atomic_flush (rcrtc );
685
716
}
686
717
718
+ enum drm_mode_status rcar_du_crtc_mode_valid (struct drm_crtc * crtc ,
719
+ const struct drm_display_mode * mode )
720
+ {
721
+ struct rcar_du_crtc * rcrtc = to_rcar_crtc (crtc );
722
+ struct rcar_du_device * rcdu = rcrtc -> group -> dev ;
723
+ bool interlaced = mode -> flags & DRM_MODE_FLAG_INTERLACE ;
724
+
725
+ if (interlaced && !rcar_du_has (rcdu , RCAR_DU_FEATURE_INTERLACED ))
726
+ return MODE_NO_INTERLACE ;
727
+
728
+ return MODE_OK ;
729
+ }
730
+
687
731
static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
688
732
.atomic_begin = rcar_du_crtc_atomic_begin ,
689
733
.atomic_flush = rcar_du_crtc_atomic_flush ,
690
734
.atomic_enable = rcar_du_crtc_atomic_enable ,
691
735
.atomic_disable = rcar_du_crtc_atomic_disable ,
736
+ .mode_valid = rcar_du_crtc_mode_valid ,
692
737
};
693
738
694
739
static void rcar_du_crtc_crc_init (struct rcar_du_crtc * rcrtc )
0 commit comments