/* * The hardware phase 0.0 refers to the center of the pixel. * We want to start from the top/left edge which is phase * -0.5. That matches how the hardware calculates the scaling * factors (from top-left of the first pixel to bottom-right * of the last pixel, as opposed to the pixel centers). * * For 4:2:0 subsampled chroma planes we obviously have to * adjust that so that the chroma sample position lands in * the right spot. * * Note that for packed YCbCr 4:2:2 formats there is no way to * control chroma siting. The hardware simply replicates the * chroma samples for both of the luma samples, and thus we don't * actually get the expected MPEG2 chroma siting convention :( * The same behaviour is observed on pre-SKL platforms as well. * * Theory behind the formula (note that we ignore sub-pixel * source coordinates): * s = source sample position * d = destination sample position * * Downscaling 4:1: * -0.5 * | 0.0 * | | 1.5 (initial phase) * | | | * v v v * | s | s | s | s | * | d | * * Upscaling 1:4: * -0.5 * | -0.375 (initial phase) * | | 0.0 * | | | * v v v * | s | * | d | d | d | d |
*/ static u16 skl_scaler_calc_phase(int sub, int scale, bool chroma_cosited)
{ int phase = -0x8000;
u16 trip = 0;
/* * Hardware initial phase limited to [-0.5:1.5]. * Since the max hardware scale factor is 3.0, we * should never actually exceed 1.0 here.
*/
WARN_ON(phase < -0x8000 || phase > 0x18000);
staticint
skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach, unsignedint scaler_user, int *scaler_id, int src_w, int src_h, int dst_w, int dst_h, conststruct drm_format_info *format,
u64 modifier, bool need_scaler)
{ struct intel_display *display = to_intel_display(crtc_state); struct intel_crtc_scaler_state *scaler_state =
&crtc_state->scaler_state; struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); conststruct drm_display_mode *adjusted_mode =
&crtc_state->hw.adjusted_mode; int pipe_src_w = drm_rect_width(&crtc_state->pipe_src); int pipe_src_h = drm_rect_height(&crtc_state->pipe_src); int min_src_w, min_src_h, min_dst_w, min_dst_h; int max_src_w, max_src_h, max_dst_w, max_dst_h;
/* * Src coordinates are already rotated by 270 degrees for * the 90/270 degree plane rotation cases (to match the * GTT mapping), hence no need to account for rotation here.
*/ if (src_w != dst_w || src_h != dst_h)
need_scaler = true;
/* * Scaling/fitting not supported in IF-ID mode in GEN9+ * TODO: Interlace fetch mode doesn't support YUV420 planar formats. * Once NV12 is enabled, handle it here while allocating scaler * for NV12.
*/ if (DISPLAY_VER(display) >= 9 && crtc_state->hw.enable &&
need_scaler && adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
drm_dbg_kms(display->drm, "[CRTC:%d:%s] scaling not supported with IF-ID mode\n",
crtc->base.base.id, crtc->base.name); return -EINVAL;
}
/* * if plane is being disabled or scaler is no more required or force detach * - free scaler binded to this plane/crtc * - in order to do this, update crtc->scaler_usage * * Here scaler state in crtc_state is set free so that * scaler can be assigned to other user. Actual register * update to free the scaler is done in plane/panel-fit programming. * For this purpose crtc/plane_state->scaler_id isn't reset here.
*/ if (force_detach || !need_scaler) { if (*scaler_id >= 0) {
scaler_state->scaler_users &= ~(1 << scaler_user);
scaler_state->scalers[*scaler_id].in_use = false;
/* * The pipe scaler does not use all the bits of PIPESRC, at least * on the earlier platforms. So even when we're scaling a plane * the *pipe* source size must not be too large. For simplicity * we assume the limits match the scaler destination size limits. * Might not be 100% accurate on all platforms, but good enough for * now.
*/ if (pipe_src_w > max_dst_w || pipe_src_h > max_dst_h) {
drm_dbg_kms(display->drm, "[CRTC:%d:%s] scaler_user index %u.%u: pipe src size %ux%u " "is out of scaler range\n",
crtc->base.base.id, crtc->base.name,
crtc->pipe, scaler_user, pipe_src_w, pipe_src_h); return -EINVAL;
}
/* mark this plane as a scaler user in crtc_state */
scaler_state->scaler_users |= (1 << scaler_user);
drm_dbg_kms(display->drm, "[CRTC:%d:%s] scaler_user index %u.%u: " "staged scaling request for %ux%u->%ux%u scaler_users = 0x%x\n",
crtc->base.base.id, crtc->base.name,
crtc->pipe, scaler_user, src_w, src_h, dst_w, dst_h,
scaler_state->scaler_users);
return 0;
}
int skl_update_scaler_crtc(struct intel_crtc_state *crtc_state)
{ conststruct drm_display_mode *pipe_mode = &crtc_state->hw.pipe_mode; int width, height;
/** * skl_update_scaler_plane - Stages update to scaler state for a given plane. * @crtc_state: crtc's scaler state * @plane_state: atomic plane state to update * * Return * 0 - scaler_usage updated successfully * error - requested scaling cannot be supported or other error condition
*/ int skl_update_scaler_plane(struct intel_crtc_state *crtc_state, struct intel_plane_state *plane_state)
{ struct intel_display *display = to_intel_display(plane_state); struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); struct drm_framebuffer *fb = plane_state->hw.fb; bool force_detach = !fb || !plane_state->uapi.visible; bool need_scaler = false;
/* Pre-gen11 and SDR planes always need a scaler for planar formats. */ if (!icl_is_hdr_plane(display, plane->id) &&
fb && intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier))
need_scaler = true;
staticint intel_allocate_scaler(struct intel_crtc_scaler_state *scaler_state, struct intel_crtc *crtc)
{ int i;
for (i = 0; i < crtc->num_scalers; i++) { if (scaler_state->scalers[i].in_use) continue;
scaler_state->scalers[i].in_use = true;
return i;
}
return -1;
}
staticvoid
calculate_max_scale(struct intel_crtc *crtc, bool is_yuv_semiplanar, int scaler_id, int *max_hscale, int *max_vscale)
{ struct intel_display *display = to_intel_display(crtc);
/* * FIXME: When two scalers are needed, but only one of * them needs to downscale, we should make sure that * the one that needs downscaling support is assigned * as the first scaler, so we don't reject downscaling * unnecessarily.
*/
if (DISPLAY_VER(display) >= 14) { /* * On versions 14 and up, only the first * scaler supports a vertical scaling factor * of more than 1.0, while a horizontal * scaling factor of 3.0 is supported.
*/
*max_hscale = 0x30000 - 1;
if (DISPLAY_VER(display) == 9) {
mode = SKL_PS_SCALER_MODE_NV12;
} elseif (icl_is_hdr_plane(display, plane->id)) { /* * On gen11+'s HDR planes we only use the scaler for * scaling. They have a dedicated chroma upsampler, so * we don't need the scaler to upsample the UV plane.
*/
mode = PS_SCALER_MODE_NORMAL;
} else { struct intel_plane *linked =
plane_state->planar_linked_plane;
mode = PS_SCALER_MODE_PLANAR;
if (linked)
mode |= PS_BINDING_Y_PLANE(linked->id);
}
} elseif (DISPLAY_VER(display) >= 10) {
mode = PS_SCALER_MODE_NORMAL;
} elseif (num_scalers_need == 1 && crtc->num_scalers > 1) { /* * when only 1 scaler is in use on a pipe with 2 scalers * scaler 0 operates in high quality (HQ) mode. * In this case use scaler 0 to take advantage of HQ mode
*/
scaler_state->scalers[*scaler_id].in_use = false;
*scaler_id = 0;
scaler_state->scalers[0].in_use = true;
mode = SKL_PS_SCALER_MODE_HQ;
} else {
mode = SKL_PS_SCALER_MODE_DYN;
}
/* * When configured for Pipe YUV 420 encoding for port output, * limit downscaling to less than 1.5 (source/destination) in * the horizontal direction and 1.0 in the vertical direction.
*/ if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420) {
max_hscale = 0x18000 - 1;
max_vscale = 0x10000;
}
/* * GLK+ scalers don't have a HQ mode so it * isn't necessary to change between HQ and dyn mode * on those platforms.
*/ if (!plane_state && DISPLAY_VER(display) >= 10) return 0;
plane_state = intel_atomic_get_plane_state(state, plane); if (IS_ERR(plane_state)) return PTR_ERR(plane_state);
/** * intel_atomic_setup_scalers() - setup scalers for crtc per staged requests * @state: atomic state * @crtc: crtc * * This function sets up scalers based on staged scaling requests for * a @crtc and its planes. It is called from crtc level check path. If request * is a supportable request, it attaches scalers to requested planes and crtc. * * This function takes into account the current scaler(s) in use by any planes * not being part of this atomic state * * Returns: * 0 - scalers were setup successfully * error code - otherwise
*/ int intel_atomic_setup_scalers(struct intel_atomic_state *state, struct intel_crtc *crtc)
{ struct intel_display *display = to_intel_display(crtc); struct intel_crtc_state *crtc_state =
intel_atomic_get_new_crtc_state(state, crtc); struct intel_crtc_scaler_state *scaler_state =
&crtc_state->scaler_state; int num_scalers_need; int i;
/* * High level flow: * - staged scaler requests are already in scaler_state->scaler_users * - check whether staged scaling requests can be supported * - add planes using scalers that aren't in current transaction * - assign scalers to requested users * - as part of plane commit, scalers will be committed * (i.e., either attached or detached) to respective planes in hw * - as part of crtc_commit, scaler will be either attached or detached * to crtc in hw
*/
/* fail if required scalers > available scalers */ if (num_scalers_need > crtc->num_scalers) {
drm_dbg_kms(display->drm, "[CRTC:%d:%s] too many scaling requests %d > %d\n",
crtc->base.base.id, crtc->base.name,
num_scalers_need, crtc->num_scalers); return -EINVAL;
}
/* walkthrough scaler_users bits and start assigning scalers */ for (i = 0; i < sizeof(scaler_state->scaler_users) * 8; i++) { int ret;
/* skip if scaler not required */ if (!(scaler_state->scaler_users & (1 << i))) continue;
if (i == SKL_CRTC_INDEX) {
ret = setup_crtc_scaler(state, crtc); if (ret) return ret;
} else { struct intel_plane *plane =
to_intel_plane(drm_plane_from_index(display->drm, i));
ret = setup_plane_scaler(state, crtc, plane); if (ret) return ret;
}
}
/* * This function detaches (aka. unbinds) unused scalers in hardware
*/ void skl_detach_scalers(struct intel_dsb *dsb, conststruct intel_crtc_state *crtc_state)
{ struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); conststruct intel_crtc_scaler_state *scaler_state =
&crtc_state->scaler_state; int i;
/* loop through and disable scalers that aren't in use */ for (i = 0; i < crtc->num_scalers; i++) { if (!scaler_state->scalers[i].in_use)
skl_detach_scaler(dsb, crtc, i);
}
}
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.