Loading drivers/gpu/drm/msm/msm_drv.h +42 −9 Original line number Diff line number Diff line Loading @@ -138,6 +138,7 @@ enum msm_mdp_crtc_property { CRTC_PROP_MEM_IB, CRTC_PROP_ROT_PREFILL_BW, CRTC_PROP_ROT_CLK, CRTC_PROP_ROI_V1, /* total # of properties */ CRTC_PROP_COUNT Loading @@ -158,6 +159,7 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_DST_Y, CONNECTOR_PROP_DST_W, CONNECTOR_PROP_DST_H, CONNECTOR_PROP_ROI_V1, /* enum/bitmask properties */ CONNECTOR_PROP_TOPOLOGY_NAME, Loading Loading @@ -199,6 +201,38 @@ enum msm_display_caps { MSM_DISPLAY_CAP_EDID = BIT(3), }; /** * struct msm_roi_alignment - region of interest alignment restrictions * @xstart_pix_align: left x offset alignment restriction * @width_pix_align: width alignment restriction * @ystart_pix_align: top y offset alignment restriction * @height_pix_align: height alignment restriction * @min_width: minimum width restriction * @min_height: minimum height restriction */ struct msm_roi_alignment { uint32_t xstart_pix_align; uint32_t width_pix_align; uint32_t ystart_pix_align; uint32_t height_pix_align; uint32_t min_width; uint32_t min_height; }; /** * struct msm_roi_caps - display's region of interest capabilities * @enabled: true if some region of interest is supported * @merge_rois: merge rois before sending to display * @num_roi: maximum number of rois supported * @align: roi alignment restrictions */ struct msm_roi_caps { bool enabled; bool merge_rois; uint32_t num_roi; struct msm_roi_alignment align; }; /** * struct msm_display_dsc_info - defines dsc configuration * @version: DSC version. Loading Loading @@ -338,6 +372,7 @@ struct msm_compression_info { * @vtotal: display vertical total * @jitter: display jitter configuration * @comp_info: Compression supported by the display * @roi_caps: Region of interest capability info */ struct msm_display_info { int intf_type; Loading @@ -361,21 +396,19 @@ struct msm_display_info { uint32_t jitter; struct msm_compression_info comp_info; struct msm_roi_caps roi_caps; }; #define MSM_MAX_ROI 4 /** * struct msm_roi_mapping - Regions of interest structure for mapping CRTC to * Connector output * @num_rects: number of valid rectangles in src and dst arrays * @src: source roi rectangle * @dst: destination roi rectangle * struct msm_roi_list - list of regions of interest for a drm object * @num_rects: number of valid rectangles in the roi array * @roi: list of roi rectangles */ struct msm_roi_mapping { struct msm_roi_list { uint32_t num_rects; struct drm_clip_rect src[MSM_MAX_ROI]; struct drm_clip_rect dst[MSM_MAX_ROI]; struct drm_clip_rect roi[MSM_MAX_ROI]; }; /** Loading @@ -383,7 +416,7 @@ struct msm_roi_mapping { * @rois: Regions of interest structure for mapping CRTC to Connector output */ struct msm_display_kickoff_params { struct msm_roi_mapping *rois; struct msm_roi_list *rois; }; /** Loading drivers/gpu/drm/msm/sde/sde_connector.c +136 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,12 @@ #define BL_NODE_NAME_SIZE 32 #define SDE_DEBUG_CONN(c, fmt, ...) SDE_DEBUG("conn%d " fmt,\ (c) ? (c)->base.base.id : -1, ##__VA_ARGS__) #define SDE_ERROR_CONN(c, fmt, ...) SDE_ERROR("conn%d " fmt,\ (c) ? (c)->base.base.id : -1, ##__VA_ARGS__) static const struct drm_prop_enum_list e_topology_name[] = { {SDE_RM_TOPOLOGY_UNKNOWN, "sde_unknown"}, {SDE_RM_TOPOLOGY_SINGLEPIPE, "sde_singlepipe"}, Loading Loading @@ -416,6 +422,122 @@ sde_connector_atomic_duplicate_state(struct drm_connector *connector) return &c_state->base; } static int _sde_connector_roi_v1_check_roi( struct sde_connector *c_conn, struct drm_clip_rect *roi_conn, const struct msm_roi_caps *caps) { const struct msm_roi_alignment *align = &caps->align; int w = roi_conn->x2 - roi_conn->x1; int h = roi_conn->y2 - roi_conn->y1; if (w <= 0 || h <= 0) { SDE_ERROR_CONN(c_conn, "invalid conn roi w %d h %d\n", w, h); return -EINVAL; } if (w < align->min_width || w % align->width_pix_align) { SDE_ERROR_CONN(c_conn, "invalid conn roi width %d min %d align %d\n", w, align->min_width, align->width_pix_align); return -EINVAL; } if (h < align->min_height || h % align->height_pix_align) { SDE_ERROR_CONN(c_conn, "invalid conn roi height %d min %d align %d\n", h, align->min_height, align->height_pix_align); return -EINVAL; } if (roi_conn->x1 % align->xstart_pix_align) { SDE_ERROR_CONN(c_conn, "invalid conn roi x1 %d align %d\n", roi_conn->x1, align->xstart_pix_align); return -EINVAL; } if (roi_conn->y1 % align->ystart_pix_align) { SDE_ERROR_CONN(c_conn, "invalid conn roi y1 %d align %d\n", roi_conn->y1, align->ystart_pix_align); return -EINVAL; } return 0; } static int _sde_connector_set_roi_v1( struct sde_connector *c_conn, struct sde_connector_state *c_state, void *usr_ptr) { struct sde_drm_roi_v1 roi_v1; struct msm_display_info display_info; struct msm_roi_caps *caps; int i, rc; if (!c_conn || !c_state) { SDE_ERROR("invalid args\n"); return -EINVAL; } rc = sde_connector_get_info(&c_conn->base, &display_info); if (rc) { SDE_ERROR_CONN(c_conn, "display get info error: %d\n", rc); return rc; } caps = &display_info.roi_caps; if (!caps->enabled) { SDE_ERROR_CONN(c_conn, "display roi capability is disabled\n"); return -ENOTSUPP; } memset(&c_state->rois, 0, sizeof(c_state->rois)); if (!usr_ptr) { SDE_DEBUG_CONN(c_conn, "rois cleared\n"); return 0; } if (copy_from_user(&roi_v1, usr_ptr, sizeof(roi_v1))) { SDE_ERROR_CONN(c_conn, "failed to copy roi_v1 data\n"); return -EINVAL; } SDE_DEBUG_CONN(c_conn, "num_rects %d\n", roi_v1.num_rects); if (roi_v1.num_rects == 0) { SDE_DEBUG_CONN(c_conn, "rois cleared\n"); return 0; } if (roi_v1.num_rects > SDE_MAX_ROI_V1 || roi_v1.num_rects > caps->num_roi) { SDE_ERROR_CONN(c_conn, "too many rects specified: %d\n", roi_v1.num_rects); return -EINVAL; } c_state->rois.num_rects = roi_v1.num_rects; for (i = 0; i < roi_v1.num_rects; ++i) { int rc; rc = _sde_connector_roi_v1_check_roi(c_conn, &roi_v1.roi[i], caps); if (rc) return rc; c_state->rois.roi[i] = roi_v1.roi[i]; SDE_DEBUG_CONN(c_conn, "roi%d: roi 0x%x 0x%x 0x%x 0x%x\n", i, c_state->rois.roi[i].x1, c_state->rois.roi[i].y1, c_state->rois.roi[i].x2, c_state->rois.roi[i].y2); } return 0; } static int sde_connector_atomic_set_property(struct drm_connector *connector, struct drm_connector_state *state, struct drm_property *property, Loading Loading @@ -481,6 +603,12 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, SDE_ERROR("invalid topology_control: 0x%llX\n", val); } if (idx == CONNECTOR_PROP_ROI_V1) { rc = _sde_connector_set_roi_v1(c_conn, c_state, (void *)val); if (rc) SDE_ERROR_CONN(c_conn, "invalid roi_v1, rc: %d\n", rc); } /* check for custom property handling */ if (!rc && c_conn->ops.set_property) { rc = c_conn->ops.set_property(connector, Loading Loading @@ -725,6 +853,7 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, struct sde_kms_info *info; struct sde_connector *c_conn = NULL; struct dsi_display *dsi_display; struct msm_display_info display_info; int rc; if (!dev || !dev->dev_private || !encoder) { Loading Loading @@ -857,6 +986,13 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, } } rc = sde_connector_get_info(&c_conn->base, &display_info); if (!rc && display_info.roi_caps.enabled) { msm_property_install_volatile_range( &c_conn->property_info, "sde_drm_roi_v1", 0x0, 0, ~0, 0, CONNECTOR_PROP_ROI_V1); } msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE", 0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE); Loading drivers/gpu/drm/msm/sde/sde_connector.h +1 −1 Original line number Diff line number Diff line Loading @@ -281,7 +281,7 @@ struct sde_connector_state { int mmu_id; uint64_t property_values[CONNECTOR_PROP_COUNT]; struct msm_roi_mapping rois; struct msm_roi_list rois; }; /** Loading drivers/gpu/drm/msm/sde/sde_crtc.c +359 −16 Original line number Diff line number Diff line Loading @@ -656,21 +656,344 @@ static void _sde_crtc_setup_dim_layer_cfg(struct drm_crtc *crtc, } } void sde_crtc_get_crtc_roi(struct drm_crtc_state *state, const struct sde_rect **crtc_roi) { struct sde_crtc_state *crtc_state; if (!state || !crtc_roi) return; crtc_state = to_sde_crtc_state(state); *crtc_roi = &crtc_state->crtc_roi; } static int _sde_crtc_set_roi_v1(struct drm_crtc_state *state, void *usr_ptr) { struct drm_crtc *crtc; struct sde_crtc_state *cstate; struct sde_drm_roi_v1 roi_v1; int i; if (!state) { SDE_ERROR("invalid args\n"); return -EINVAL; } cstate = to_sde_crtc_state(state); crtc = cstate->base.crtc; memset(&cstate->user_roi_list, 0, sizeof(cstate->user_roi_list)); if (!usr_ptr) { SDE_DEBUG("crtc%d: rois cleared\n", DRMID(crtc)); return 0; } if (copy_from_user(&roi_v1, usr_ptr, sizeof(roi_v1))) { SDE_ERROR("crtc%d: failed to copy roi_v1 data\n", DRMID(crtc)); return -EINVAL; } SDE_DEBUG("crtc%d: num_rects %d\n", DRMID(crtc), roi_v1.num_rects); if (roi_v1.num_rects == 0) { SDE_DEBUG("crtc%d: rois cleared\n", DRMID(crtc)); return 0; } if (roi_v1.num_rects > SDE_MAX_ROI_V1) { SDE_ERROR("crtc%d: too many rects specified: %d\n", DRMID(crtc), roi_v1.num_rects); return -EINVAL; } cstate->user_roi_list.num_rects = roi_v1.num_rects; for (i = 0; i < roi_v1.num_rects; ++i) { cstate->user_roi_list.roi[i] = roi_v1.roi[i]; SDE_DEBUG("crtc%d: roi%d: roi (%d,%d) (%d,%d)\n", DRMID(crtc), i, cstate->user_roi_list.roi[i].x1, cstate->user_roi_list.roi[i].y1, cstate->user_roi_list.roi[i].x2, cstate->user_roi_list.roi[i].y2); } return 0; } static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct drm_connector *conn; struct drm_connector_state *conn_state; struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; struct sde_rect *crtc_roi; struct drm_clip_rect crtc_clip, *user_rect; int i, num_attached_conns = 0; if (!crtc || !state) return -EINVAL; sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(state); crtc_roi = &crtc_state->crtc_roi; /* init to invalid range maxes */ crtc_clip.x1 = ~0; crtc_clip.y1 = ~0; crtc_clip.x2 = 0; crtc_clip.y2 = 0; for_each_connector_in_state(state->state, conn, conn_state, i) { struct sde_connector_state *sde_conn_state; if (!conn_state || conn_state->crtc != crtc) continue; if (num_attached_conns) { SDE_ERROR( "crtc%d: unsupported: roi on crtc w/ >1 connectors\n", DRMID(crtc)); return -EINVAL; } ++num_attached_conns; sde_conn_state = to_sde_connector_state(conn_state); if (memcmp(&sde_conn_state->rois, &crtc_state->user_roi_list, sizeof(crtc_state->user_roi_list))) { SDE_ERROR("%s: crtc -> conn roi scaling unsupported\n", sde_crtc->name); return -EINVAL; } } /* aggregate all clipping rectangles together for overall crtc roi */ for (i = 0; i < crtc_state->user_roi_list.num_rects; i++) { user_rect = &crtc_state->user_roi_list.roi[i]; crtc_clip.x1 = min(crtc_clip.x1, user_rect->x1); crtc_clip.y1 = min(crtc_clip.y1, user_rect->y1); crtc_clip.x2 = max(crtc_clip.x2, user_rect->x2); crtc_clip.y2 = max(crtc_clip.y2, user_rect->y2); SDE_DEBUG( "%s: conn%d roi%d (%d,%d),(%d,%d) -> crtc (%d,%d),(%d,%d)\n", sde_crtc->name, DRMID(crtc), i, user_rect->x1, user_rect->y1, user_rect->x2, user_rect->y2, crtc_clip.x1, crtc_clip.y1, crtc_clip.x2, crtc_clip.y2); } if (crtc_clip.x2 && crtc_clip.y2) { crtc_roi->x = crtc_clip.x1; crtc_roi->y = crtc_clip.y1; crtc_roi->w = crtc_clip.x2 - crtc_clip.x1; crtc_roi->h = crtc_clip.y2 - crtc_clip.y1; } else { crtc_roi->x = 0; crtc_roi->y = 0; crtc_roi->w = 0; crtc_roi->h = 0; } SDE_DEBUG("%s: crtc roi (%d,%d,%d,%d)\n", sde_crtc->name, crtc_roi->x, crtc_roi->y, crtc_roi->w, crtc_roi->h); return 0; } static int _sde_crtc_set_lm_roi(struct drm_crtc *crtc, struct drm_crtc_state *state, int lm_idx) { struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; const struct sde_rect *crtc_roi; const struct sde_rect *lm_bounds; struct sde_rect *lm_roi; if (!crtc || !state || lm_idx >= ARRAY_SIZE(crtc_state->lm_bounds)) return -EINVAL; sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(state); crtc_roi = &crtc_state->crtc_roi; lm_bounds = &crtc_state->lm_bounds[lm_idx]; lm_roi = &crtc_state->lm_roi[lm_idx]; if (!sde_kms_rect_is_null(crtc_roi)) { sde_kms_rect_intersect(crtc_roi, lm_bounds, lm_roi); if (sde_kms_rect_is_null(lm_roi)) { SDE_ERROR("unsupported R/L only partial update\n"); return -EINVAL; } } else { memcpy(lm_roi, lm_bounds, sizeof(*lm_roi)); } SDE_DEBUG("%s: lm%d roi (%d,%d,%d,%d)\n", sde_crtc->name, lm_idx, lm_roi->x, lm_roi->y, lm_roi->w, lm_roi->h); return 0; } static int _sde_crtc_check_rois_centered_and_symmetric(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; const struct sde_rect *roi_prv, *roi_cur; int lm_idx; if (!crtc || !state) return -EINVAL; /* * On certain HW, ROIs must be centered on the split between LMs, * and be of equal width. */ sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(state); roi_prv = &crtc_state->lm_roi[0]; for (lm_idx = 1; lm_idx < sde_crtc->num_mixers; lm_idx++) { roi_cur = &crtc_state->lm_roi[lm_idx]; /* check lm rois are equal width & first roi ends at 2nd roi */ if (((roi_prv->x + roi_prv->w) != roi_cur->x) || (roi_prv->w != roi_cur->w)) { SDE_ERROR("%s: roi lm%d x %d w %d lm%d x %d w %d\n", sde_crtc->name, lm_idx-1, roi_prv->x, roi_prv->w, lm_idx, roi_cur->x, roi_cur->w); return -EINVAL; } roi_prv = roi_cur; } return 0; } static int _sde_crtc_check_planes_within_crtc_roi(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; const struct sde_rect *crtc_roi; struct drm_plane_state *pstate; struct drm_plane *plane; if (!crtc || !state) return -EINVAL; /* * Reject commit if a Plane CRTC destination coordinates fall outside * the partial CRTC ROI. LM output is determined via connector ROIs, * if they are specified, not Plane CRTC ROIs. */ sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(state); crtc_roi = &crtc_state->crtc_roi; if (sde_kms_rect_is_null(crtc_roi)) return 0; drm_atomic_crtc_state_for_each_plane(plane, state) { struct sde_rect plane_roi, intersection; pstate = drm_atomic_get_plane_state(state->state, plane); if (IS_ERR_OR_NULL(pstate)) { int rc = PTR_ERR(pstate); SDE_ERROR("%s: failed to get plane%d state, %d\n", sde_crtc->name, plane->base.id, rc); return rc; } plane_roi.x = pstate->crtc_x; plane_roi.y = pstate->crtc_y; plane_roi.w = pstate->crtc_w; plane_roi.h = pstate->crtc_h; sde_kms_rect_intersect(crtc_roi, &plane_roi, &intersection); if (!sde_kms_rect_is_equal(&plane_roi, &intersection)) { SDE_ERROR( "%s: plane%d crtc roi (%d,%d,%d,%d) outside crtc roi (%d,%d,%d,%d)\n", sde_crtc->name, plane->base.id, plane_roi.x, plane_roi.y, plane_roi.w, plane_roi.h, crtc_roi->x, crtc_roi->y, crtc_roi->w, crtc_roi->h); return -E2BIG; } } return 0; } static int _sde_crtc_check_rois(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct sde_crtc *sde_crtc; int lm_idx; int rc; if (!crtc || !state) return -EINVAL; sde_crtc = to_sde_crtc(crtc); rc = _sde_crtc_set_crtc_roi(crtc, state); if (rc) return rc; for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) { rc = _sde_crtc_set_lm_roi(crtc, state, lm_idx); if (rc) return rc; } rc = _sde_crtc_check_rois_centered_and_symmetric(crtc, state); if (rc) return rc; rc = _sde_crtc_check_planes_within_crtc_roi(crtc, state); if (rc) return rc; return 0; } static void _sde_crtc_program_lm_output_roi(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; const struct sde_rect *lm_roi; struct sde_hw_mixer *hw_lm; int lm_idx, lm_horiz_position; if (!crtc) return; sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(crtc->state); lm_horiz_position = 0; for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) { const struct sde_rect *lm_roi = &crtc_state->lm_bounds[lm_idx]; struct sde_hw_mixer *hw_lm = sde_crtc->mixers[lm_idx].hw_lm; struct sde_hw_mixer_cfg cfg; lm_roi = &crtc_state->lm_roi[lm_idx]; hw_lm = sde_crtc->mixers[lm_idx].hw_lm; SDE_EVT32(DRMID(crtc_state->base.crtc), lm_idx, lm_roi->x, lm_roi->y, lm_roi->w, lm_roi->h); if (sde_kms_rect_is_null(lm_roi)) continue; Loading Loading @@ -742,9 +1065,12 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, format = to_sde_format(msm_framebuffer_format(pstate->base.fb)); SDE_EVT32(DRMID(plane), state->src_x, state->src_y, state->src_w >> 16, state->src_h >> 16, state->crtc_x, state->crtc_y, state->crtc_w, state->crtc_h); SDE_EVT32(DRMID(crtc), DRMID(plane), state->fb ? state->fb->base.id : -1, state->src_x >> 16, state->src_y >> 16, state->src_w >> 16, state->src_h >> 16, state->crtc_x, state->crtc_y, state->crtc_w, state->crtc_h); for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) { struct sde_rect intersect; Loading Loading @@ -877,6 +1203,8 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc) ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx, &sde_crtc->stage_cfg, i); } _sde_crtc_program_lm_output_roi(crtc); } void sde_crtc_prepare_commit(struct drm_crtc *crtc, Loading Loading @@ -1329,14 +1657,18 @@ static void _sde_crtc_setup_lm_bounds(struct drm_crtc *crtc, crtc_split_width = sde_crtc_mixer_width(sde_crtc, adj_mode); for (i = 0; i < sde_crtc->num_mixers; i++) { struct sde_rect *lm_bound = &cstate->lm_bounds[i]; lm_bound->x = crtc_split_width * i; lm_bound->y = 0; lm_bound->w = crtc_split_width; lm_bound->h = adj_mode->vdisplay; SDE_EVT32(DRMID(crtc), i, lm_bound->x, lm_bound->y, lm_bound->w, lm_bound->h); cstate->lm_bounds[i].x = crtc_split_width * i; cstate->lm_bounds[i].y = 0; cstate->lm_bounds[i].w = crtc_split_width; cstate->lm_bounds[i].h = adj_mode->vdisplay; memcpy(&cstate->lm_roi[i], &cstate->lm_bounds[i], sizeof(cstate->lm_roi[i])); SDE_EVT32(DRMID(crtc), i, cstate->lm_bounds[i].x, cstate->lm_bounds[i].y, cstate->lm_bounds[i].w, cstate->lm_bounds[i].h); SDE_DEBUG("%s: lm%d bnd&roi (%d,%d,%d,%d)\n", sde_crtc->name, i, cstate->lm_roi[i].x, cstate->lm_roi[i].y, cstate->lm_roi[i].w, cstate->lm_roi[i].h); } drm_mode_debug_printmodeline(adj_mode); Loading Loading @@ -1366,10 +1698,10 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, sde_crtc = to_sde_crtc(crtc); dev = crtc->dev; if (!sde_crtc->num_mixers) if (!sde_crtc->num_mixers) { _sde_crtc_setup_mixers(crtc); _sde_crtc_setup_lm_bounds(crtc, crtc->state); } if (sde_crtc->event) { WARN_ON(sde_crtc->event); Loading Loading @@ -2117,6 +2449,11 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, } } rc = _sde_crtc_check_rois(crtc, state); if (rc) { SDE_ERROR("crtc%d failed roi check %d\n", crtc->base.id, rc); goto end; } end: _sde_crtc_rp_free_unused(&cstate->rp); Loading Loading @@ -2243,6 +2580,9 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, "dim_layer_v1", 0x0, 0, ~0, 0, CRTC_PROP_DIM_LAYER_V1); } msm_property_install_volatile_range(&sde_crtc->property_info, "sde_drm_roi_v1", 0x0, 0, ~0, 0, CRTC_PROP_ROI_V1); sde_kms_info_reset(info); sde_kms_info_add_keyint(info, "hw_version", catalog->hwversion); Loading Loading @@ -2315,6 +2655,9 @@ static int sde_crtc_atomic_set_property(struct drm_crtc *crtc, case CRTC_PROP_DIM_LAYER_V1: _sde_crtc_set_dim_layer_v1(cstate, (void *)val); break; case CRTC_PROP_ROI_V1: ret = _sde_crtc_set_roi_v1(state, (void *)val); break; default: /* nothing to do */ break; Loading drivers/gpu/drm/msm/sde/sde_crtc.h +18 −0 Original line number Diff line number Diff line Loading @@ -250,6 +250,11 @@ struct sde_crtc_respool { * @rsc_client : sde rsc client when mode is valid * @lm_bounds : LM boundaries based on current mode full resolution, no ROI. * Origin top left of CRTC. * @crtc_roi : Current CRTC ROI. Possibly sub-rectangle of mode. * Origin top left of CRTC. * @lm_roi : Current LM ROI, possibly sub-rectangle of mode. * Origin top left of CRTC. * @user_roi_list : List of user's requested ROIs as from set property * @property_values: Current crtc property values * @input_fence_timeout_ns : Cached input fence timeout, in ns * @property_blobs: Reference pointers for blob properties Loading @@ -270,6 +275,9 @@ struct sde_crtc_state { bool rsc_update; struct sde_rect lm_bounds[CRTC_DUAL_MIXERS]; struct sde_rect crtc_roi; struct sde_rect lm_roi[CRTC_DUAL_MIXERS]; struct msm_roi_list user_roi_list; uint64_t property_values[CRTC_PROP_COUNT]; uint64_t input_fence_timeout_ns; Loading Loading @@ -433,4 +441,14 @@ void *sde_crtc_res_get(struct drm_crtc_state *state, u32 type, u64 tag); */ void sde_crtc_res_put(struct drm_crtc_state *state, u32 type, u64 tag); /** * sde_crtc_get_crtc_roi - retrieve the crtc_roi from the given state object * used to allow the planes to adjust their final lm out_xy value in the * case of partial update * @crtc_state: Pointer to crtc state * @crtc_roi: Output pointer to crtc roi in the given state */ void sde_crtc_get_crtc_roi(struct drm_crtc_state *state, const struct sde_rect **crtc_roi); #endif /* _SDE_CRTC_H_ */ Loading
drivers/gpu/drm/msm/msm_drv.h +42 −9 Original line number Diff line number Diff line Loading @@ -138,6 +138,7 @@ enum msm_mdp_crtc_property { CRTC_PROP_MEM_IB, CRTC_PROP_ROT_PREFILL_BW, CRTC_PROP_ROT_CLK, CRTC_PROP_ROI_V1, /* total # of properties */ CRTC_PROP_COUNT Loading @@ -158,6 +159,7 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_DST_Y, CONNECTOR_PROP_DST_W, CONNECTOR_PROP_DST_H, CONNECTOR_PROP_ROI_V1, /* enum/bitmask properties */ CONNECTOR_PROP_TOPOLOGY_NAME, Loading Loading @@ -199,6 +201,38 @@ enum msm_display_caps { MSM_DISPLAY_CAP_EDID = BIT(3), }; /** * struct msm_roi_alignment - region of interest alignment restrictions * @xstart_pix_align: left x offset alignment restriction * @width_pix_align: width alignment restriction * @ystart_pix_align: top y offset alignment restriction * @height_pix_align: height alignment restriction * @min_width: minimum width restriction * @min_height: minimum height restriction */ struct msm_roi_alignment { uint32_t xstart_pix_align; uint32_t width_pix_align; uint32_t ystart_pix_align; uint32_t height_pix_align; uint32_t min_width; uint32_t min_height; }; /** * struct msm_roi_caps - display's region of interest capabilities * @enabled: true if some region of interest is supported * @merge_rois: merge rois before sending to display * @num_roi: maximum number of rois supported * @align: roi alignment restrictions */ struct msm_roi_caps { bool enabled; bool merge_rois; uint32_t num_roi; struct msm_roi_alignment align; }; /** * struct msm_display_dsc_info - defines dsc configuration * @version: DSC version. Loading Loading @@ -338,6 +372,7 @@ struct msm_compression_info { * @vtotal: display vertical total * @jitter: display jitter configuration * @comp_info: Compression supported by the display * @roi_caps: Region of interest capability info */ struct msm_display_info { int intf_type; Loading @@ -361,21 +396,19 @@ struct msm_display_info { uint32_t jitter; struct msm_compression_info comp_info; struct msm_roi_caps roi_caps; }; #define MSM_MAX_ROI 4 /** * struct msm_roi_mapping - Regions of interest structure for mapping CRTC to * Connector output * @num_rects: number of valid rectangles in src and dst arrays * @src: source roi rectangle * @dst: destination roi rectangle * struct msm_roi_list - list of regions of interest for a drm object * @num_rects: number of valid rectangles in the roi array * @roi: list of roi rectangles */ struct msm_roi_mapping { struct msm_roi_list { uint32_t num_rects; struct drm_clip_rect src[MSM_MAX_ROI]; struct drm_clip_rect dst[MSM_MAX_ROI]; struct drm_clip_rect roi[MSM_MAX_ROI]; }; /** Loading @@ -383,7 +416,7 @@ struct msm_roi_mapping { * @rois: Regions of interest structure for mapping CRTC to Connector output */ struct msm_display_kickoff_params { struct msm_roi_mapping *rois; struct msm_roi_list *rois; }; /** Loading
drivers/gpu/drm/msm/sde/sde_connector.c +136 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,12 @@ #define BL_NODE_NAME_SIZE 32 #define SDE_DEBUG_CONN(c, fmt, ...) SDE_DEBUG("conn%d " fmt,\ (c) ? (c)->base.base.id : -1, ##__VA_ARGS__) #define SDE_ERROR_CONN(c, fmt, ...) SDE_ERROR("conn%d " fmt,\ (c) ? (c)->base.base.id : -1, ##__VA_ARGS__) static const struct drm_prop_enum_list e_topology_name[] = { {SDE_RM_TOPOLOGY_UNKNOWN, "sde_unknown"}, {SDE_RM_TOPOLOGY_SINGLEPIPE, "sde_singlepipe"}, Loading Loading @@ -416,6 +422,122 @@ sde_connector_atomic_duplicate_state(struct drm_connector *connector) return &c_state->base; } static int _sde_connector_roi_v1_check_roi( struct sde_connector *c_conn, struct drm_clip_rect *roi_conn, const struct msm_roi_caps *caps) { const struct msm_roi_alignment *align = &caps->align; int w = roi_conn->x2 - roi_conn->x1; int h = roi_conn->y2 - roi_conn->y1; if (w <= 0 || h <= 0) { SDE_ERROR_CONN(c_conn, "invalid conn roi w %d h %d\n", w, h); return -EINVAL; } if (w < align->min_width || w % align->width_pix_align) { SDE_ERROR_CONN(c_conn, "invalid conn roi width %d min %d align %d\n", w, align->min_width, align->width_pix_align); return -EINVAL; } if (h < align->min_height || h % align->height_pix_align) { SDE_ERROR_CONN(c_conn, "invalid conn roi height %d min %d align %d\n", h, align->min_height, align->height_pix_align); return -EINVAL; } if (roi_conn->x1 % align->xstart_pix_align) { SDE_ERROR_CONN(c_conn, "invalid conn roi x1 %d align %d\n", roi_conn->x1, align->xstart_pix_align); return -EINVAL; } if (roi_conn->y1 % align->ystart_pix_align) { SDE_ERROR_CONN(c_conn, "invalid conn roi y1 %d align %d\n", roi_conn->y1, align->ystart_pix_align); return -EINVAL; } return 0; } static int _sde_connector_set_roi_v1( struct sde_connector *c_conn, struct sde_connector_state *c_state, void *usr_ptr) { struct sde_drm_roi_v1 roi_v1; struct msm_display_info display_info; struct msm_roi_caps *caps; int i, rc; if (!c_conn || !c_state) { SDE_ERROR("invalid args\n"); return -EINVAL; } rc = sde_connector_get_info(&c_conn->base, &display_info); if (rc) { SDE_ERROR_CONN(c_conn, "display get info error: %d\n", rc); return rc; } caps = &display_info.roi_caps; if (!caps->enabled) { SDE_ERROR_CONN(c_conn, "display roi capability is disabled\n"); return -ENOTSUPP; } memset(&c_state->rois, 0, sizeof(c_state->rois)); if (!usr_ptr) { SDE_DEBUG_CONN(c_conn, "rois cleared\n"); return 0; } if (copy_from_user(&roi_v1, usr_ptr, sizeof(roi_v1))) { SDE_ERROR_CONN(c_conn, "failed to copy roi_v1 data\n"); return -EINVAL; } SDE_DEBUG_CONN(c_conn, "num_rects %d\n", roi_v1.num_rects); if (roi_v1.num_rects == 0) { SDE_DEBUG_CONN(c_conn, "rois cleared\n"); return 0; } if (roi_v1.num_rects > SDE_MAX_ROI_V1 || roi_v1.num_rects > caps->num_roi) { SDE_ERROR_CONN(c_conn, "too many rects specified: %d\n", roi_v1.num_rects); return -EINVAL; } c_state->rois.num_rects = roi_v1.num_rects; for (i = 0; i < roi_v1.num_rects; ++i) { int rc; rc = _sde_connector_roi_v1_check_roi(c_conn, &roi_v1.roi[i], caps); if (rc) return rc; c_state->rois.roi[i] = roi_v1.roi[i]; SDE_DEBUG_CONN(c_conn, "roi%d: roi 0x%x 0x%x 0x%x 0x%x\n", i, c_state->rois.roi[i].x1, c_state->rois.roi[i].y1, c_state->rois.roi[i].x2, c_state->rois.roi[i].y2); } return 0; } static int sde_connector_atomic_set_property(struct drm_connector *connector, struct drm_connector_state *state, struct drm_property *property, Loading Loading @@ -481,6 +603,12 @@ static int sde_connector_atomic_set_property(struct drm_connector *connector, SDE_ERROR("invalid topology_control: 0x%llX\n", val); } if (idx == CONNECTOR_PROP_ROI_V1) { rc = _sde_connector_set_roi_v1(c_conn, c_state, (void *)val); if (rc) SDE_ERROR_CONN(c_conn, "invalid roi_v1, rc: %d\n", rc); } /* check for custom property handling */ if (!rc && c_conn->ops.set_property) { rc = c_conn->ops.set_property(connector, Loading Loading @@ -725,6 +853,7 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, struct sde_kms_info *info; struct sde_connector *c_conn = NULL; struct dsi_display *dsi_display; struct msm_display_info display_info; int rc; if (!dev || !dev->dev_private || !encoder) { Loading Loading @@ -857,6 +986,13 @@ struct drm_connector *sde_connector_init(struct drm_device *dev, } } rc = sde_connector_get_info(&c_conn->base, &display_info); if (!rc && display_info.roi_caps.enabled) { msm_property_install_volatile_range( &c_conn->property_info, "sde_drm_roi_v1", 0x0, 0, ~0, 0, CONNECTOR_PROP_ROI_V1); } msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE", 0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE); Loading
drivers/gpu/drm/msm/sde/sde_connector.h +1 −1 Original line number Diff line number Diff line Loading @@ -281,7 +281,7 @@ struct sde_connector_state { int mmu_id; uint64_t property_values[CONNECTOR_PROP_COUNT]; struct msm_roi_mapping rois; struct msm_roi_list rois; }; /** Loading
drivers/gpu/drm/msm/sde/sde_crtc.c +359 −16 Original line number Diff line number Diff line Loading @@ -656,21 +656,344 @@ static void _sde_crtc_setup_dim_layer_cfg(struct drm_crtc *crtc, } } void sde_crtc_get_crtc_roi(struct drm_crtc_state *state, const struct sde_rect **crtc_roi) { struct sde_crtc_state *crtc_state; if (!state || !crtc_roi) return; crtc_state = to_sde_crtc_state(state); *crtc_roi = &crtc_state->crtc_roi; } static int _sde_crtc_set_roi_v1(struct drm_crtc_state *state, void *usr_ptr) { struct drm_crtc *crtc; struct sde_crtc_state *cstate; struct sde_drm_roi_v1 roi_v1; int i; if (!state) { SDE_ERROR("invalid args\n"); return -EINVAL; } cstate = to_sde_crtc_state(state); crtc = cstate->base.crtc; memset(&cstate->user_roi_list, 0, sizeof(cstate->user_roi_list)); if (!usr_ptr) { SDE_DEBUG("crtc%d: rois cleared\n", DRMID(crtc)); return 0; } if (copy_from_user(&roi_v1, usr_ptr, sizeof(roi_v1))) { SDE_ERROR("crtc%d: failed to copy roi_v1 data\n", DRMID(crtc)); return -EINVAL; } SDE_DEBUG("crtc%d: num_rects %d\n", DRMID(crtc), roi_v1.num_rects); if (roi_v1.num_rects == 0) { SDE_DEBUG("crtc%d: rois cleared\n", DRMID(crtc)); return 0; } if (roi_v1.num_rects > SDE_MAX_ROI_V1) { SDE_ERROR("crtc%d: too many rects specified: %d\n", DRMID(crtc), roi_v1.num_rects); return -EINVAL; } cstate->user_roi_list.num_rects = roi_v1.num_rects; for (i = 0; i < roi_v1.num_rects; ++i) { cstate->user_roi_list.roi[i] = roi_v1.roi[i]; SDE_DEBUG("crtc%d: roi%d: roi (%d,%d) (%d,%d)\n", DRMID(crtc), i, cstate->user_roi_list.roi[i].x1, cstate->user_roi_list.roi[i].y1, cstate->user_roi_list.roi[i].x2, cstate->user_roi_list.roi[i].y2); } return 0; } static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct drm_connector *conn; struct drm_connector_state *conn_state; struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; struct sde_rect *crtc_roi; struct drm_clip_rect crtc_clip, *user_rect; int i, num_attached_conns = 0; if (!crtc || !state) return -EINVAL; sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(state); crtc_roi = &crtc_state->crtc_roi; /* init to invalid range maxes */ crtc_clip.x1 = ~0; crtc_clip.y1 = ~0; crtc_clip.x2 = 0; crtc_clip.y2 = 0; for_each_connector_in_state(state->state, conn, conn_state, i) { struct sde_connector_state *sde_conn_state; if (!conn_state || conn_state->crtc != crtc) continue; if (num_attached_conns) { SDE_ERROR( "crtc%d: unsupported: roi on crtc w/ >1 connectors\n", DRMID(crtc)); return -EINVAL; } ++num_attached_conns; sde_conn_state = to_sde_connector_state(conn_state); if (memcmp(&sde_conn_state->rois, &crtc_state->user_roi_list, sizeof(crtc_state->user_roi_list))) { SDE_ERROR("%s: crtc -> conn roi scaling unsupported\n", sde_crtc->name); return -EINVAL; } } /* aggregate all clipping rectangles together for overall crtc roi */ for (i = 0; i < crtc_state->user_roi_list.num_rects; i++) { user_rect = &crtc_state->user_roi_list.roi[i]; crtc_clip.x1 = min(crtc_clip.x1, user_rect->x1); crtc_clip.y1 = min(crtc_clip.y1, user_rect->y1); crtc_clip.x2 = max(crtc_clip.x2, user_rect->x2); crtc_clip.y2 = max(crtc_clip.y2, user_rect->y2); SDE_DEBUG( "%s: conn%d roi%d (%d,%d),(%d,%d) -> crtc (%d,%d),(%d,%d)\n", sde_crtc->name, DRMID(crtc), i, user_rect->x1, user_rect->y1, user_rect->x2, user_rect->y2, crtc_clip.x1, crtc_clip.y1, crtc_clip.x2, crtc_clip.y2); } if (crtc_clip.x2 && crtc_clip.y2) { crtc_roi->x = crtc_clip.x1; crtc_roi->y = crtc_clip.y1; crtc_roi->w = crtc_clip.x2 - crtc_clip.x1; crtc_roi->h = crtc_clip.y2 - crtc_clip.y1; } else { crtc_roi->x = 0; crtc_roi->y = 0; crtc_roi->w = 0; crtc_roi->h = 0; } SDE_DEBUG("%s: crtc roi (%d,%d,%d,%d)\n", sde_crtc->name, crtc_roi->x, crtc_roi->y, crtc_roi->w, crtc_roi->h); return 0; } static int _sde_crtc_set_lm_roi(struct drm_crtc *crtc, struct drm_crtc_state *state, int lm_idx) { struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; const struct sde_rect *crtc_roi; const struct sde_rect *lm_bounds; struct sde_rect *lm_roi; if (!crtc || !state || lm_idx >= ARRAY_SIZE(crtc_state->lm_bounds)) return -EINVAL; sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(state); crtc_roi = &crtc_state->crtc_roi; lm_bounds = &crtc_state->lm_bounds[lm_idx]; lm_roi = &crtc_state->lm_roi[lm_idx]; if (!sde_kms_rect_is_null(crtc_roi)) { sde_kms_rect_intersect(crtc_roi, lm_bounds, lm_roi); if (sde_kms_rect_is_null(lm_roi)) { SDE_ERROR("unsupported R/L only partial update\n"); return -EINVAL; } } else { memcpy(lm_roi, lm_bounds, sizeof(*lm_roi)); } SDE_DEBUG("%s: lm%d roi (%d,%d,%d,%d)\n", sde_crtc->name, lm_idx, lm_roi->x, lm_roi->y, lm_roi->w, lm_roi->h); return 0; } static int _sde_crtc_check_rois_centered_and_symmetric(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; const struct sde_rect *roi_prv, *roi_cur; int lm_idx; if (!crtc || !state) return -EINVAL; /* * On certain HW, ROIs must be centered on the split between LMs, * and be of equal width. */ sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(state); roi_prv = &crtc_state->lm_roi[0]; for (lm_idx = 1; lm_idx < sde_crtc->num_mixers; lm_idx++) { roi_cur = &crtc_state->lm_roi[lm_idx]; /* check lm rois are equal width & first roi ends at 2nd roi */ if (((roi_prv->x + roi_prv->w) != roi_cur->x) || (roi_prv->w != roi_cur->w)) { SDE_ERROR("%s: roi lm%d x %d w %d lm%d x %d w %d\n", sde_crtc->name, lm_idx-1, roi_prv->x, roi_prv->w, lm_idx, roi_cur->x, roi_cur->w); return -EINVAL; } roi_prv = roi_cur; } return 0; } static int _sde_crtc_check_planes_within_crtc_roi(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; const struct sde_rect *crtc_roi; struct drm_plane_state *pstate; struct drm_plane *plane; if (!crtc || !state) return -EINVAL; /* * Reject commit if a Plane CRTC destination coordinates fall outside * the partial CRTC ROI. LM output is determined via connector ROIs, * if they are specified, not Plane CRTC ROIs. */ sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(state); crtc_roi = &crtc_state->crtc_roi; if (sde_kms_rect_is_null(crtc_roi)) return 0; drm_atomic_crtc_state_for_each_plane(plane, state) { struct sde_rect plane_roi, intersection; pstate = drm_atomic_get_plane_state(state->state, plane); if (IS_ERR_OR_NULL(pstate)) { int rc = PTR_ERR(pstate); SDE_ERROR("%s: failed to get plane%d state, %d\n", sde_crtc->name, plane->base.id, rc); return rc; } plane_roi.x = pstate->crtc_x; plane_roi.y = pstate->crtc_y; plane_roi.w = pstate->crtc_w; plane_roi.h = pstate->crtc_h; sde_kms_rect_intersect(crtc_roi, &plane_roi, &intersection); if (!sde_kms_rect_is_equal(&plane_roi, &intersection)) { SDE_ERROR( "%s: plane%d crtc roi (%d,%d,%d,%d) outside crtc roi (%d,%d,%d,%d)\n", sde_crtc->name, plane->base.id, plane_roi.x, plane_roi.y, plane_roi.w, plane_roi.h, crtc_roi->x, crtc_roi->y, crtc_roi->w, crtc_roi->h); return -E2BIG; } } return 0; } static int _sde_crtc_check_rois(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct sde_crtc *sde_crtc; int lm_idx; int rc; if (!crtc || !state) return -EINVAL; sde_crtc = to_sde_crtc(crtc); rc = _sde_crtc_set_crtc_roi(crtc, state); if (rc) return rc; for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) { rc = _sde_crtc_set_lm_roi(crtc, state, lm_idx); if (rc) return rc; } rc = _sde_crtc_check_rois_centered_and_symmetric(crtc, state); if (rc) return rc; rc = _sde_crtc_check_planes_within_crtc_roi(crtc, state); if (rc) return rc; return 0; } static void _sde_crtc_program_lm_output_roi(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc; struct sde_crtc_state *crtc_state; const struct sde_rect *lm_roi; struct sde_hw_mixer *hw_lm; int lm_idx, lm_horiz_position; if (!crtc) return; sde_crtc = to_sde_crtc(crtc); crtc_state = to_sde_crtc_state(crtc->state); lm_horiz_position = 0; for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) { const struct sde_rect *lm_roi = &crtc_state->lm_bounds[lm_idx]; struct sde_hw_mixer *hw_lm = sde_crtc->mixers[lm_idx].hw_lm; struct sde_hw_mixer_cfg cfg; lm_roi = &crtc_state->lm_roi[lm_idx]; hw_lm = sde_crtc->mixers[lm_idx].hw_lm; SDE_EVT32(DRMID(crtc_state->base.crtc), lm_idx, lm_roi->x, lm_roi->y, lm_roi->w, lm_roi->h); if (sde_kms_rect_is_null(lm_roi)) continue; Loading Loading @@ -742,9 +1065,12 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc, format = to_sde_format(msm_framebuffer_format(pstate->base.fb)); SDE_EVT32(DRMID(plane), state->src_x, state->src_y, state->src_w >> 16, state->src_h >> 16, state->crtc_x, state->crtc_y, state->crtc_w, state->crtc_h); SDE_EVT32(DRMID(crtc), DRMID(plane), state->fb ? state->fb->base.id : -1, state->src_x >> 16, state->src_y >> 16, state->src_w >> 16, state->src_h >> 16, state->crtc_x, state->crtc_y, state->crtc_w, state->crtc_h); for (lm_idx = 0; lm_idx < sde_crtc->num_mixers; lm_idx++) { struct sde_rect intersect; Loading Loading @@ -877,6 +1203,8 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc) ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx, &sde_crtc->stage_cfg, i); } _sde_crtc_program_lm_output_roi(crtc); } void sde_crtc_prepare_commit(struct drm_crtc *crtc, Loading Loading @@ -1329,14 +1657,18 @@ static void _sde_crtc_setup_lm_bounds(struct drm_crtc *crtc, crtc_split_width = sde_crtc_mixer_width(sde_crtc, adj_mode); for (i = 0; i < sde_crtc->num_mixers; i++) { struct sde_rect *lm_bound = &cstate->lm_bounds[i]; lm_bound->x = crtc_split_width * i; lm_bound->y = 0; lm_bound->w = crtc_split_width; lm_bound->h = adj_mode->vdisplay; SDE_EVT32(DRMID(crtc), i, lm_bound->x, lm_bound->y, lm_bound->w, lm_bound->h); cstate->lm_bounds[i].x = crtc_split_width * i; cstate->lm_bounds[i].y = 0; cstate->lm_bounds[i].w = crtc_split_width; cstate->lm_bounds[i].h = adj_mode->vdisplay; memcpy(&cstate->lm_roi[i], &cstate->lm_bounds[i], sizeof(cstate->lm_roi[i])); SDE_EVT32(DRMID(crtc), i, cstate->lm_bounds[i].x, cstate->lm_bounds[i].y, cstate->lm_bounds[i].w, cstate->lm_bounds[i].h); SDE_DEBUG("%s: lm%d bnd&roi (%d,%d,%d,%d)\n", sde_crtc->name, i, cstate->lm_roi[i].x, cstate->lm_roi[i].y, cstate->lm_roi[i].w, cstate->lm_roi[i].h); } drm_mode_debug_printmodeline(adj_mode); Loading Loading @@ -1366,10 +1698,10 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc, sde_crtc = to_sde_crtc(crtc); dev = crtc->dev; if (!sde_crtc->num_mixers) if (!sde_crtc->num_mixers) { _sde_crtc_setup_mixers(crtc); _sde_crtc_setup_lm_bounds(crtc, crtc->state); } if (sde_crtc->event) { WARN_ON(sde_crtc->event); Loading Loading @@ -2117,6 +2449,11 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, } } rc = _sde_crtc_check_rois(crtc, state); if (rc) { SDE_ERROR("crtc%d failed roi check %d\n", crtc->base.id, rc); goto end; } end: _sde_crtc_rp_free_unused(&cstate->rp); Loading Loading @@ -2243,6 +2580,9 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, "dim_layer_v1", 0x0, 0, ~0, 0, CRTC_PROP_DIM_LAYER_V1); } msm_property_install_volatile_range(&sde_crtc->property_info, "sde_drm_roi_v1", 0x0, 0, ~0, 0, CRTC_PROP_ROI_V1); sde_kms_info_reset(info); sde_kms_info_add_keyint(info, "hw_version", catalog->hwversion); Loading Loading @@ -2315,6 +2655,9 @@ static int sde_crtc_atomic_set_property(struct drm_crtc *crtc, case CRTC_PROP_DIM_LAYER_V1: _sde_crtc_set_dim_layer_v1(cstate, (void *)val); break; case CRTC_PROP_ROI_V1: ret = _sde_crtc_set_roi_v1(state, (void *)val); break; default: /* nothing to do */ break; Loading
drivers/gpu/drm/msm/sde/sde_crtc.h +18 −0 Original line number Diff line number Diff line Loading @@ -250,6 +250,11 @@ struct sde_crtc_respool { * @rsc_client : sde rsc client when mode is valid * @lm_bounds : LM boundaries based on current mode full resolution, no ROI. * Origin top left of CRTC. * @crtc_roi : Current CRTC ROI. Possibly sub-rectangle of mode. * Origin top left of CRTC. * @lm_roi : Current LM ROI, possibly sub-rectangle of mode. * Origin top left of CRTC. * @user_roi_list : List of user's requested ROIs as from set property * @property_values: Current crtc property values * @input_fence_timeout_ns : Cached input fence timeout, in ns * @property_blobs: Reference pointers for blob properties Loading @@ -270,6 +275,9 @@ struct sde_crtc_state { bool rsc_update; struct sde_rect lm_bounds[CRTC_DUAL_MIXERS]; struct sde_rect crtc_roi; struct sde_rect lm_roi[CRTC_DUAL_MIXERS]; struct msm_roi_list user_roi_list; uint64_t property_values[CRTC_PROP_COUNT]; uint64_t input_fence_timeout_ns; Loading Loading @@ -433,4 +441,14 @@ void *sde_crtc_res_get(struct drm_crtc_state *state, u32 type, u64 tag); */ void sde_crtc_res_put(struct drm_crtc_state *state, u32 type, u64 tag); /** * sde_crtc_get_crtc_roi - retrieve the crtc_roi from the given state object * used to allow the planes to adjust their final lm out_xy value in the * case of partial update * @crtc_state: Pointer to crtc state * @crtc_roi: Output pointer to crtc roi in the given state */ void sde_crtc_get_crtc_roi(struct drm_crtc_state *state, const struct sde_rect **crtc_roi); #endif /* _SDE_CRTC_H_ */