/** * DOC: VC4 CRTC module * * In VC4, the Pixel Valve is what most closely corresponds to the * DRM's concept of a CRTC. The PV generates video timings from the * encoder's clock plus its configuration. It pulls scaled pixels from * the HVS at that timing, and feeds it to the encoder. * * However, the DRM CRTC also collects the configuration of all the * DRM planes attached to it. As a result, the CRTC is also * responsible for writing the display list for the HVS channel that * the CRTC will use. * * The 2835 has 3 different pixel valves. pv0 in the audio power * domain feeds DSI0 or DPI, while pv1 feeds DS1 or SMI. pv2 in the * image domain can feed either HDMI or the SDTV controller. The * pixel valve chooses from the CPRMAN clocks (HSM for HDMI, VEC for * SDTV, etc.) according to which output type is chosen in the mux. * * For power management, the pixel valve's registers are all clocked * by the AXI clock, while the timings and FIFOs make use of the * output-specific clock. Since the encoders also directly consume * the CPRMAN clocks, and know what timings they need, they are the * ones that set the clock.
*/
#define CRTC_WRITE(offset, val) \ do { \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
writel(val, vc4_crtc->regs + (offset)); \
} while (0)
#define CRTC_READ(offset) \
({ \
kunit_fail_current_test("Accessing a register in a unit test!\n"); \
readl(vc4_crtc->regs + (offset)); \
})
/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
/* Get optional system timestamp before query. */ if (stime)
*stime = ktime_get();
/* * Read vertical scanline which is currently composed for our * pixelvalve by the HVS, and also the scaler status.
*/ if (vc4->gen >= VC4_GEN_6_C)
val = HVS_READ(SCALER6_DISPX_STATUS(channel)); else
val = HVS_READ(SCALER_DISPSTATX(channel));
/* Get optional system timestamp after query. */ if (etime)
*etime = ktime_get();
/* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
*vpos /= 2;
/* Use hpos to correct for field offset in interlaced mode. */ if (vc4_hvs_get_fifo_frame_count(hvs, channel) % 2)
*hpos += mode->crtc_htotal / 2;
}
cob_size = vc4_crtc_get_cob_allocation(vc4, channel); /* This is the offset we need for translating hvs -> pv scanout pos. */
fifo_lines = cob_size / mode->crtc_hdisplay;
if (fifo_lines > 0)
ret = true;
/* HVS more than fifo_lines into frame for compositing? */ if (*vpos > fifo_lines) { /* * We are in active scanout and can get some meaningful results * from HVS. The actual PV scanout can not trail behind more * than fifo_lines as that is the fifo's capacity. Assume that * in active scanout the HVS and PV work in lockstep wrt. HVS * refilling the fifo and PV consuming from the fifo, ie. * whenever the PV consumes and frees up a scanline in the * fifo, the HVS will immediately refill it, therefore * incrementing vpos. Therefore we choose HVS read position - * fifo size in scanlines as a estimate of the real scanout * position of the PV.
*/
*vpos -= fifo_lines + 1;
return ret;
}
/* * Less: This happens when we are in vblank and the HVS, after getting * the VSTART restart signal from the PV, just started refilling its * fifo with new lines from the top-most lines of the new framebuffers. * The PV does not scan out in vblank, so does not remove lines from * the fifo, so the fifo will be full quickly and the HVS has to pause. * We can't get meaningful readings wrt. scanline position of the PV * and need to make things up in a approximative but consistent way.
*/
vblank_lines = mode->vtotal - mode->vdisplay;
if (in_vblank_irq) { /* * Assume the irq handler got called close to first * line of vblank, so PV has about a full vblank * scanlines to go, and as a base timestamp use the * one taken at entry into vblank irq handler, so it * is not affected by random delays due to lock * contention on event_lock or vblank_time lock in * the core.
*/
*vpos = -vblank_lines;
if (stime)
*stime = vc4_crtc->t_vblank; if (etime)
*etime = vc4_crtc->t_vblank;
/* * If the HVS fifo is not yet full then we know for certain * we are at the very beginning of vblank, as the hvs just * started refilling, and the stime and etime timestamps * truly correspond to start of vblank. * * Unfortunately there's no way to report this to upper levels * and make it more useful.
*/
} else { /* * No clue where we are inside vblank. Return a vpos of zero, * which will cause calling code to just return the etime * timestamp uncorrected. At least this is no worse than the * standard fallback.
*/
*vpos = 0;
}
/* * NOTE: Could we use register 0x68 (PV_HW_CFG1) to get the FIFO * size?
*/
u32 fifo_len_bytes = pv_data->fifo_depth;
/* * Pixels are pulled from the HVS if the number of bytes is * lower than the FIFO full level. * * The latency of the pixel fetch mechanism is 6 pixels, so we * need to convert those 6 pixels in bytes, depending on the * format, and then subtract that from the length of the FIFO * to make sure we never end up in a situation where the FIFO * is full.
*/ switch (format) { case PV_CONTROL_FORMAT_DSIV_16: case PV_CONTROL_FORMAT_DSIC_16: return fifo_len_bytes - 2 * HVS_FIFO_LATENCY_PIX; case PV_CONTROL_FORMAT_DSIV_18: return fifo_len_bytes - 14; case PV_CONTROL_FORMAT_24: case PV_CONTROL_FORMAT_DSIV_24: default: /* * For some reason, the pixelvalve4 doesn't work with * the usual formula and will only work with 32.
*/ if (crtc_data->hvs_output == 5) return 32;
/* * It looks like in some situations, we will overflow * the PixelValve FIFO (with the bit 10 of PV stat being * set) and stall the HVS / PV, eventually resulting in * a page flip timeout. * * Displaying the video overlay during a playback with * Kodi on an RPi3 seems to be a great solution with a * failure rate around 50%. * * Removing 1 from the FIFO full level however * seems to completely remove that issue.
*/ if (vc4->gen == VC4_GEN_4) return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX - 1;
ret |= VC4_SET_FIELD((level >> 6),
PV5_CONTROL_FIFO_LEVEL_HIGH);
return ret | VC4_SET_FIELD(level & 0x3f,
PV_CONTROL_FIFO_LEVEL);
}
/* * Returns the encoder attached to the CRTC. * * VC4 can only scan out to one encoder at a time, while the DRM core * allows drivers to push pixels to more than one encoder from the * same CRTC.
*/ struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc, struct drm_crtc_state *state)
{ struct drm_encoder *encoder;
/* The PV needs to be disabled before it can be flushed */
CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) & ~PV_CONTROL_EN);
CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_FIFO_CLR);
CRTC_WRITE(PV_V_CONTROL,
CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN);
ret = wait_for(!(CRTC_READ(PV_V_CONTROL) & PV_VCONTROL_VIDEN), 1);
WARN_ONCE(ret, "Timeout waiting for !PV_VCONTROL_VIDEN\n");
/* * This delay is needed to avoid to get a pixel stuck in an * unflushable FIFO between the pixelvalve and the HDMI * controllers on the BCM2711. * * Timing is fairly sensitive here, so mdelay is the safest * approach. * * If it was to be reworked, the stuck pixel happens on a * BCM2711 when changing mode with a good probability, so a * script that changes mode on a regular basis should trigger * the bug after less than 10 attempts. It manifests itself with * every pixels being shifted by one to the right, and thus the * last pixel of a line actually being displayed as the first * pixel on the next line.
*/
mdelay(20);
if (vc4_encoder && vc4_encoder->post_crtc_disable)
vc4_encoder->post_crtc_disable(encoder, state);
if (vc4_encoder->pre_crtc_enable)
vc4_encoder->pre_crtc_enable(encoder, state);
/* When feeding the transposer block the pixelvalve is unneeded and * should not be enabled.
*/
CRTC_WRITE(PV_V_CONTROL,
CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN);
if (vc4_encoder->post_crtc_enable)
vc4_encoder->post_crtc_enable(encoder, state);
drm_dev_exit(idx);
}
staticenum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc, conststruct drm_display_mode *mode)
{ /* Do not allow doublescan modes from user space */ if (mode->flags & DRM_MODE_FLAG_DBLSCAN) {
DRM_DEBUG_KMS("[CRTC:%d] Doublescan mode rejected.\n",
crtc->base.id); return MODE_NO_DBLESCAN;
}
/* We have to interate over all new connector states because * vc4_crtc_get_margins() might be called before * vc4_crtc_atomic_check() which means margins info in vc4_crtc_state * might be outdated.
*/
for_each_new_connector_in_state(state->state, conn, conn_state, i) { if (conn_state->crtc != state->crtc) continue;
for_each_new_connector_in_state(state, conn, conn_state,
i) { if (conn_state->crtc != crtc) continue;
if (memcmp(&vc4_state->margins, &conn_state->tv.margins, sizeof(vc4_state->margins))) {
memcpy(&vc4_state->margins, &conn_state->tv.margins, sizeof(vc4_state->margins));
/* * Need to force the dlist entries for all planes to be * updated so that the dest rectangles are changed.
*/
crtc_state->zpos_changed = true;
} break;
}
/* Wait for the page flip to unmask the underrun to ensure that * the display list was updated by the hardware. Before that * happens, the HVS will be using the previous display list with * the CRTC and encoder already reconfigured, leading to * underruns. This can be seen when reconfiguring the CRTC.
*/ if (vc4->gen < VC4_GEN_6_C)
vc4_hvs_unmask_underrun(hvs, chan);
}
spin_unlock(&vc4_crtc->irq_lock);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
/* Called when the V3D execution for the BO being flipped to is done, so that * we can actually update the plane's address to point to it.
*/ staticvoid
vc4_async_page_flip_complete(struct vc4_async_flip_state *flip_state)
{ struct drm_crtc *crtc = flip_state->crtc; struct drm_device *dev = crtc->dev; struct drm_plane *plane = crtc->primary;
vc4_plane_async_set_fb(plane, flip_state->fb); if (flip_state->event) { unsignedlong flags;
/* * Decrement the BO usecnt in order to keep the inc/dec * calls balanced when the planes are updated through * the async update path. * * FIXME: we should move to generic async-page-flip when * it's available, so that we can get rid of this * hand-made cleanup_fb() logic.
*/ if (bo)
vc4_bo_dec_usecnt(bo);
}
ret = dma_resv_get_singleton(dma_bo->base.resv, DMA_RESV_USAGE_READ, &fence); if (ret) return ret;
/* If there's no fence, complete the page flip immediately */ if (!fence) {
async_page_flip_complete_function(fence, &flip_state->cb); return 0;
}
/* If the fence has already been completed, complete the page flip */ if (dma_fence_add_callback(fence, &flip_state->cb,
async_page_flip_complete_function))
async_page_flip_complete_function(fence, &flip_state->cb);
/* Save the current FB before it's replaced by the new one in * drm_atomic_set_fb_for_plane(). We'll need the old FB in * vc4_async_page_flip_complete() to decrement the BO usecnt and keep * it consistent. * FIXME: we should move to generic async-page-flip when it's * available, so that we can get rid of this hand-made cleanup_fb() * logic.
*/
flip_state->old_fb = plane->state->fb; if (flip_state->old_fb)
drm_framebuffer_get(flip_state->old_fb);
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
/* Immediately update the plane's legacy fb pointer, so that later * modeset prep sees the state that will be present when the semaphore * is released.
*/
drm_atomic_set_fb_for_plane(plane->state, fb);
vc4_async_set_fence_cb(dev, flip_state);
/* Driver takes ownership of state on successful async commit. */ return 0;
}
/* Implements async (non-vblank-synced) page flips. * * The page flip ioctl needs to return immediately, so we grab the * modeset semaphore on the pipe, and queue the address update for * when V3D is done with the BO being flipped to.
*/ staticint vc4_async_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event,
uint32_t flags)
{ struct drm_device *dev = crtc->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); struct drm_gem_dma_object *dma_bo = drm_fb_dma_get_gem_obj(fb, 0); struct vc4_bo *bo = to_vc4_bo(&dma_bo->base); int ret;
if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) return -ENODEV;
/* * Increment the BO usecnt here, so that we never end up with an * unbalanced number of vc4_bo_{dec,inc}_usecnt() calls when the * plane is later updated through the non-async path. * * FIXME: we should move to generic async-page-flip when * it's available, so that we can get rid of this * hand-made prepare_fb() logic.
*/
ret = vc4_bo_inc_usecnt(bo); if (ret) return ret;
ret = vc4_async_page_flip_common(crtc, fb, event, flags); if (ret) {
vc4_bo_dec_usecnt(bo); return ret;
}
drm_for_each_encoder(encoder, drm) { struct vc4_encoder *vc4_encoder; int i;
if (encoder->encoder_type == DRM_MODE_ENCODER_VIRTUAL) continue;
vc4_encoder = to_vc4_encoder(encoder); for (i = 0; i < ARRAY_SIZE(pv_data->encoder_types); i++) { if (vc4_encoder->type == encoder_types[i]) {
vc4_encoder->clock_select = i;
encoder->possible_crtcs |= drm_crtc_mask(crtc); break;
}
}
}
}
/** * __vc4_crtc_init - Initializes a CRTC * @drm: DRM Device * @pdev: CRTC Platform Device * @vc4_crtc: CRTC Object to Initialize * @data: Configuration data associated with this CRTC * @primary_plane: Primary plane for CRTC * @crtc_funcs: Callbacks for the new CRTC * @crtc_helper_funcs: Helper Callbacks for the new CRTC * @feeds_txp: Is this CRTC connected to the TXP? * * Initializes our private CRTC structure. This function is mostly * relevant for KUnit testing, all other users should use * vc4_crtc_init() instead. * * Returns: * 0 on success, a negative error code on failure.
*/ int __vc4_crtc_init(struct drm_device *drm, struct platform_device *pdev, struct vc4_crtc *vc4_crtc, conststruct vc4_crtc_data *data, struct drm_plane *primary_plane, conststruct drm_crtc_funcs *crtc_funcs, conststruct drm_crtc_helper_funcs *crtc_helper_funcs, bool feeds_txp)
{ struct vc4_dev *vc4 = to_vc4_dev(drm); struct drm_crtc *crtc = &vc4_crtc->base; unsignedint i; int ret;
/* 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;
}
/* For now, we create just the primary and the legacy cursor * planes. We should be able to stack more planes on easily, * but to do that we would need to compute the bandwidth * requirement of the plane configuration, and reject ones * that will take too much.
*/
primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0); if (IS_ERR(primary_plane)) {
dev_err(drm->dev, "failed to construct primary plane\n"); return PTR_ERR(primary_plane);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.