Skip to content

Commit a037f7b

Browse files
committed
drm/vc4: Add support for GEN_6_D gamma tables
BCM2712D0 should have a working gamma block, so add it to the driver. Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
1 parent 8d3206e commit a037f7b

4 files changed

Lines changed: 180 additions & 16 deletions

File tree

drivers/gpu/drm/vc4/vc4_crtc.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1447,6 +1447,27 @@ int __vc4_crtc_init(struct drm_device *drm,
14471447
* implemented as private driver state in vc4_kms, not here.
14481448
*/
14491449
drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
1450+
} else if (vc4->gen == VC4_GEN_6_D) {
1451+
/* This is a lie for hvs5 which uses a 16 point PWL, but it
1452+
* allows for something smarter than just 16 linearly spaced
1453+
* segments. Conversion is done in vc5_hvs_update_gamma_lut.
1454+
*/
1455+
drm_mode_crtc_set_gamma_size(crtc, 256);
1456+
drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size);
1457+
1458+
/* Initialize the gamma PWL entries. Assume 12-bit pipeline,
1459+
* evenly spread over full range.
1460+
*/
1461+
for (i = 0; i < SCALER6D_DSPGAMMA_NUM_POINTS; i++) {
1462+
vc4_crtc->pwl_r[i] =
1463+
VC6D_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
1464+
vc4_crtc->pwl_g[i] =
1465+
VC6D_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
1466+
vc4_crtc->pwl_b[i] =
1467+
VC6D_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
1468+
vc4_crtc->pwl_a[i] =
1469+
VC6D_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8);
1470+
}
14501471
}
14511472

14521473
for (i = 0; i < crtc->gamma_size; i++) {

drivers/gpu/drm/vc4/vc4_drv.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <kunit/test-bug.h>
2525

2626
#include "uapi/drm/vc4_drm.h"
27+
#include "vc4_regs.h"
2728

2829
struct drm_device;
2930
struct drm_gem_object;
@@ -610,6 +611,17 @@ extern const struct vc4_pv_data bcm2711_pv4_data;
610611
extern const struct vc4_pv_data bcm2712_pv0_data;
611612
extern const struct vc4_pv_data bcm2712_pv1_data;
612613

614+
struct vc6_gamma_entry {
615+
u32 x_c_terms;
616+
u32 grad_term;
617+
};
618+
619+
#define VC6D_HVS_SET_GAMMA_ENTRY(x, c, g) (struct vc6_gamma_entry){ \
620+
.x_c_terms = VC4_SET_FIELD((x), SCALER6D_DSPGAMMA_OFF_X) | \
621+
VC4_SET_FIELD((c), SCALER6D_DSPGAMMA_OFF_C), \
622+
.grad_term = (g) \
623+
}
624+
613625
struct vc4_crtc {
614626
struct drm_crtc base;
615627
struct platform_device *pdev;
@@ -619,9 +631,19 @@ struct vc4_crtc {
619631
/* Timestamp at start of vblank irq - unaffected by lock delays. */
620632
ktime_t t_vblank;
621633

622-
u8 lut_r[256];
623-
u8 lut_g[256];
624-
u8 lut_b[256];
634+
union {
635+
struct { /* VC4 gamma LUT */
636+
u8 lut_r[256];
637+
u8 lut_g[256];
638+
u8 lut_b[256];
639+
};
640+
struct { /* VC6_D gamma PWL entries */
641+
struct vc6_gamma_entry pwl_r[SCALER6D_DSPGAMMA_NUM_POINTS];
642+
struct vc6_gamma_entry pwl_g[SCALER6D_DSPGAMMA_NUM_POINTS];
643+
struct vc6_gamma_entry pwl_b[SCALER6D_DSPGAMMA_NUM_POINTS];
644+
struct vc6_gamma_entry pwl_a[SCALER6D_DSPGAMMA_NUM_POINTS];
645+
};
646+
};
625647

626648
struct drm_pending_vblank_event *event;
627649

drivers/gpu/drm/vc4/vc4_hvs.c

Lines changed: 108 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

204205
void 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+
527604
static 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) {

drivers/gpu/drm/vc4/vc4_regs.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,32 @@
738738
#define SCALER6D_QOS2 0x000001a4
739739
#define SCALER6D_PROF2 0x000001a8
740740

741+
#define SCALER6D_DITHERGAMMA 0x00000500
742+
# define SCALER6D_DITHERGAMMA_GAMMA(chan) BIT(7 + chan * 8)
743+
# define SCALER6D_DITHERGAMMA_CSC(chan) BIT(6 + chan * 8)
744+
745+
/* Gamma PWL for each channel. 16 points for each of 4 colour channels (alpha
746+
* only on channel 2). 8 bytes per entry, offsets first, then gradient:
747+
* Y = GRAD * X + C
748+
*
749+
* Values for X and C are left justified, and vary depending on the width of
750+
* the HVS channel:
751+
* 8-bit pipeline: X uses [31:24], C is U8.8 format, and GRAD is U4.8.
752+
* 12-bit pipeline: X uses [31:20], C is U12.4 format, and GRAD is U4.8.
753+
*
754+
* The 3 HVS channels start at 0x400 offsets (ie chan 1 starts at 0x2400, and
755+
* chan 2 at 0x2800).
756+
*/
757+
#define SCALER6D_DSPGAMMA_NUM_POINTS 16
758+
#define SCALER6D_DSPGAMMA_START 0x00008800
759+
#define SCALER6D_DSPGAMMA_CHAN_OFFSET 0x200
760+
# define SCALER6D_DSPGAMMA_OFF_X_MASK VC4_MASK(31, 20)
761+
# define SCALER6D_DSPGAMMA_OFF_X_SHIFT 20
762+
# define SCALER6D_DSPGAMMA_OFF_C_MASK VC4_MASK(15, 0)
763+
# define SCALER6D_DSPGAMMA_OFF_C_SHIFT 0
764+
# define SCALER6D_DSPGAMMA_GRAD_MASK VC4_MASK(11, 0)
765+
# define SCALER6D_DSPGAMMA_GRAD_SHIFT 0
766+
741767
#define SCALER6(x) ((hvs->vc4->gen == VC4_GEN_6_C) ? SCALER6_ ## x : SCALER6D_ ## x)
742768

743769
# define VC4_HDMI_SW_RESET_FORMAT_DETECT BIT(1)

0 commit comments

Comments
 (0)