@@ -199,6 +199,7 @@ static const struct debugfs_reg32 vc6_d_hvs_regs[] = {
199199 VC4_REG32 (SCALER6D_HISTBIN6 ),
200200 VC4_REG32 (SCALER6D_HISTBIN7 ),
201201 VC4_REG32 (SCALER6D_HVS_ID ),
202+ VC4_REG32 (SCALER6D_DITHERGAMMA ),
202203};
203204
204205void vc4_hvs_dump_state (struct vc4_hvs * hvs )
@@ -524,6 +525,82 @@ static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs,
524525 vc4_hvs_lut_load (hvs , vc4_crtc );
525526}
526527
528+ static void vc6_hvs_write_gamma_entry (struct vc4_dev * vc4 ,
529+ u32 offset ,
530+ struct vc6_gamma_entry * gamma )
531+ {
532+ struct vc4_hvs * hvs = vc4 -> hvs ;
533+
534+ HVS_WRITE (offset , gamma -> x_c_terms );
535+ HVS_WRITE (offset + 4 , gamma -> grad_term );
536+ }
537+
538+ static void vc6_hvs_lut_load (struct drm_crtc * crtc )
539+ {
540+ struct drm_device * dev = crtc -> dev ;
541+ struct vc4_dev * vc4 = to_vc4_dev (dev );
542+ struct vc4_crtc * vc4_crtc = to_vc4_crtc (crtc );
543+ struct vc4_crtc_state * vc4_state = to_vc4_crtc_state (crtc -> state );
544+ u32 i ;
545+ u32 offset = SCALER6D_DSPGAMMA_START +
546+ vc4_state -> assigned_channel * SCALER6D_DSPGAMMA_CHAN_OFFSET ;
547+
548+ for (i = 0 ; i < SCALER6D_DSPGAMMA_NUM_POINTS ; i ++ , offset += 8 )
549+ vc6_hvs_write_gamma_entry (vc4 , offset , & vc4_crtc -> pwl_b [i ]);
550+ for (i = 0 ; i < SCALER6D_DSPGAMMA_NUM_POINTS ; i ++ , offset += 8 )
551+ vc6_hvs_write_gamma_entry (vc4 , offset , & vc4_crtc -> pwl_g [i ]);
552+ for (i = 0 ; i < SCALER6D_DSPGAMMA_NUM_POINTS ; i ++ , offset += 8 )
553+ vc6_hvs_write_gamma_entry (vc4 , offset , & vc4_crtc -> pwl_r [i ]);
554+
555+ if (vc4_state -> assigned_channel == 2 ) {
556+ /* Alpha only valid on channel 2 */
557+ for (i = 0 ; i < SCALER6D_DSPGAMMA_NUM_POINTS ; i ++ , offset += 8 )
558+ vc6_hvs_write_gamma_entry (vc4 , offset , & vc4_crtc -> pwl_a [i ]);
559+ }
560+ }
561+
562+ static void vc6_hvs_update_gamma_lut (struct drm_crtc * crtc )
563+ {
564+ struct vc4_crtc * vc4_crtc = to_vc4_crtc (crtc );
565+ struct drm_color_lut * lut = crtc -> state -> gamma_lut -> data ;
566+ unsigned int step , i ;
567+ u32 start , end ;
568+
569+ #define VC6D_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT (pwl , chan ) \
570+ start = drm_color_lut_extract(lut[i * step].chan, 12); \
571+ end = drm_color_lut_extract(lut[(i + 1) * step - 1].chan, 12); \
572+ \
573+ /* Negative gradients not permitted by the hardware, so \
574+ * flatten such points out. \
575+ */ \
576+ if (end < start ) \
577+ end = start ; \
578+ \
579+ /* Assume 12bit pipeline. \
580+ * X evenly spread over full range (12 bit). \
581+ * C as U12.4 format. \
582+ * Gradient as U4.8 format. \
583+ */ \
584+ vc4_crtc -> pwl [i ] = \
585+ VC6D_HVS_SET_GAMMA_ENTRY (i << 8 , start << 4 , \
586+ ((end - start ) << 4 ) / (step - 1 ))
587+
588+ /* HVS6 has a 16 point piecewise linear function for each colour
589+ * channel (including alpha on channel 2) on each display channel.
590+ *
591+ * Currently take a crude subsample of the gamma LUT, but this could
592+ * be improved to implement curve fitting.
593+ */
594+ step = crtc -> gamma_size / SCALER6D_DSPGAMMA_NUM_POINTS ;
595+ for (i = 0 ; i < SCALER6D_DSPGAMMA_NUM_POINTS ; i ++ ) {
596+ VC6D_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT (pwl_r , red );
597+ VC6D_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT (pwl_g , green );
598+ VC6D_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT (pwl_b , blue );
599+ }
600+
601+ vc6_hvs_lut_load (crtc );
602+ }
603+
527604static void vc4_hvs_irq_enable_eof (struct vc4_hvs * hvs ,
528605 unsigned int channel )
529606{
@@ -1032,6 +1109,9 @@ static int vc6_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
10321109 VC4_SET_FIELD (mode -> vdisplay - 1 ,
10331110 SCALER6_DISPX_CTRL0_LINES ));
10341111
1112+ if (vc4 -> gen > VC4_GEN_6_D )
1113+ vc6_hvs_lut_load (crtc );
1114+
10351115 drm_dev_exit (idx );
10361116
10371117 return 0 ;
@@ -1346,21 +1426,36 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
13461426 }
13471427
13481428 if (crtc -> state -> color_mgmt_changed ) {
1349- u32 dispbkgndx = HVS_READ (SCALER_DISPBKGNDX (channel ));
1350-
1351- WARN_ON_ONCE (vc4 -> gen > VC4_GEN_5 );
1352-
1353- if (crtc -> state -> gamma_lut ) {
1354- vc4_hvs_update_gamma_lut (hvs , vc4_crtc );
1355- dispbkgndx |= SCALER_DISPBKGND_GAMMA ;
1429+ WARN_ON_ONCE (vc4 -> gen > VC4_GEN_5 && vc4 -> gen < VC4_GEN_6_C );
1430+
1431+ if (vc4 -> gen == VC4_GEN_4 ) {
1432+ u32 dispbkgndx = HVS_READ (SCALER_DISPBKGNDX (channel ));
1433+ if (crtc -> state -> gamma_lut ) {
1434+ vc4_hvs_update_gamma_lut (hvs , vc4_crtc );
1435+ dispbkgndx |= SCALER_DISPBKGND_GAMMA ;
1436+ } else {
1437+ /* Unsetting DISPBKGND_GAMMA skips the gamma lut step
1438+ * in hardware, which is the same as a linear lut that
1439+ * DRM expects us to use in absence of a user lut.
1440+ */
1441+ dispbkgndx &= ~SCALER_DISPBKGND_GAMMA ;
1442+ }
1443+ HVS_WRITE (SCALER_DISPBKGNDX (channel ), dispbkgndx );
13561444 } else {
1357- /* Unsetting DISPBKGND_GAMMA skips the gamma lut step
1358- * in hardware, which is the same as a linear lut that
1359- * DRM expects us to use in absence of a user lut.
1360- */
1361- dispbkgndx &= ~SCALER_DISPBKGND_GAMMA ;
1445+ u32 dither_gamma = HVS_READ (SCALER6D_DITHERGAMMA );
1446+
1447+ if (crtc -> state -> gamma_lut ) {
1448+ vc6_hvs_update_gamma_lut (crtc );
1449+ dither_gamma |= SCALER6D_DITHERGAMMA_GAMMA (channel );
1450+ } else {
1451+ /* Unsetting DISPBKGND_GAMMA skips the gamma lut step
1452+ * in hardware, which is the same as a linear lut that
1453+ * DRM expects us to use in absence of a user lut.
1454+ */
1455+ dither_gamma &= ~SCALER6D_DITHERGAMMA_GAMMA (channel );
1456+ }
1457+ HVS_WRITE (SCALER6D_DITHERGAMMA , dither_gamma );
13621458 }
1363- HVS_WRITE (SCALER_DISPBKGNDX (channel ), dispbkgndx );
13641459 }
13651460
13661461 if (debug_dump_regs ) {
0 commit comments