diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c index de5f9c86b9..cafb0608ff 100644 --- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c @@ -2140,10 +2140,6 @@ static void __execlists_unhold(struct i915_request *rq) if (p->flags & I915_DEPENDENCY_WEAK) continue; - /* Propagate any change in error status */ - if (rq->fence.error) - i915_request_set_error_once(w, rq->fence.error); - if (w->engine != rq->engine) continue; diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 23be68a8ec..65c53a10ed 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -294,6 +294,23 @@ struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc, return NULL; } +#define drm_for_each_connector_mask(connector, dev, connector_mask) \ + list_for_each_entry((connector), &(dev)->mode_config.connector_list, head) \ + for_each_if ((connector_mask) & drm_connector_mask(connector)) + +struct drm_connector *vc4_get_crtc_connector(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct drm_connector *connector; + + WARN_ON(hweight32(state->connector_mask) > 1); + + drm_for_each_connector_mask(connector, crtc->dev, state->connector_mask) + return connector; + + return NULL; +} + static void vc4_crtc_pixelvalve_reset(struct drm_crtc *crtc) { struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); @@ -393,6 +410,7 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode CRTC_WRITE(PV_V_CONTROL, PV_VCONTROL_CONTINUOUS | (is_dsi ? PV_VCONTROL_DSI : 0)); + CRTC_WRITE(PV_VSYNCD_EVEN, 0); } CRTC_WRITE(PV_VERTA, @@ -1162,19 +1180,42 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc, if (!vc4->hvs->hvs5) { drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r)); + } else { + /* This is a lie for hvs5 which uses a 16 point PWL, but it + * allows for something smarter than just 16 linearly spaced + * segments. Conversion is done in vc5_hvs_update_gamma_lut. + */ + drm_mode_crtc_set_gamma_size(crtc, 256); + } - drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size); + drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size); + if (!vc4->hvs->hvs5) { /* We support CTM, but only for one CRTC at a time. It's therefore * implemented as private driver state in vc4_kms, not here. */ drm_crtc_enable_color_mgmt(crtc, 0, true, crtc->gamma_size); - } - for (i = 0; i < crtc->gamma_size; i++) { - vc4_crtc->lut_r[i] = i; - vc4_crtc->lut_g[i] = i; - vc4_crtc->lut_b[i] = i; + /* Initialize the VC4 gamma LUTs */ + for (i = 0; i < crtc->gamma_size; i++) { + vc4_crtc->lut_r[i] = i; + vc4_crtc->lut_g[i] = i; + vc4_crtc->lut_b[i] = i; + } + } else { + /* Initialize the VC5 gamma PWL entries. Assume 12-bit pipeline, + * evenly spread over full range. + */ + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++) { + vc4_crtc->pwl_r[i] = + VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8); + vc4_crtc->pwl_g[i] = + VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8); + vc4_crtc->pwl_b[i] = + VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8); + vc4_crtc->pwl_a[i] = + VC5_HVS_SET_GAMMA_ENTRY(i << 8, i << 12, 1 << 8); + } } return 0; diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 48d5d13f38..81bca15b31 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -19,6 +19,7 @@ #include #include "uapi/drm/vc4_drm.h" +#include "vc4_regs.h" struct drm_device; struct drm_gem_object; @@ -325,7 +326,6 @@ struct vc4_hvs { u32 __iomem *dlist; struct clk *core_clk; - struct clk_request *core_req; /* Memory manager for CRTCs to allocate space in the display * list. Units are dwords. @@ -481,6 +481,17 @@ struct vc4_pv_data { }; +struct vc5_gamma_entry { + u32 x_c_terms; + u32 grad_term; +}; + +#define VC5_HVS_SET_GAMMA_ENTRY(x, c, g) (struct vc5_gamma_entry){ \ + .x_c_terms = VC4_SET_FIELD((x), SCALER5_DSPGAMMA_OFF_X) | \ + VC4_SET_FIELD((c), SCALER5_DSPGAMMA_OFF_C), \ + .grad_term = (g) \ +} + struct vc4_crtc { struct drm_crtc base; struct platform_device *pdev; @@ -490,9 +501,19 @@ struct vc4_crtc { /* Timestamp at start of vblank irq - unaffected by lock delays. */ ktime_t t_vblank; - u8 lut_r[256]; - u8 lut_g[256]; - u8 lut_b[256]; + union { + struct { /* VC4 gamma LUT */ + u8 lut_r[256]; + u8 lut_g[256]; + u8 lut_b[256]; + }; + struct { /* VC5 gamma PWL entries */ + struct vc5_gamma_entry pwl_r[SCALER5_DSPGAMMA_NUM_POINTS]; + struct vc5_gamma_entry pwl_g[SCALER5_DSPGAMMA_NUM_POINTS]; + struct vc5_gamma_entry pwl_b[SCALER5_DSPGAMMA_NUM_POINTS]; + struct vc5_gamma_entry pwl_a[SCALER5_DSPGAMMA_NUM_POINTS]; + }; + }; struct drm_pending_vblank_event *event; @@ -546,6 +567,9 @@ vc4_crtc_to_vc4_pv_data(const struct vc4_crtc *crtc) return container_of(data, struct vc4_pv_data, base); } +struct drm_connector *vc4_get_crtc_connector(struct drm_crtc *crtc, + struct drm_crtc_state *state); + struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc, struct drm_crtc_state *state); diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c index ab79d0a82a..6372c48097 100644 --- a/drivers/gpu/drm/vc4/vc4_hvs.c +++ b/drivers/gpu/drm/vc4/vc4_hvs.c @@ -134,6 +134,84 @@ static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data) return 0; } +static int vc5_hvs_debugfs_gamma(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct drm_printer p = drm_seq_file_printer(m); + unsigned int i, chan; + u32 dispstat, dispbkgndx; + + for (chan = 0; chan < SCALER_CHANNELS_COUNT; chan++) { + u32 x_c, grad; + u32 offset = SCALER5_DSPGAMMA_START + + chan * SCALER5_DSPGAMMA_CHAN_OFFSET; + + dispstat = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)), + SCALER_DISPSTATX_MODE); + if (dispstat == SCALER_DISPSTATX_MODE_DISABLED || + dispstat == SCALER_DISPSTATX_MODE_EOF) { + drm_printf(&p, "HVS channel %u: Channel disabled\n", chan); + continue; + } + + dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan)); + if (!(dispbkgndx & SCALER_DISPBKGND_GAMMA)) { + drm_printf(&p, "HVS channel %u: Gamma disabled\n", chan); + continue; + } + + drm_printf(&p, "HVS channel %u:\n", chan); + drm_printf(&p, " red:\n"); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) { + x_c = HVS_READ(offset); + grad = HVS_READ(offset + 4); + drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n", + x_c, grad, + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X), + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C), + grad); + } + drm_printf(&p, " green:\n"); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) { + x_c = HVS_READ(offset); + grad = HVS_READ(offset + 4); + drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n", + x_c, grad, + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X), + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C), + grad); + } + drm_printf(&p, " blue:\n"); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) { + x_c = HVS_READ(offset); + grad = HVS_READ(offset + 4); + drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n", + x_c, grad, + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X), + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C), + grad); + } + + /* Alpha only valid on channel 2 */ + if (chan != 2) + continue; + + drm_printf(&p, " alpha:\n"); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) { + x_c = HVS_READ(offset); + grad = HVS_READ(offset + 4); + drm_printf(&p, " %08x %08x - x %u, c %u, grad %u\n", + x_c, grad, + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_X), + VC4_GET_FIELD(x_c, SCALER5_DSPGAMMA_OFF_C), + grad); + } + } + return 0; +} + /* The filter kernel is composed of dwords each containing 3 9-bit * signed integers packed next to each other. */ @@ -236,6 +314,80 @@ static void vc4_hvs_update_gamma_lut(struct drm_crtc *crtc) vc4_hvs_lut_load(crtc); } +static void vc5_hvs_write_gamma_entry(struct vc4_dev *vc4, + u32 offset, + struct vc5_gamma_entry *gamma) +{ + HVS_WRITE(offset, gamma->x_c_terms); + HVS_WRITE(offset + 4, gamma->grad_term); +} + +static void vc5_hvs_lut_load(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + u32 i; + u32 offset = SCALER5_DSPGAMMA_START + + vc4_state->assigned_channel * SCALER5_DSPGAMMA_CHAN_OFFSET; + + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) + vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_r[i]); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) + vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_g[i]); + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) + vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_b[i]); + + if (vc4_state->assigned_channel == 2) { + /* Alpha only valid on channel 2 */ + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++, offset += 8) + vc5_hvs_write_gamma_entry(vc4, offset, &vc4_crtc->pwl_a[i]); + } +} + +static void vc5_hvs_update_gamma_lut(struct drm_crtc *crtc) +{ + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct drm_color_lut *lut = crtc->state->gamma_lut->data; + unsigned int step, i; + u32 start, end; + +#define VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl, chan) \ + start = drm_color_lut_extract(lut[i * step].chan, 12); \ + end = drm_color_lut_extract(lut[(i + 1) * step - 1].chan, 12); \ + \ + /* Negative gradients not permitted by the hardware, so \ + * flatten such points out. \ + */ \ + if (end < start) \ + end = start; \ + \ + /* Assume 12bit pipeline. \ + * X evenly spread over full range (12 bit). \ + * C as U12.4 format. \ + * Gradient as U4.8 format. \ + */ \ + vc4_crtc->pwl[i] = \ + VC5_HVS_SET_GAMMA_ENTRY(i << 8, start << 4, \ + ((end - start) << 4) / (step - 1)) + + /* HVS5 has a 16 point piecewise linear function for each colour + * channel (including alpha on channel 2) on each display channel. + * + * Currently take a crude subsample of the gamma LUT, but this could + * be improved to implement curve fitting. + */ + step = crtc->gamma_size / SCALER5_DSPGAMMA_NUM_POINTS; + for (i = 0; i < SCALER5_DSPGAMMA_NUM_POINTS; i++) { + VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_r, red); + VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_g, green); + VC5_HVS_UPDATE_GAMMA_ENTRY_FROM_LUT(pwl_b, blue); + } + + vc5_hvs_lut_load(crtc); +} + int vc4_hvs_get_fifo_from_output(struct drm_device *dev, unsigned int output) { struct vc4_dev *vc4 = to_vc4_dev(dev); @@ -329,14 +481,16 @@ static int vc4_hvs_init_channel(struct vc4_dev *vc4, struct drm_crtc *crtc, dispbkgndx &= ~SCALER_DISPBKGND_INTERLACE; HVS_WRITE(SCALER_DISPBKGNDX(chan), dispbkgndx | - SCALER_DISPBKGND_AUTOHS | - ((!vc4->hvs->hvs5) ? SCALER_DISPBKGND_GAMMA : 0) | + SCALER_DISPBKGND_AUTOHS | SCALER_DISPBKGND_GAMMA | (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); /* Reload the LUT, since the SRAMs would have been disabled if * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once. */ - vc4_hvs_lut_load(crtc); + if (!vc4->hvs->hvs5) + vc4_hvs_lut_load(crtc); + else + vc5_hvs_lut_load(crtc); return 0; } @@ -365,6 +519,36 @@ void vc4_hvs_stop_channel(struct drm_device *dev, unsigned int chan) SCALER_DISPSTATX_EMPTY); } +static int vc4_hvs_gamma_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + struct drm_connector_state *conn_state; + struct drm_connector *connector; + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + + if (!vc4->hvs->hvs5) + return 0; + + if (!crtc_state->color_mgmt_changed) + return 0; + + connector = vc4_get_crtc_connector(crtc, crtc_state); + if (!connector) + return -EINVAL; + + if (!(connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)) + return 0; + + conn_state = drm_atomic_get_connector_state(state, connector); + if (!conn_state) + return -EINVAL; + + crtc_state->mode_changed = true; + return 0; +} + int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) { struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); @@ -395,7 +579,7 @@ int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) if (ret) return ret; - return 0; + return vc4_hvs_gamma_check(crtc, state); } static void vc4_hvs_update_dlist(struct drm_crtc *crtc) @@ -534,7 +718,10 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)); if (crtc->state->gamma_lut) { - vc4_hvs_update_gamma_lut(crtc); + if (!vc4->hvs->hvs5) + vc4_hvs_update_gamma_lut(crtc); + else + vc5_hvs_update_gamma_lut(crtc); dispbkgndx |= SCALER_DISPBKGND_GAMMA; } else { /* Unsetting DISPBKGND_GAMMA skips the gamma lut step @@ -730,6 +917,9 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) NULL); vc4_debugfs_add_file(drm, "hvs_dlists", vc4_hvs_debugfs_dlist, NULL); + if (hvs->hvs5) + vc4_debugfs_add_file(drm, "hvs_gamma", vc5_hvs_debugfs_gamma, + NULL); return 0; } diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 2511113a6f..77c2a7ce5e 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -40,6 +40,7 @@ static struct vc4_ctm_state *to_vc4_ctm_state(struct drm_private_state *priv) struct vc4_hvs_state { struct drm_private_state base; unsigned long core_clock_rate; + struct clk_request *core_req; struct { unsigned in_use: 1; @@ -343,20 +344,20 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state) struct drm_device *dev = state->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); struct vc4_hvs *hvs = vc4->hvs; - struct drm_crtc_state *old_crtc_state; struct drm_crtc_state *new_crtc_state; struct vc4_hvs_state *new_hvs_state; struct drm_crtc *crtc; struct vc4_hvs_state *old_hvs_state; struct clk_request *core_req; + unsigned int channel; int i; old_hvs_state = vc4_hvs_get_old_global_state(state); - if (WARN_ON(!old_hvs_state)) + if (WARN_ON(IS_ERR(old_hvs_state))) return; new_hvs_state = vc4_hvs_get_new_global_state(state); - if (WARN_ON(!new_hvs_state)) + if (WARN_ON(IS_ERR(new_hvs_state))) return; for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { @@ -369,16 +370,10 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state) vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel); } - for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { - struct vc4_crtc_state *vc4_crtc_state = - to_vc4_crtc_state(old_crtc_state); + for (channel = 0; channel < HVS_NUM_CHANNELS; channel++) { struct drm_crtc_commit *commit; - unsigned int channel = vc4_crtc_state->assigned_channel; int ret; - if (channel == VC4_HVS_CHANNEL_DISABLED) - continue; - if (!old_hvs_state->fifo_state[channel].in_use) continue; @@ -389,6 +384,9 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state) ret = drm_crtc_commit_wait(commit); if (ret) drm_err(dev, "Timed out waiting for commit\n"); + + drm_crtc_commit_put(commit); + old_hvs_state->fifo_state[channel].pending_commit = NULL; } if (vc4->hvs && vc4->hvs->hvs5) { @@ -408,7 +406,8 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state) * And remove the previous one based on the HVS * requirements if any. */ - clk_request_done(hvs->core_req); + clk_request_done(old_hvs_state->core_req); + old_hvs_state->core_req = NULL; } drm_atomic_helper_commit_modeset_disables(dev, state); @@ -442,8 +441,8 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state) * Request a clock rate based on the current HVS * requirements. */ - hvs->core_req = clk_request_start(hvs->core_clk, - new_hvs_state->core_clock_rate); + new_hvs_state->core_req = clk_request_start(hvs->core_clk, + new_hvs_state->core_clock_rate); /* And drop the temporary request */ clk_request_done(core_req); @@ -468,8 +467,8 @@ static int vc4_atomic_commit_setup(struct drm_atomic_state *state) state->legacy_cursor_update = false; hvs_state = vc4_hvs_get_new_global_state(state); - if (!hvs_state) - return -EINVAL; + if (WARN_ON(IS_ERR(hvs_state))) + return PTR_ERR(hvs_state); for_each_new_crtc_in_state(state, crtc, crtc_state, i) { struct vc4_crtc_state *vc4_crtc_state = @@ -717,12 +716,6 @@ vc4_hvs_channels_duplicate_state(struct drm_private_obj *obj) for (i = 0; i < HVS_NUM_CHANNELS; i++) { state->fifo_state[i].in_use = old_state->fifo_state[i].in_use; state->fifo_state[i].fifo_load = old_state->fifo_state[i].fifo_load; - - if (!old_state->fifo_state[i].pending_commit) - continue; - - state->fifo_state[i].pending_commit = - drm_crtc_commit_get(old_state->fifo_state[i].pending_commit); } state->core_clock_rate = old_state->core_clock_rate; @@ -814,8 +807,8 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev, unsigned int i; hvs_new_state = vc4_hvs_get_global_state(state); - if (!hvs_new_state) - return -EINVAL; + if (IS_ERR(hvs_new_state)) + return PTR_ERR(hvs_new_state); for (i = 0; i < ARRAY_SIZE(hvs_new_state->fifo_state); i++) if (!hvs_new_state->fifo_state[i].in_use) @@ -907,8 +900,8 @@ vc4_core_clock_atomic_check(struct drm_atomic_state *state) load_state = to_vc4_load_tracker_state(priv_state); hvs_new_state = vc4_hvs_get_global_state(state); - if (!hvs_new_state) - return -EINVAL; + if (IS_ERR(hvs_new_state)) + return PTR_ERR(hvs_new_state); for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 7538b84a6d..5989b2ff28 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -491,6 +491,28 @@ #define SCALER_DLIST_START 0x00002000 #define SCALER_DLIST_SIZE 0x00004000 +/* Gamma PWL for each channel. 16 points for each of 4 colour channels (alpha + * only on channel 2). 8 bytes per entry, offsets first, then gradient: + * Y = GRAD * X + C + * + * Values for X and C are left justified, and vary depending on the width of + * the HVS channel: + * 8-bit pipeline: X uses [31:24], C is U8.8 format, and GRAD is U4.8. + * 12-bit pipeline: X uses [31:20], C is U12.4 format, and GRAD is U4.8. + * + * The 3 HVS channels start at 0x400 offsets (ie chan 1 starts at 0x2400, and + * chan 2 at 0x2800). + */ +#define SCALER5_DSPGAMMA_NUM_POINTS 16 +#define SCALER5_DSPGAMMA_START 0x00002000 +#define SCALER5_DSPGAMMA_CHAN_OFFSET 0x400 +# define SCALER5_DSPGAMMA_OFF_X_MASK VC4_MASK(31, 20) +# define SCALER5_DSPGAMMA_OFF_X_SHIFT 20 +# define SCALER5_DSPGAMMA_OFF_C_MASK VC4_MASK(15, 0) +# define SCALER5_DSPGAMMA_OFF_C_SHIFT 0 +# define SCALER5_DSPGAMMA_GRAD_MASK VC4_MASK(11, 0) +# define SCALER5_DSPGAMMA_GRAD_SHIFT 0 + #define SCALER5_DLIST_START 0x00004000 # define VC4_HDMI_SW_RESET_FORMAT_DETECT BIT(1) diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c index b684595a5d..d263fdaa1e 100644 --- a/drivers/gpu/drm/vc4/vc4_vec.c +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -245,7 +245,8 @@ enum vc4_vec_tv_mode_id { }; struct vc4_vec_tv_mode { - const struct drm_display_mode *mode; + const struct drm_display_mode *interlaced_mode; + const struct drm_display_mode *progressive_mode; u32 config0; u32 config1; u32 custom_freq; @@ -279,61 +280,81 @@ static const struct debugfs_reg32 vec_regs[] = { }; static const struct drm_display_mode drm_mode_480i = { - DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500, + DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0, 480, 480 + 7, 480 + 7 + 6, 525, 0, DRM_MODE_FLAG_INTERLACE) }; +static const struct drm_display_mode drm_mode_240p = { + DRM_MODE("720x240", DRM_MODE_TYPE_DRIVER, 13500, + 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0, + 240, 240 + 3, 240 + 3 + 3, 262, 0, 0) +}; + static const struct drm_display_mode drm_mode_576i = { - DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500, + DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, 576, 576 + 4, 576 + 4 + 6, 625, 0, DRM_MODE_FLAG_INTERLACE) }; +static const struct drm_display_mode drm_mode_288p = { + DRM_MODE("720x288", DRM_MODE_TYPE_DRIVER, 13500, + 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, + 288, 288 + 2, 288 + 2 + 3, 312, 0, 0) +}; + static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { [VC4_VEC_TV_MODE_NTSC] = { - .mode = &drm_mode_480i, + .interlaced_mode = &drm_mode_480i, + .progressive_mode = &drm_mode_240p, .config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN, .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_NTSC_J] = { - .mode = &drm_mode_480i, + .interlaced_mode = &drm_mode_480i, + .progressive_mode = &drm_mode_240p, .config0 = VEC_CONFIG0_NTSC_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_NTSC_443] = { /* NTSC with PAL chroma frequency */ - .mode = &drm_mode_480i, + .interlaced_mode = &drm_mode_480i, + .progressive_mode = &drm_mode_240p, .config0 = VEC_CONFIG0_NTSC_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ, .custom_freq = 0x2a098acb, }, [VC4_VEC_TV_MODE_PAL] = { - .mode = &drm_mode_576i, + .interlaced_mode = &drm_mode_576i, + .progressive_mode = &drm_mode_288p, .config0 = VEC_CONFIG0_PAL_BDGHI_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_PAL_M] = { - .mode = &drm_mode_480i, + .interlaced_mode = &drm_mode_480i, + .progressive_mode = &drm_mode_240p, .config0 = VEC_CONFIG0_PAL_M_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_PAL_N] = { - .mode = &drm_mode_576i, + .interlaced_mode = &drm_mode_576i, + .progressive_mode = &drm_mode_288p, .config0 = VEC_CONFIG0_PAL_N_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS, }, [VC4_VEC_TV_MODE_PAL60] = { /* PAL-M with chroma frequency of regular PAL */ - .mode = &drm_mode_480i, + .interlaced_mode = &drm_mode_480i, + .progressive_mode = &drm_mode_240p, .config0 = VEC_CONFIG0_PAL_M_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ, .custom_freq = 0x2a098acb, }, [VC4_VEC_TV_MODE_SECAM] = { - .mode = &drm_mode_576i, + .interlaced_mode = &drm_mode_576i, + .progressive_mode = &drm_mode_288p, .config0 = VEC_CONFIG0_SECAM_STD, .config1 = VEC_CONFIG1_C_CVBS_CVBS, .custom_freq = 0x29c71c72, @@ -393,16 +414,32 @@ static void vc4_vec_connector_destroy(struct drm_connector *connector) static int vc4_vec_connector_get_modes(struct drm_connector *connector) { struct drm_connector_state *state = connector->state; - struct drm_display_mode *mode; + struct drm_display_mode *interlaced_mode, *progressive_mode; - mode = drm_mode_duplicate(connector->dev, - vc4_vec_tv_modes[state->tv.mode].mode); - if (!mode) { + interlaced_mode = + drm_mode_duplicate(connector->dev, + vc4_vec_tv_modes[state->tv.mode].interlaced_mode); + progressive_mode = + drm_mode_duplicate(connector->dev, + vc4_vec_tv_modes[state->tv.mode].progressive_mode); + if (!interlaced_mode || !progressive_mode) { DRM_ERROR("Failed to create a new display mode\n"); + drm_mode_destroy(connector->dev, interlaced_mode); + drm_mode_destroy(connector->dev, progressive_mode); return -ENOMEM; } - drm_mode_probed_add(connector, mode); + if (connector->cmdline_mode.specified && + connector->cmdline_mode.refresh_specified && + !connector->cmdline_mode.interlace) + /* progressive mode set at boot, let's make it preferred */ + progressive_mode->type |= DRM_MODE_TYPE_PREFERRED; + else + /* otherwise, interlaced mode is preferred */ + interlaced_mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, interlaced_mode); + drm_mode_probed_add(connector, progressive_mode); return 1; } @@ -423,18 +460,11 @@ static int vc4_vec_connector_atomic_check(struct drm_connector *conn, struct drm_connector_state *new_state = drm_atomic_get_new_connector_state(state, conn); - const struct vc4_vec_tv_mode *vec_mode = - &vc4_vec_tv_modes[new_state->tv.mode]; - - if (new_state->crtc) { + if (new_state->crtc && old_state->tv.mode != new_state->tv.mode) { struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); - if (!drm_mode_equal(vec_mode->mode, &crtc_state->mode)) - return -EINVAL; - - if (old_state->tv.mode != new_state->tv.mode) - crtc_state->mode_changed = true; + crtc_state->mode_changed = true; } return 0; @@ -559,7 +589,10 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder) VEC_WRITE(VEC_CLMP0_START, 0xac); VEC_WRITE(VEC_CLMP0_END, 0xec); VEC_WRITE(VEC_CONFIG2, - VEC_CONFIG2_UV_DIG_DIS | VEC_CONFIG2_RGB_DIG_DIS); + VEC_CONFIG2_UV_DIG_DIS | + VEC_CONFIG2_RGB_DIG_DIS | + ((encoder->crtc->state->adjusted_mode.flags & + DRM_MODE_FLAG_INTERLACE) ? 0 : VEC_CONFIG2_PROG_SCAN)); VEC_WRITE(VEC_CONFIG3, VEC_CONFIG3_HORIZ_LEN_STD); VEC_WRITE(VEC_DAC_CONFIG, vec->variant->dac_config); @@ -582,17 +615,88 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder) } -static bool vc4_vec_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { - return true; + const struct drm_display_mode *reference_mode = + vc4_vec_tv_modes[conn_state->tv.mode].interlaced_mode; + + if (crtc_state->adjusted_mode.crtc_clock != reference_mode->clock || + crtc_state->adjusted_mode.crtc_htotal != reference_mode->htotal || + crtc_state->adjusted_mode.crtc_hdisplay % 4 != 0 || + crtc_state->adjusted_mode.crtc_hsync_end - + crtc_state->adjusted_mode.crtc_hsync_start < 1) + return -EINVAL; + + switch (reference_mode->vtotal) { + case 525: + if (crtc_state->adjusted_mode.crtc_vdisplay < 1 || + crtc_state->adjusted_mode.crtc_vdisplay > 253 || + crtc_state->adjusted_mode.crtc_vsync_start - + crtc_state->adjusted_mode.crtc_vdisplay < 1 || + crtc_state->adjusted_mode.crtc_vsync_end - + crtc_state->adjusted_mode.crtc_vsync_start != 3 || + crtc_state->adjusted_mode.crtc_vtotal - + crtc_state->adjusted_mode.crtc_vsync_end < 4 || + crtc_state->adjusted_mode.crtc_vtotal > 262) + return -EINVAL; + + if ((crtc_state->adjusted_mode.flags & + DRM_MODE_FLAG_INTERLACE) && + (crtc_state->adjusted_mode.vdisplay % 2 != 0 || + crtc_state->adjusted_mode.vsync_start % 2 != 1 || + crtc_state->adjusted_mode.vsync_end % 2 != 1 || + crtc_state->adjusted_mode.vtotal % 2 != 1)) + return -EINVAL; + + /* progressive mode is hard-wired to 262 total lines */ + if (!(crtc_state->adjusted_mode.flags & + DRM_MODE_FLAG_INTERLACE) && + crtc_state->adjusted_mode.crtc_vtotal != 262) + return -EINVAL; + + break; + + case 625: + if (crtc_state->adjusted_mode.crtc_vdisplay < 1 || + crtc_state->adjusted_mode.crtc_vdisplay > 305 || + crtc_state->adjusted_mode.crtc_vsync_start - + crtc_state->adjusted_mode.crtc_vdisplay < 1 || + crtc_state->adjusted_mode.crtc_vsync_end - + crtc_state->adjusted_mode.crtc_vsync_start != 3 || + crtc_state->adjusted_mode.crtc_vtotal - + crtc_state->adjusted_mode.crtc_vsync_end < 2 || + crtc_state->adjusted_mode.crtc_vtotal > 312) + return -EINVAL; + + if ((crtc_state->adjusted_mode.flags & + DRM_MODE_FLAG_INTERLACE) && + (crtc_state->adjusted_mode.vdisplay % 2 != 0 || + crtc_state->adjusted_mode.vsync_start % 2 != 0 || + crtc_state->adjusted_mode.vsync_end % 2 != 0 || + crtc_state->adjusted_mode.vtotal % 2 != 1)) + return -EINVAL; + + /* progressive mode is hard-wired to 312 total lines */ + if (!(crtc_state->adjusted_mode.flags & + DRM_MODE_FLAG_INTERLACE) && + crtc_state->adjusted_mode.crtc_vtotal != 312) + return -EINVAL; + + break; + + default: + return -EINVAL; + } + + return 0; } static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = { .disable = vc4_vec_encoder_disable, .enable = vc4_vec_encoder_enable, - .mode_fixup = vc4_vec_encoder_mode_fixup, + .atomic_check = vc4_vec_encoder_atomic_check, }; static const struct vc4_vec_variant bcm2835_vec_variant = {