/** * DOC: VC4 plane module * * Each DRM plane is a layer of pixels being scanned out by the HVS. * * At atomic modeset check time, we compute the HVS display element * state that would be necessary for displaying the plane (giving us a * chance to figure out if a plane configuration is invalid), then at * atomic flush time the CRTC will ask us to write our element state * into the region of the HVS that it has allocated for us.
*/
/* Called during init to allocate the plane's atomic state. */ staticvoid vc4_plane_reset(struct drm_plane *plane)
{ struct vc4_plane_state *vc4_state;
if (plane->state)
__drm_atomic_helper_plane_destroy_state(plane->state);
kfree(plane->state);
vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL); if (!vc4_state) return;
/* Returns the scl0/scl1 field based on whether the dimensions need to * be up/down/non-scaled. * * This is a replication of a table from the spec.
*/ static u32 vc4_get_scl_field(struct drm_plane_state *state, int plane)
{ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
switch (vc4_state->x_scaling[plane] << 2 | vc4_state->y_scaling[plane]) { case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF: return SCALER_CTL0_SCL_H_PPF_V_PPF; case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF: return SCALER_CTL0_SCL_H_TPZ_V_PPF; case VC4_SCALING_PPF << 2 | VC4_SCALING_TPZ: return SCALER_CTL0_SCL_H_PPF_V_TPZ; case VC4_SCALING_TPZ << 2 | VC4_SCALING_TPZ: return SCALER_CTL0_SCL_H_TPZ_V_TPZ; case VC4_SCALING_PPF << 2 | VC4_SCALING_NONE: return SCALER_CTL0_SCL_H_PPF_V_NONE; case VC4_SCALING_NONE << 2 | VC4_SCALING_PPF: return SCALER_CTL0_SCL_H_NONE_V_PPF; case VC4_SCALING_NONE << 2 | VC4_SCALING_TPZ: return SCALER_CTL0_SCL_H_NONE_V_TPZ; case VC4_SCALING_TPZ << 2 | VC4_SCALING_NONE: return SCALER_CTL0_SCL_H_TPZ_V_NONE; default: case VC4_SCALING_NONE << 2 | VC4_SCALING_NONE: /* The unity case is independently handled by * SCALER_CTL0_UNITY.
*/ return 0;
}
}
/* YUV conversion requires that horizontal scaling be enabled * on the UV plane even if vc4_get_scaling_mode() returned * VC4_SCALING_NONE (which can happen when the down-scaling * ratio is 0.5). Let's force it to VC4_SCALING_PPF in this * case.
*/ if (vc4_state->x_scaling[1] == VC4_SCALING_NONE)
vc4_state->x_scaling[1] = VC4_SCALING_PPF;
/* Similarly UV needs vertical scaling to be enabled. * Without this a 1:1 scaled YUV422 plane isn't rendered.
*/ if (vc4_state->y_scaling[1] == VC4_SCALING_NONE)
vc4_state->y_scaling[1] = VC4_SCALING_PPF;
} else {
vc4_state->is_yuv = false;
vc4_state->x_scaling[1] = VC4_SCALING_NONE;
vc4_state->y_scaling[1] = VC4_SCALING_NONE;
}
/* The specs note that while the reciprocal would be defined * as (1<<32)/scale, ~0 is close enough.
*/
recip = ~0 / scale;
vc4_dlist_write(vc4_state, /* * The BCM2712 is lacking BIT(31) compared to * the previous generations, but we don't use * it.
*/
VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) |
VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE));
vc4_dlist_write(vc4_state,
VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP));
}
/* * Start the phase at 1/2 pixel from the 1st pixel at src_x. * 1/4 pixel for YUV.
*/ if (channel) { /* * The phase is relative to scale_src->x, so shift it for * display list's x value
*/
offset = (xy & 0x1ffff) >> (16 - PHASE_BITS) >> 1;
offset += -(1 << PHASE_BITS >> 2);
} else { /* * The phase is relative to scale_src->x, so shift it for * display list's x value
*/
offset = (xy & 0xffff) >> (16 - PHASE_BITS);
offset += -(1 << PHASE_BITS >> 1);
/* * This is a kludge to make sure the scaling factors are * consistent with YUV's luma scaling. We lose 1-bit precision * because of this.
*/
scale &= ~1;
}
/* * There may be a also small error introduced by precision of scale. * Add half of that as a compromise
*/
offset2 = src - dst * scale;
offset2 >>= 16 - PHASE_BITS;
phase = offset + (offset2 >> 1);
/* Ensure +ve values don't touch the sign bit, then truncate negative values */ if (phase >= 1 << PHASE_BITS)
phase = (1 << PHASE_BITS) - 1;
phase &= SCALER_PPF_IPHASE_MASK;
vc4_dlist_write(vc4_state,
SCALER_PPF_AGC |
VC4_SET_FIELD(scale, SCALER_PPF_SCALE) | /* * The register layout documentation is slightly * different to setup the phase in the BCM2712, * but they seem equivalent.
*/
VC4_SET_FIELD(phase, SCALER_PPF_IPHASE));
}
/* LBM is not needed when there's no vertical scaling. */ if (vc4_state->y_scaling[0] == VC4_SCALING_NONE &&
vc4_state->y_scaling[1] == VC4_SCALING_NONE) return 0;
/* * This can be further optimized in the RGB/YUV444 case if the PPF * decimation factor is between 0.5 and 1.0 by using crtc_w. * * It's not an issue though, since in that case since src_w[0] is going * to be greater than or equal to crtc_w.
*/ if (vc4_state->x_scaling[0] == VC4_SCALING_TPZ)
pix_per_line = vc4_state->crtc_w; else
pix_per_line = vc4_state->src_w[0] >> 16;
if (!vc4_state->is_yuv) { if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ)
lbm = pix_per_line * 8; else { /* In special cases, this multiplier might be 12. */
lbm = pix_per_line * 16;
}
} else { /* There are cases for this going down to a multiplier * of 2, but according to the firmware source, the * table in the docs is somewhat wrong.
*/
lbm = pix_per_line * 16;
}
/* Align it to 64 or 128 (hvs5) bytes */
lbm = roundup(lbm, vc4->gen == VC4_GEN_5 ? 128 : 64);
/* Each "word" of the LBM memory contains 2 or 4 (hvs5) pixels */
lbm /= vc4->gen == VC4_GEN_5 ? 4 : 2;
/* LBM is meant to use the smaller of source or dest width, but there * is a issue with UV scaling that the size required for the second * channel is based on the source width only.
*/ if (info->hsub > 1 && channel == 1)
width = state->src_w >> 16; else
width = min(state->src_w >> 16, state->crtc_w);
width = round_up(width / info->hsub, 4);
wpc = vc4_lbm_words_per_component(state, channel); if (!wpc) return 0;
components = vc4_lbm_components(state, channel); if (!components) return 0;
if (state->alpha != DRM_BLEND_ALPHA_OPAQUE && info->has_alpha)
components -= 1;
words = width * wpc * components;
lines = DIV_ROUND_UP(words, 128 / info->hsub);
for (i = 0; i < 2; i++) if (vc4_state->y_scaling[channel] != VC4_SCALING_NONE)
channels_scaled++;
/* LBM is not needed when there's no vertical scaling. */ if (vc4_state->y_scaling[0] == VC4_SCALING_NONE &&
vc4_state->y_scaling[1] == VC4_SCALING_NONE) return 0;
if (vc4->gen >= VC4_GEN_6_C) return __vc6_lbm_size(state); else return __vc4_lbm_size(state);
}
/* * TODO: This only works for raster formats, and is sub-optimal * for buffers with a stride aligned on 32 bytes.
*/ unsignedint words_per_line = (stride + 62) / 32; unsignedint fetch_region_size = words_per_line * 32; unsignedint buffer_lines = 2 << vc4_state->upm_buffer_lines; unsignedint buffer_size = fetch_region_size * buffer_lines;
/* The HVS is able to process 2 pixels/cycle when scaling the source, * 4 pixels/cycle otherwise. * Alpha blending step seems to be pipelined and it's always operating * at 4 pixels/cycle, so the limiting aspect here seems to be the * scaler block. * HVS load is expressed in clk-cycles/sec (AKA Hz).
*/ if (vc4_state->x_scaling[0] != VC4_SCALING_NONE ||
vc4_state->x_scaling[1] != VC4_SCALING_NONE ||
vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
vc4_state->y_scaling[1] != VC4_SCALING_NONE)
hvs_load_shift = 1; else
hvs_load_shift = 2;
vc4_state->membus_load = 0;
vc4_state->hvs_load = 0; for (i = 0; i < fb->format->num_planes; i++) { /* Even if the bandwidth/plane required for a single frame is * * (vc4_state->src_w[i] >> 16) * (vc4_state->src_h[i] >> 16) * * cpp * vrefresh * * when downscaling, we have to read more pixels per line in * the time frame reserved for a single line, so the bandwidth * demand can be punctually higher. To account for that, we * calculate the down-scaling factor and multiply the plane * load by this number. We're likely over-estimating the read * demand, but that's better than under-estimating it.
*/
vscale_factor = DIV_ROUND_UP(vc4_state->src_h[i] >> 16,
vc4_state->crtc_h);
vc4_state->membus_load += (vc4_state->src_w[i] >> 16) *
(vc4_state->src_h[i] >> 16) *
vscale_factor * fb->format->cpp[i];
vc4_state->hvs_load += vc4_state->crtc_h * vc4_state->crtc_w;
}
lbm_size = vc4_lbm_size(state); if (!lbm_size) return 0;
/* * NOTE: BCM2712 doesn't need to be aligned, since the size * returned by vc4_lbm_size() is in words already.
*/ if (vc4->gen == VC4_GEN_5)
lbm_size = ALIGN(lbm_size, 64); elseif (vc4->gen == VC4_GEN_4)
lbm_size = ALIGN(lbm_size, 32);
if (WARN_ON(!vc4_state->lbm_offset)) return -EINVAL;
/* Allocate the LBM memory that the HVS will use for temporary * storage due to our scaling/format conversion.
*/ if (!drm_mm_node_allocated(&vc4_state->lbm)) { int ret;
if (upm_handle &&
hvs->upm_refcounts[upm_handle].size == upm_size) { /* Allocation is the same size as the previous user of * the plane. Keep the allocation.
*/
vc4_state->upm_handle[i] = upm_handle;
} else { if (upm_handle &&
refcount_dec_and_test(&hvs->upm_refcounts[upm_handle].refcount)) {
vc4_plane_release_upm_ida(hvs, upm_handle);
vc4_state->upm_handle[i] = 0;
}
switch (vc4->gen) { default: case VC4_GEN_5: case VC4_GEN_6_C: if (!state->fb->format->has_alpha) return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED,
SCALER5_CTL2_ALPHA_MODE);
switch (state->pixel_blend_mode) { case DRM_MODE_BLEND_PIXEL_NONE: return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED,
SCALER5_CTL2_ALPHA_MODE); default: case DRM_MODE_BLEND_PREMULTI: return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_PIPELINE,
SCALER5_CTL2_ALPHA_MODE) |
SCALER5_CTL2_ALPHA_PREMULT; case DRM_MODE_BLEND_COVERAGE: return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_PIPELINE,
SCALER5_CTL2_ALPHA_MODE);
} case VC4_GEN_6_D: /* 2712-D configures fixed alpha mode in CTL0 */ return state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI ?
SCALER5_CTL2_ALPHA_PREMULT : 0;
}
}
/* SCL1 is used for Cb/Cr scaling of planar formats. For RGB * and 4:4:4, scl1 should be set to scl0 so both channels of * the scaler do the same thing. For YUV, the Y plane needs * to be put in channel 1 and Cb/Cr in channel 0, so we swap * the scl fields here.
*/ if (num_planes == 1) {
scl0 = vc4_get_scl_field(state, 0);
scl1 = scl0;
} else {
scl0 = vc4_get_scl_field(state, 1);
scl1 = vc4_get_scl_field(state, 0);
}
/* We must point to the last line when Y reflection is enabled. */
src_y = vc4_state->src_y >> 16; if (rotation & DRM_MODE_REFLECT_Y)
src_y += height - 1;
/* Adjust the base pointer to the first pixel to be scanned * out.
*/ for (i = 0; i < num_planes; i++) {
offsets[i] += src_y / (i ? v_subsample : 1) * fb->pitches[i];
offsets[i] += src_x / (i ? h_subsample : 1) * fb->format->cpp[i];
}
break;
case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: {
u32 tile_size_shift = 12; /* T tiles are 4kb */ /* Whole-tile offsets, mostly for setting the pitch. */
u32 tile_w_shift = fb->format->cpp[0] == 2 ? 6 : 5;
u32 tile_h_shift = 5; /* 16 and 32bpp are 32 pixels high */
u32 tile_w_mask = (1 << tile_w_shift) - 1; /* The height mask on 32-bit-per-pixel tiles is 63, i.e. twice * the height (in pixels) of a 4k tile.
*/
u32 tile_h_mask = (2 << tile_h_shift) - 1; /* For T-tiled, the FB pitch is "how many bytes from one row to * the next, such that * * pitch * tile_h == tile_size * tiles_per_row
*/
u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift);
u32 tiles_l = src_x >> tile_w_shift;
u32 tiles_r = tiles_w - tiles_l;
u32 tiles_t = src_y >> tile_h_shift; /* Intra-tile offsets, which modify the base address (the * SCALER_PITCH0_TILE_Y_OFFSET tells HVS how to walk from that * base address).
*/
u32 tile_y = (src_y >> 4) & 1;
u32 subtile_y = (src_y >> 2) & 3;
u32 utile_y = src_y & 3;
u32 x_off = src_x & tile_w_mask;
u32 y_off = src_y & tile_h_mask;
/* When Y reflection is requested we must set the * SCALER_PITCH0_TILE_LINE_DIR flag to tell HVS that all lines * after the initial one should be fetched in descending order, * which makes sense since we start from the last line and go * backward. * Don't know why we need y_off = max_y_off - y_off, but it's * definitely required (I guess it's also related to the "going * backward" situation).
*/ if (rotation & DRM_MODE_REFLECT_Y) {
y_off = tile_h_mask - y_off;
pitch0 = SCALER_PITCH0_TILE_LINE_DIR;
} else {
pitch0 = 0;
}
case DRM_FORMAT_MOD_BROADCOM_SAND64: case DRM_FORMAT_MOD_BROADCOM_SAND128: case DRM_FORMAT_MOD_BROADCOM_SAND256: {
uint32_t param = fourcc_mod_broadcom_param(fb->modifier);
if (param > SCALER_TILE_HEIGHT_MASK) {
DRM_DEBUG_KMS("SAND height too large (%d)\n",
param); return -EINVAL;
}
switch (base_format_mod) { case DRM_FORMAT_MOD_BROADCOM_SAND64:
tiling = SCALER_CTL0_TILING_64B; break; case DRM_FORMAT_MOD_BROADCOM_SAND128:
tiling = SCALER_CTL0_TILING_128B; break; case DRM_FORMAT_MOD_BROADCOM_SAND256:
tiling = SCALER_CTL0_TILING_256B_OR_T; break; default: return -EINVAL;
}
}
/* Adjust the base pointer to the first pixel to be scanned * out. * * For P030, y_ptr [31:4] is the 128bit word for the start pixel * y_ptr [3:0] is the pixel (0-11) contained within that 128bit * word that should be taken as the first pixel. * Ditto uv_ptr [31:4] vs [3:0], however [3:0] contains the * element within the 128bit word, eg for pixel 3 the value * should be 6.
*/ for (i = 0; i < num_planes; i++) {
u32 tile_w, tile, x_off, pix_per_tile;
if (fb->format->format == DRM_FORMAT_P030) { /* * Spec says: bits [31:4] of the given address * should point to the 128-bit word containing * the desired starting pixel, and bits[3:0] * should be between 0 and 11, indicating which * of the 12-pixels in that 128-bit word is the * first pixel to be used
*/
u32 remaining_pixels = src_x % 96;
u32 aligned = remaining_pixels / 12;
u32 last_bits = remaining_pixels % 12;
default:
DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx",
(longlong)fb->modifier); return -EINVAL;
}
/* fetch an extra pixel if we don't actually line up with the left edge. */ if ((vc4_state->src_x & 0xffff) && vc4_state->src_x < (state->fb->width << 16))
width++;
/* same for the right side */ if (((vc4_state->src_x + vc4_state->src_w[0]) & 0xffff) &&
vc4_state->src_x + vc4_state->src_w[0] < (state->fb->width << 16))
width++;
/* now for the top */ if ((vc4_state->src_y & 0xffff) && vc4_state->src_y < (state->fb->height << 16))
height++;
/* and the bottom */ if (((vc4_state->src_y + vc4_state->src_h[0]) & 0xffff) &&
vc4_state->src_y + vc4_state->src_h[0] < (state->fb->height << 16))
height++;
/* For YUV444 the hardware wants double the width, otherwise it doesn't * fetch full width of chroma
*/ if (format->drm == DRM_FORMAT_YUV444 || format->drm == DRM_FORMAT_YVU444)
width <<= 1;
/* Don't waste cycles mixing with plane alpha if the set alpha * is opaque or there is no per-pixel alpha information. * In any case we use the alpha property value as the fixed alpha.
*/
mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE &&
fb->format->has_alpha;
if (vc4_state->x_scaling[0] != VC4_SCALING_NONE ||
vc4_state->x_scaling[1] != VC4_SCALING_NONE ||
vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
vc4_state->y_scaling[1] != VC4_SCALING_NONE) { /* Reserve a slot for the LBM Base Address. The real value will * be set when calling vc4_plane_allocate_lbm().
*/ if (vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
vc4_state->lbm_offset = vc4_state->dlist_count;
vc4_dlist_counter_increment(vc4_state);
}
if (num_planes > 1) { /* Emit Cb/Cr as channel 0 and Y as channel * 1. This matches how we set up scl0/scl1 * above.
*/
vc4_write_scaling_parameters(state, 1);
}
vc4_write_scaling_parameters(state, 0);
/* If any PPF setup was done, then all the kernel * pointers get uploaded.
*/ if (vc4_state->x_scaling[0] == VC4_SCALING_PPF ||
vc4_state->y_scaling[0] == VC4_SCALING_PPF ||
vc4_state->x_scaling[1] == VC4_SCALING_PPF ||
vc4_state->y_scaling[1] == VC4_SCALING_PPF) {
u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start,
SCALER_PPF_KERNEL_OFFSET);
/* crtc_* are already clipped coordinates. */
covers_screen = vc4_state->crtc_x == 0 && vc4_state->crtc_y == 0 &&
vc4_state->crtc_w == state->crtc->mode.hdisplay &&
vc4_state->crtc_h == state->crtc->mode.vdisplay; /* Background fill might be necessary when the plane has per-pixel * alpha content or a non-opaque plane alpha and could blend from the * background or does not cover the entire screen.
*/
vc4_state->needs_bg_fill = fb->format->has_alpha || !covers_screen ||
state->alpha != DRM_BLEND_ALPHA_OPAQUE;
/* Flag the dlist as initialized to avoid checking it twice in case * the async update check already called vc4_plane_mode_set() and * decided to fallback to sync update because async update was not * possible.
*/
vc4_state->dlist_initialized = 1;
/* CSC pre-loaded with: * 0 = BT601 limited range * 1 = BT709 limited range * 2 = BT2020 limited range * 3 = BT601 full range * 4 = BT709 full range * 5 = BT2020 full range
*/ if (color_encoding > DRM_COLOR_YCBCR_BT2020)
color_encoding = DRM_COLOR_YCBCR_BT601; if (color_range > DRM_COLOR_YCBCR_FULL_RANGE)
color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
if (vc4->gen == VC4_GEN_6_C) {
ret |= SCALER6C_CTL2_CSC_ENABLE;
ret |= VC4_SET_FIELD(color_encoding + (color_range * 3),
SCALER6C_CTL2_BRCM_CFC_CONTROL);
} else {
ret |= SCALER6D_CTL2_CSC_ENABLE;
ret |= VC4_SET_FIELD(color_encoding + (color_range * 3),
SCALER6D_CTL2_BRCM_CFC_CONTROL);
}
}
/* SCL1 is used for Cb/Cr scaling of planar formats. For RGB * and 4:4:4, scl1 should be set to scl0 so both channels of * the scaler do the same thing. For YUV, the Y plane needs * to be put in channel 1 and Cb/Cr in channel 0, so we swap * the scl fields here.
*/ if (num_planes == 1) {
scl0 = vc4_get_scl_field(state, 0);
scl1 = scl0;
} else {
scl0 = vc4_get_scl_field(state, 1);
scl1 = vc4_get_scl_field(state, 0);
}
/* We must point to the last line when Y reflection is enabled. */
src_y = vc4_state->src_y >> 16; if (rotation & DRM_MODE_REFLECT_Y)
src_y += height - 1;
src_x = vc4_state->src_x >> 16;
switch (base_format_mod) { case DRM_FORMAT_MOD_LINEAR:
tiling = SCALER6_CTL0_ADDR_MODE_LINEAR;
/* Adjust the base pointer to the first pixel to be scanned * out.
*/ for (i = 0; i < num_planes; i++) {
offsets[i] += src_y / (i ? v_subsample : 1) * fb->pitches[i];
offsets[i] += src_x / (i ? h_subsample : 1) * fb->format->cpp[i];
}
break;
case DRM_FORMAT_MOD_BROADCOM_SAND128: case DRM_FORMAT_MOD_BROADCOM_SAND256: {
uint32_t param = fourcc_mod_broadcom_param(fb->modifier);
u32 components_per_word;
u32 starting_offset;
u32 fetch_count;
if (param > SCALER_TILE_HEIGHT_MASK) {
DRM_DEBUG_KMS("SAND height too large (%d)\n",
param); return -EINVAL;
}
switch (base_format_mod) { case DRM_FORMAT_MOD_BROADCOM_SAND128:
tiling = SCALER6_CTL0_ADDR_MODE_128B; break; case DRM_FORMAT_MOD_BROADCOM_SAND256:
tiling = SCALER6_CTL0_ADDR_MODE_256B; break; default: return -EINVAL;
}
}
/* Adjust the base pointer to the first pixel to be scanned * out. * * For P030, y_ptr [31:4] is the 128bit word for the start pixel * y_ptr [3:0] is the pixel (0-11) contained within that 128bit * word that should be taken as the first pixel. * Ditto uv_ptr [31:4] vs [3:0], however [3:0] contains the * element within the 128bit word, eg for pixel 3 the value * should be 6.
*/ for (i = 0; i < num_planes; i++) {
u32 tile_w, tile, x_off, pix_per_tile;
if (fb->format->format == DRM_FORMAT_P030) { /* * Spec says: bits [31:4] of the given address * should point to the 128-bit word containing * the desired starting pixel, and bits[3:0] * should be between 0 and 11, indicating which * of the 12-pixels in that 128-bit word is the * first pixel to be used
*/
u32 remaining_pixels = src_x % 96;
u32 aligned = remaining_pixels / 12;
u32 last_bits = remaining_pixels % 12;
default:
DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx",
(longlong)fb->modifier); return -EINVAL;
}
/* fetch an extra pixel if we don't actually line up with the left edge. */ if ((vc4_state->src_x & 0xffff) && vc4_state->src_x < (state->fb->width << 16))
width++;
/* same for the right side */ if (((vc4_state->src_x + vc4_state->src_w[0]) & 0xffff) &&
vc4_state->src_x + vc4_state->src_w[0] < (state->fb->width << 16))
width++;
/* now for the top */ if ((vc4_state->src_y & 0xffff) && vc4_state->src_y < (state->fb->height << 16))
height++;
/* and the bottom */ if (((vc4_state->src_y + vc4_state->src_h[0]) & 0xffff) &&
vc4_state->src_y + vc4_state->src_h[0] < (state->fb->height << 16))
height++;
/* for YUV444 hardware wants double the width, otherwise it doesn't * fetch full width of chroma
*/ if (format->drm == DRM_FORMAT_YUV444 || format->drm == DRM_FORMAT_YVU444)
width <<= 1;
/* Don't waste cycles mixing with plane alpha if the set alpha * is opaque or there is no per-pixel alpha information. * In any case we use the alpha property value as the fixed alpha.
*/
mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE &&
fb->format->has_alpha;
/* * Palette Word 0 * TODO: We're not using the palette mode
*/
/* * Trans Word 0 * TODO: It's only relevant if we set the trans_rgb bit in the * control word 0, and we don't at the moment.
*/
vc4_state->lbm_offset = 0;
if (!vc4_state->is_unity || fb->format->is_yuv) { /* * Reserve a slot for the LBM Base Address. The real value will * be set when calling vc4_plane_allocate_lbm().
*/ if (vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
vc4_state->lbm_offset = vc4_state->dlist_count;
vc4_dlist_counter_increment(vc4_state);
}
if (vc4_state->x_scaling[0] != VC4_SCALING_NONE ||
vc4_state->x_scaling[1] != VC4_SCALING_NONE ||
vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
vc4_state->y_scaling[1] != VC4_SCALING_NONE) { if (num_planes > 1) /* * Emit Cb/Cr as channel 0 and Y as channel * 1. This matches how we set up scl0/scl1 * above.
*/
vc4_write_scaling_parameters(state, 1);
vc4_write_scaling_parameters(state, 0);
}
/* * If any PPF setup was done, then all the kernel * pointers get uploaded.
*/ if (vc4_state->x_scaling[0] == VC4_SCALING_PPF ||
vc4_state->y_scaling[0] == VC4_SCALING_PPF ||
vc4_state->x_scaling[1] == VC4_SCALING_PPF ||
vc4_state->y_scaling[1] == VC4_SCALING_PPF) {
u32 kernel =
VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start,
SCALER_PPF_KERNEL_OFFSET);
/* * Background fill might be necessary when the plane has per-pixel * alpha content or a non-opaque plane alpha and could blend from the * background or does not cover the entire screen.
*/
vc4_state->needs_bg_fill = fb->format->has_alpha || !covers_screen ||
state->alpha != DRM_BLEND_ALPHA_OPAQUE;
/* * Flag the dlist as initialized to avoid checking it twice in case * the async update check already called vc4_plane_mode_set() and * decided to fallback to sync update because async update was not * possible.
*/
vc4_state->dlist_initialized = 1;
/* If a modeset involves changing the setup of a plane, the atomic * infrastructure will call this to validate a proposed plane setup. * However, if a plane isn't getting updated, this (and the * corresponding vc4_plane_atomic_update) won't get called. Thus, we * compute the dlist here and have all active plane dlists get updated * in the CRTC's flush.
*/ staticint vc4_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state)
{ struct vc4_dev *vc4 = to_vc4_dev(plane->dev); struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane); struct vc4_plane_state *vc4_state = to_vc4_plane_state(new_plane_state); int ret;
vc4_state->dlist_count = 0;
if (!plane_enabled(new_plane_state)) { struct drm_plane_state *old_plane_state =
drm_atomic_get_old_plane_state(state, plane);
if (vc4->gen >= VC4_GEN_6_C)
ret = vc6_plane_mode_set(plane, new_plane_state); else
ret = vc4_plane_mode_set(plane, new_plane_state); if (ret) return ret;
if (!vc4_state->src_w[0] || !vc4_state->src_h[0] ||
!vc4_state->crtc_w || !vc4_state->crtc_h) return 0;
ret = vc4_plane_allocate_lbm(new_plane_state); if (ret) return ret;
if (vc4->gen >= VC4_GEN_6_C) {
ret = vc6_plane_allocate_upm(new_plane_state); if (ret) return ret;
}
return 0;
}
staticvoid vc4_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state)
{ /* No contents here. Since we don't know where in the CRTC's * dlist we should be stored, our dlist is uploaded to the * hardware with vc4_plane_write_dlist() at CRTC atomic_flush * time.
*/
}
u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
{ struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); int i; int idx;
if (!drm_dev_enter(plane->dev, &idx)) goto out;
vc4_state->hw_dlist = dlist;
/* Can't memcpy_toio() because it needs to be 32-bit writes. */ for (i = 0; i < vc4_state->dlist_count; i++)
writel(vc4_state->dlist[i], &dlist[i]);
/* Updates the plane to immediately (well, once the FIFO needs * refilling) scan out from at a new framebuffer.
*/ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
{ struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state); struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, 0); struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
dma_addr_t dma_addr = bo->dma_addr + fb->offsets[0]; int idx;
if (!drm_dev_enter(plane->dev, &idx)) return;
/* We're skipping the address adjustment for negative origin, * because this is only called on the primary plane.
*/
WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0);
if (vc4->gen == VC4_GEN_6_C) {
u32 value;
value = vc4_state->dlist[vc4_state->ptr0_offset[0]] &
~SCALER6_PTR0_UPPER_ADDR_MASK;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.33 Sekunden
(vorverarbeitet)
¤
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.