struct dpu_plane *dplane;
struct dpu_plane_res *res;
struct dpu_fetchdecode *fd;
+ struct dpu_hscaler *hs;
+ struct dpu_vscaler *vs;
struct dpu_layerblend *lb;
struct dpu_extdst *ed;
int fd_id, lb_id;
fd = res->fd[fd_id];
lb = res->lb[lb_id];
+ hs = fetchdecode_get_hscaler(fd);
+ vs = fetchdecode_get_vscaler(fd);
+
layerblend_pixengcfg_clken(lb, CLKEN__DISABLE);
fetchdecode_layerproperty(fd, false);
+ hscaler_pixengcfg_clken(hs, CLKEN__DISABLE);
+ vscaler_pixengcfg_clken(vs, CLKEN__DISABLE);
+ hscaler_mode(hs, SCALER_NEUTRAL);
+ vscaler_mode(vs, SCALER_NEUTRAL);
if (old_dpstate->is_top) {
ed = res->ed[dplane->stream_id];
extdst_pixengcfg_src_sel(ed, ED_SRC_DISABLE);
fetchdecode_set_stream_id(res->fd[i],
DPU_PLANE_SRC_DISABLED);
}
+
+ for (i = 0; i < ARRAY_SIZE(res->hs); i++) {
+ if (res->hs[i] && !hscaler_is_enabled(res->hs[i]))
+ hscaler_set_stream_id(res->hs[i],
+ DPU_PLANE_SRC_DISABLED);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(res->vs); i++) {
+ if (res->vs[i] && !vscaler_is_enabled(res->vs[i]))
+ vscaler_set_stream_id(res->vs[i],
+ DPU_PLANE_SRC_DISABLED);
+ }
}
static void dpu_crtc_mode_set_nofb(struct drm_crtc *crtc)
struct dpu_plane *dplane;
struct dpu_plane_grp *grp;
struct dpu_fetchdecode *fd;
+ struct dpu_hscaler *hs;
+ struct dpu_vscaler *vs;
unsigned int sid, src_sid;
int i, j, k;
int fd_id;
+ u32 cap_mask, hs_mask, vs_mask;
/* for active planes only */
for (i = 0; i < n; i++) {
if (src_sid && src_sid != BIT(sid))
continue;
+ cap_mask = fetchdecode_get_vproc_mask(fd);
+
+ if (states[i]->src_w >> 16 != states[i]->crtc_w) {
+ hs = fetchdecode_get_hscaler(fd);
+
+ /* avoid on-the-fly/hot migration */
+ src_sid = hscaler_get_stream_id(hs);
+ if (src_sid && src_sid != BIT(sid))
+ continue;
+
+ /* fetch unit has the hscale capability? */
+ if (!dpu_vproc_has_hscale_cap(cap_mask))
+ continue;
+
+ hs_mask = dpu_vproc_get_hscale_cap(cap_mask);
+
+ /* hscaler available? */
+ if (grp->src_use_vproc_mask & hs_mask)
+ continue;
+
+ grp->src_use_vproc_mask |= hs_mask;
+ }
+
+ if (states[i]->src_h >> 16 != states[i]->crtc_h) {
+ vs = fetchdecode_get_vscaler(fd);
+
+ /* avoid on-the-fly/hot migration */
+ src_sid = vscaler_get_stream_id(vs);
+ if (src_sid && src_sid != BIT(sid))
+ continue;
+
+ /* fetch unit has the vscale capability? */
+ if (!dpu_vproc_has_vscale_cap(cap_mask))
+ continue;
+
+ vs_mask = dpu_vproc_get_vscale_cap(cap_mask);
+
+ /* vscaler available? */
+ if (grp->src_use_vproc_mask & vs_mask)
+ continue;
+
+ grp->src_use_vproc_mask |= vs_mask;
+ }
+
grp->src_mask |= BIT(k);
break;
}
struct drm_crtc_state *crtc_state;
struct drm_plane *plane;
struct dpu_plane *dpu_plane;
+ const struct drm_plane_state *plane_state;
struct dpu_plane_grp *grp[MAX_DPU_PLANE_GRP];
- int ret, i, grp_id, active_plane[MAX_DPU_PLANE_GRP];
+ int ret, i, grp_id;
+ int active_plane[MAX_DPU_PLANE_GRP];
+ int active_plane_hscale[MAX_DPU_PLANE_GRP];
+ int active_plane_vscale[MAX_DPU_PLANE_GRP];
for (i = 0; i < MAX_DPU_PLANE_GRP; i++) {
active_plane[i] = 0;
+ active_plane_hscale[i] = 0;
+ active_plane_vscale[i] = 0;
grp[i] = NULL;
}
if (ret)
return ret;
- drm_atomic_crtc_state_for_each_plane(plane, crtc_state) {
+ drm_atomic_crtc_state_for_each_plane_state(plane, plane_state,
+ crtc_state) {
dpu_plane = to_dpu_plane(plane);
grp_id = dpu_plane->grp->id;
active_plane[grp_id]++;
+ if (plane_state->src_w >> 16 != plane_state->crtc_w)
+ active_plane_hscale[grp_id]++;
+
+ if (plane_state->src_h >> 16 != plane_state->crtc_h)
+ active_plane_vscale[grp_id]++;
+
if (grp[grp_id] == NULL)
grp[grp_id] = dpu_plane->grp;
}
}
- for (i = 0; i < MAX_DPU_PLANE_GRP; i++)
- if (grp[i] && active_plane[i] > grp[i]->hw_plane_num)
- return -EINVAL;
+ /* enough resources? */
+ for (i = 0; i < MAX_DPU_PLANE_GRP; i++) {
+ if (grp[i]) {
+ if (active_plane[i] > grp[i]->hw_plane_num)
+ return -EINVAL;
- /* clear source mask */
+ if (active_plane_hscale[i] >
+ grp[i]->hw_plane_hscaler_num)
+ return -EINVAL;
+
+ if (active_plane_vscale[i] >
+ grp[i]->hw_plane_vscaler_num)
+ return -EINVAL;
+ }
+ }
+
+ /* clear resource mask */
for (i = 0; i < MAX_DPU_PLANE_GRP; i++) {
- if (grp[i])
+ if (grp[i]) {
grp[i]->src_mask = 0;
+ grp[i]->src_use_vproc_mask = 0;
+ }
}
ret = drm_atomic_helper_check_modeset(dev, state);
static int dpu_plane_atomic_check(struct drm_plane *plane,
struct drm_plane_state *state)
{
+ struct dpu_plane *dplane = to_dpu_plane(plane);
struct dpu_plane_state *dpstate = to_dpu_plane_state(state);
struct drm_crtc_state *crtc_state;
struct drm_framebuffer *fb = state->fb;
return 0;
}
- /* no scaling */
- if (state->src_w >> 16 != state->crtc_w ||
- state->src_h >> 16 != state->crtc_h)
- return -EINVAL;
+ if (dplane->grp->has_vproc) {
+ /* no down scaling */
+ if (state->src_w >> 16 > state->crtc_w ||
+ state->src_h >> 16 > state->crtc_h)
+ return -EINVAL;
+ } else {
+ /* no scaling */
+ if (state->src_w >> 16 != state->crtc_w ||
+ state->src_h >> 16 != state->crtc_h)
+ return -EINVAL;
+ }
/* no off screen */
if (state->crtc_x < 0 || state->crtc_y < 0)
struct drm_framebuffer *fb = state->fb;
struct dpu_plane_res *res = &dplane->grp->res;
struct dpu_fetchdecode *fd;
+ struct dpu_hscaler *hs;
+ struct dpu_vscaler *vs;
struct dpu_layerblend *lb;
struct dpu_constframe *cf;
struct dpu_extdst *ed;
struct device *dev = plane->dev->dev;
+ dpu_block_id_t vs_id = ID_NONE, hs_id;
+ lb_sec_sel_t lb_src = dpstate->source;
unsigned int depth, src_w, src_h;
int bpp, fd_id, lb_id;
+ bool need_hscaler = false, need_vscaler = false;
/*
* Do nothing since the plane is disabled by
src_w = state->src_w >> 16;
src_h = state->src_h >> 16;
+ if (src_w != state->crtc_w) {
+ need_hscaler = true;
+ hs = fetchdecode_get_hscaler(fd);
+ if (IS_ERR(hs))
+ return;
+ }
+
+ if (src_h != state->crtc_h) {
+ need_vscaler = true;
+ vs = fetchdecode_get_vscaler(fd);
+ if (IS_ERR(vs))
+ return;
+ }
+
drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp);
fetchdecode_source_bpp(fd, bpp);
DPU_PLANE_SRC_TO_DISP_STREAM1 :
DPU_PLANE_SRC_TO_DISP_STREAM0);
+ /* vscaler comes first */
+ if (need_vscaler) {
+ vs_id = vscaler_get_block_id(vs);
+ if (vs_id == ID_NONE)
+ return;
+
+ vscaler_pixengcfg_dynamic_src_sel(vs,
+ (vs_src_sel_t)(dpstate->source));
+ vscaler_pixengcfg_clken(vs, CLKEN__AUTOMATIC);
+ vscaler_setup1(vs, src_h, state->crtc_h);
+ vscaler_output_size(vs, state->crtc_h);
+ vscaler_field_mode(vs, SCALER_INPUT);
+ vscaler_filter_mode(vs, SCALER_LINEAR);
+ vscaler_scale_mode(vs, SCLAER_UPSCALE);
+ vscaler_mode(vs, SCALER_ACTIVE);
+ vscaler_set_stream_id(vs, dplane->stream_id ?
+ DPU_PLANE_SRC_TO_DISP_STREAM1 :
+ DPU_PLANE_SRC_TO_DISP_STREAM0);
+
+ lb_src = (lb_sec_sel_t)vs_id;
+
+ dev_dbg(dev, "[PLANE:%d:%s] vscaler-0x%02x\n",
+ plane->base.id, plane->name, vs_id);
+ }
+
+ /* and then, hscaler */
+ if (need_hscaler) {
+ hs_id = hscaler_get_block_id(hs);
+ if (hs_id == ID_NONE)
+ return;
+
+ hscaler_pixengcfg_dynamic_src_sel(hs, need_vscaler ?
+ (hs_src_sel_t)(vs_id) :
+ (hs_src_sel_t)(dpstate->source));
+ hscaler_pixengcfg_clken(hs, CLKEN__AUTOMATIC);
+ hscaler_setup1(hs, src_w, state->crtc_w);
+ hscaler_output_size(hs, state->crtc_w);
+ hscaler_filter_mode(hs, SCALER_LINEAR);
+ hscaler_scale_mode(hs, SCLAER_UPSCALE);
+ hscaler_mode(hs, SCALER_ACTIVE);
+ hscaler_set_stream_id(hs, dplane->stream_id ?
+ DPU_PLANE_SRC_TO_DISP_STREAM1 :
+ DPU_PLANE_SRC_TO_DISP_STREAM0);
+
+ lb_src = (lb_sec_sel_t)hs_id;
+
+ dev_dbg(dev, "[PLANE:%d:%s] hscaler-0x%02x\n",
+ plane->base.id, plane->name, hs_id);
+ }
+
layerblend_pixengcfg_dynamic_prim_sel(lb, dpstate->stage);
- layerblend_pixengcfg_dynamic_sec_sel(lb, dpstate->source);
+ layerblend_pixengcfg_dynamic_sec_sel(lb, lb_src);
layerblend_control(lb, LB_BLEND);
layerblend_pixengcfg_clken(lb, CLKEN__AUTOMATIC);
layerblend_position(lb, dpstate->layer_x, dpstate->layer_y);