cfg->b.b = CONVERT_S3_15(ctm->matrix[8]);
}
+static void _dpu_crtc_get_gc_lut(struct drm_crtc_state *state,
+ struct dpu_hw_gc_lut *gc_lut)
+{
+ struct drm_color_lut *lut;
+ int i;
+ u32 val_even, val_odd;
+
+ lut = (struct drm_color_lut *)state->gamma_lut->data;
+
+ if (!lut)
+ return;
+
+ /* Pack 1024 10-bit entries in 512 32-bit registers */
+ for (i = 0; i < PGC_TBL_LEN; i++) {
+ val_even = drm_color_lut_extract(lut[i * 2].green, 10);
+ val_odd = drm_color_lut_extract(lut[i * 2 + 1].green, 10);
+ gc_lut->c0[i] = val_even | (val_odd << 16);
+ val_even = drm_color_lut_extract(lut[i * 2].blue, 10);
+ val_odd = drm_color_lut_extract(lut[i * 2 + 1].blue, 10);
+ gc_lut->c1[i] = val_even | (val_odd << 16);
+ val_even = drm_color_lut_extract(lut[i * 2].red, 10);
+ val_odd = drm_color_lut_extract(lut[i * 2 + 1].red, 10);
+ gc_lut->c2[i] = val_even | (val_odd << 16);
+ }
+
+ /* Disable 8-bit rounding mode */
+ gc_lut->flags = 0;
+}
+
static void _dpu_crtc_setup_cp_blocks(struct drm_crtc *crtc)
{
struct drm_crtc_state *state = crtc->state;
struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state);
struct dpu_crtc_mixer *mixer = cstate->mixers;
struct dpu_hw_pcc_cfg cfg;
+ struct dpu_hw_gc_lut *gc_lut;
struct dpu_hw_ctl *ctl;
struct dpu_hw_dspp *dspp;
int i;
ctl = mixer[i].lm_ctl;
dspp = mixer[i].hw_dspp;
- if (!dspp || !dspp->ops.setup_pcc)
+ if (!dspp)
continue;
- if (!state->ctm) {
- dspp->ops.setup_pcc(dspp, NULL);
- } else {
- _dpu_crtc_get_pcc_coeff(state, &cfg);
- dspp->ops.setup_pcc(dspp, &cfg);
+ if (dspp->ops.setup_pcc) {
+ if (!state->ctm) {
+ dspp->ops.setup_pcc(dspp, NULL);
+ } else {
+ _dpu_crtc_get_pcc_coeff(state, &cfg);
+ dspp->ops.setup_pcc(dspp, &cfg);
+ }
+
+ /* stage config flush mask */
+ ctl->ops.update_pending_flush_dspp(ctl,
+ mixer[i].hw_dspp->idx, DPU_DSPP_PCC);
}
- /* stage config flush mask */
- ctl->ops.update_pending_flush_dspp(ctl,
- mixer[i].hw_dspp->idx, DPU_DSPP_PCC);
+ if (dspp->ops.setup_gc) {
+ if (!state->gamma_lut) {
+ dspp->ops.setup_gc(dspp, NULL);
+ } else {
+ gc_lut = kzalloc(sizeof(*gc_lut), GFP_KERNEL);
+ if (!gc_lut)
+ continue;
+ _dpu_crtc_get_gc_lut(state, gc_lut);
+ dspp->ops.setup_gc(dspp, gc_lut);
+ kfree(gc_lut);
+ }
+
+ /* stage config flush mask */
+ ctl->ops.update_pending_flush_dspp(ctl,
+ mixer[i].hw_dspp->idx, DPU_DSPP_GC);
+ }
}
}
*
* If DSC is enabled, use 2 LMs for 2:2:1 topology
*
- * Add dspps to the reservation requirements if ctm is requested
+ * Add dspps to the reservation requirements if ctm or gamma_lut are requested
*
* Only hardcode num_lm to 2 for cases where num_intf == 2 and CWB is not
* enabled. This is because in cases where CWB is enabled, num_intf will
else
topology.num_lm = 1;
- if (crtc_state->ctm)
+ if (crtc_state->ctm || crtc_state->gamma_lut)
topology.num_dspp = topology.num_lm;
return topology;
bool needs_dirtyfb = dpu_crtc_needs_dirtyfb(crtc_state);
/* don't reallocate resources if only ACTIVE has beeen changed */
- if (crtc_state->mode_changed || crtc_state->connectors_changed) {
+ if (crtc_state->mode_changed || crtc_state->connectors_changed ||
+ crtc_state->color_mgmt_changed) {
rc = dpu_crtc_assign_resources(crtc, crtc_state);
if (rc < 0)
return rc;
drm_crtc_helper_add(crtc, &dpu_crtc_helper_funcs);
- if (dpu_kms->catalog->dspp_count)
- drm_crtc_enable_color_mgmt(crtc, 0, true, 0);
+ if (dpu_kms->catalog->dspp_count) {
+ const struct dpu_dspp_cfg *dspp = &dpu_kms->catalog->dspp[0];
+
+ if (dspp->sblk->gc.base) {
+ drm_mode_crtc_set_gamma_size(crtc, DPU_GAMMA_LUT_SIZE);
+ drm_crtc_enable_color_mgmt(crtc, 0, true, DPU_GAMMA_LUT_SIZE);
+ } else {
+ drm_crtc_enable_color_mgmt(crtc, 0, true, 0);
+ }
+ }
/* save user friendly CRTC name for later */
snprintf(dpu_crtc->name, DPU_CRTC_NAME_SIZE, "crtc%u", crtc->base.id);
#define PCC_BLUE_G_OFF 0x24
#define PCC_BLUE_B_OFF 0x30
+/* DSPP_GC */
+#define GC_EN BIT(0)
+#define GC_DIS 0
+#define GC_8B_ROUND_EN BIT(1)
+#define GC_LUT_SWAP_OFF 0x1c
+#define GC_C0_OFF 0x4
+#define GC_C1_OFF 0xc
+#define GC_C2_OFF 0x14
+#define GC_C0_INDEX_OFF 0x8
+#define GC_C1_INDEX_OFF 0x10
+#define GC_C2_INDEX_OFF 0x18
+
static void dpu_setup_dspp_pcc(struct dpu_hw_dspp *ctx,
struct dpu_hw_pcc_cfg *cfg)
{
DPU_REG_WRITE(&ctx->hw, base, PCC_EN);
}
+static void dpu_setup_dspp_gc(struct dpu_hw_dspp *ctx,
+ struct dpu_hw_gc_lut *gc_lut)
+{
+ int i = 0;
+ u32 base, reg;
+
+ if (!ctx) {
+ DRM_ERROR("invalid ctx\n");
+ return;
+ }
+
+ base = ctx->cap->sblk->gc.base;
+
+ if (!base) {
+ DRM_ERROR("invalid ctx %pK gc base\n", ctx);
+ return;
+ }
+
+ if (!gc_lut) {
+ DRM_DEBUG_DRIVER("disable gc feature\n");
+ DPU_REG_WRITE(&ctx->hw, base, GC_DIS);
+ return;
+ }
+
+ DPU_REG_WRITE(&ctx->hw, base + GC_C0_INDEX_OFF, 0);
+ DPU_REG_WRITE(&ctx->hw, base + GC_C1_INDEX_OFF, 0);
+ DPU_REG_WRITE(&ctx->hw, base + GC_C2_INDEX_OFF, 0);
+
+ for (i = 0; i < PGC_TBL_LEN; i++) {
+ DPU_REG_WRITE(&ctx->hw, base + GC_C0_OFF, gc_lut->c0[i]);
+ DPU_REG_WRITE(&ctx->hw, base + GC_C1_OFF, gc_lut->c1[i]);
+ DPU_REG_WRITE(&ctx->hw, base + GC_C2_OFF, gc_lut->c2[i]);
+ }
+
+ DPU_REG_WRITE(&ctx->hw, base + GC_LUT_SWAP_OFF, BIT(0));
+
+ reg = GC_EN | ((gc_lut->flags & PGC_8B_ROUND) ? GC_8B_ROUND_EN : 0);
+ DPU_REG_WRITE(&ctx->hw, base, reg);
+}
+
/**
* dpu_hw_dspp_init() - Initializes the DSPP hw driver object.
* should be called once before accessing every DSPP.
c->cap = cfg;
if (c->cap->sblk->pcc.base)
c->ops.setup_pcc = dpu_setup_dspp_pcc;
+ if (c->cap->sblk->gc.base)
+ c->ops.setup_gc = dpu_setup_dspp_gc;
return c;
}