diff --git a/include/uapi/display/drm/sde_drm.h b/include/uapi/display/drm/sde_drm.h index 4bd05e9962..a6f31b54b8 100644 --- a/include/uapi/display/drm/sde_drm.h +++ b/include/uapi/display/drm/sde_drm.h @@ -526,6 +526,92 @@ struct sde_drm_roi_v1 { #define SDE_RECOVERY_CAPTURE 1 #define SDE_RECOVERY_HARD_RESET 2 +/** + * Define UBWC statistics config + */ +#define UBWC_STATS_MAX_ROI 0x3 + +/** + * struct sde_drm_ubwc_stats_roi - region of interest for ubwc stats + * y_coord0: first y offset from top of display + * y_coord1: second y offset from top of display + */ +struct sde_drm_ubwc_stats_roi { + __u16 y_coord0; + __u16 y_coord1; +}; + +/** + * struct sde_drm_ubwc_stats_data: ubwc statistics + * roi: region of interest + * worst_bw: worst bandwidth, per roi + * worst_bw_y_coord: y offset (row) location of worst bandwidth, per roi + * total_bw: total bandwidth, per roi + * error: error status + * meta_error: meta error data + */ +struct sde_drm_ubwc_stats_data { + struct sde_drm_ubwc_stats_roi roi; + __u16 worst_bw[UBWC_STATS_MAX_ROI]; + __u16 worst_bw_y_coord[UBWC_STATS_MAX_ROI]; + __u32 total_bw[UBWC_STATS_MAX_ROI]; + __u32 error; + __u32 meta_error; +}; + +/** + * Define frame data config + */ +#define SDE_FRAME_DATA_BUFFER_MAX 0x3 +#define SDE_FRAME_DATA_GUARD_BYTES 0xFF +#define SDE_FRAME_DATA_MAX_PLANES 0x10 + +/** + * struct sde_drm_frame_data_buffers_ctrl - control frame data buffers + * num_buffers: number of allocated buffers + * fds: fd list for allocated buffers + */ +struct sde_drm_frame_data_buffers_ctrl { + __u32 num_buffers; + __u32 fds[SDE_FRAME_DATA_BUFFER_MAX]; +}; + +/** + * struct sde_drm_frame_data_buf - frame data buffer info sent to userspace + * fd: buffer fd + * offset: offset from buffer address + * status: status flag + */ +struct sde_drm_frame_data_buf { + __u32 fd; + __u32 offset; + __u32 status; +}; + +/** + * struct sde_drm_plane_frame_data - definition of plane frame data struct + * plane_id: drm plane id + * ubwc_stats: ubwc statistics + */ +struct sde_drm_plane_frame_data { + __u32 plane_id; + + struct sde_drm_ubwc_stats_data ubwc_stats; +}; + +/** + * struct sde_drm_frame_data_packet - definition of frame data struct + * frame_count: interface frame count + * commit_count: sw commit count + * plane_frame_data: data available per plane + */ +struct sde_drm_frame_data_packet { + __u32 frame_count; + __u64 commit_count; + + struct sde_drm_plane_frame_data plane_frame_data[SDE_FRAME_DATA_MAX_PLANES]; +}; + /* * Colorimetry Data Block values * These bit nums are defined as per the CTA spec @@ -723,6 +809,7 @@ struct drm_msm_noise_layer_cfg { #define DRM_EVENT_LTM_WB_PB 0X80000009 #define DRM_EVENT_LTM_OFF 0X8000000A #define DRM_EVENT_MMRM_CB 0X8000000B +#define DRM_EVENT_FRAME_DATA 0x8000000C #ifndef DRM_MODE_FLAG_VID_MODE_PANEL #define DRM_MODE_FLAG_VID_MODE_PANEL 0x01 diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 35ef717ffd..777352f2f6 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -136,6 +136,7 @@ enum msm_mdp_plane_property { PLANE_PROP_INVERSE_PMA, PLANE_PROP_FP16_IGC, PLANE_PROP_FP16_UNMULT, + PLANE_PROP_UBWC_STATS_ROI, /* enum/bitmask properties */ PLANE_PROP_BLEND_OP, @@ -181,6 +182,7 @@ enum msm_mdp_crtc_property { CRTC_PROP_CACHE_STATE, CRTC_PROP_VM_REQ_STATE, CRTC_PROP_NOISE_LAYER_V1, + CRTC_PROP_FRAME_DATA_BUF, /* total # of properties */ CRTC_PROP_COUNT diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index d1eb84022b..fe4e9de363 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -454,6 +454,23 @@ static const struct attribute_group *sde_crtc_attr_groups[] = { NULL, }; +static void sde_crtc_event_notify(struct drm_crtc *crtc, uint32_t type, uint32_t len, uint64_t val) +{ + struct drm_event event; + + if (!crtc) { + SDE_ERROR("invalid crtc\n"); + return; + } + + event.type = type; + event.length = len; + msm_mode_object_event_notify(&crtc->base, crtc->dev, &event, (u8 *)&val); + + SDE_EVT32(DRMID(crtc), type, len, val >> 32, val & 0xFFFFFFFF); + SDE_DEBUG("crtc:%d event(%d) value(%llu) notified\n", DRMID(crtc), type, val); +} + static void sde_crtc_destroy(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); @@ -2313,6 +2330,144 @@ static void _sde_crtc_dest_scaler_setup(struct drm_crtc *crtc) } } +static void _sde_crtc_put_frame_data_buffer(struct sde_frame_data_buffer *buf) +{ + if (!buf) + return; + + msm_gem_put_buffer(buf->gem); + kfree(buf); + buf = NULL; +} + +static int _sde_crtc_get_frame_data_buffer(struct drm_crtc *crtc, uint32_t fd) +{ + struct sde_crtc *sde_crtc; + struct sde_frame_data_buffer *buf; + uint32_t cur_buf; + + sde_crtc = to_sde_crtc(crtc); + cur_buf = sde_crtc->frame_data.cnt; + + buf = kzalloc(sizeof(struct sde_frame_data_buffer), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + sde_crtc->frame_data.buf[cur_buf] = buf; + buf->fb = drm_framebuffer_lookup(crtc->dev, NULL, fd); + if (!buf->fb) { + SDE_ERROR("unable to get fb"); + return -EINVAL; + } + + buf->gem = msm_framebuffer_bo(buf->fb, 0); + if (!buf->gem) { + SDE_ERROR("unable to get drm gem"); + return -EINVAL; + } + + return msm_gem_get_buffer(buf->gem, crtc->dev, buf->fb, + sizeof(struct sde_drm_frame_data_packet)); +} + +static void _sde_crtc_set_frame_data_buffers(struct drm_crtc *crtc, + struct sde_crtc_state *cstate, void __user *usr) +{ + struct sde_crtc *sde_crtc; + struct sde_drm_frame_data_buffers_ctrl ctrl; + int i, ret; + + if (!crtc || !cstate || !usr) + return; + + sde_crtc = to_sde_crtc(crtc); + + ret = copy_from_user(&ctrl, usr, sizeof(ctrl)); + if (ret) { + SDE_ERROR("failed to copy frame data ctrl, ret %d\n", ret); + return; + } + + if (!ctrl.num_buffers) { + SDE_DEBUG("clearing frame data buffers"); + goto exit; + } else if (ctrl.num_buffers > SDE_FRAME_DATA_BUFFER_MAX) { + SDE_ERROR("invalid number of buffers %d", ctrl.num_buffers); + return; + } + + for (i = 0; i < ctrl.num_buffers; i++) { + if (_sde_crtc_get_frame_data_buffer(crtc, ctrl.fds[i])) { + SDE_ERROR("unable to set buffer for fd %d", ctrl.fds[i]); + goto exit; + } + sde_crtc->frame_data.cnt++; + } + + return; +exit: + while (sde_crtc->frame_data.cnt--) + _sde_crtc_put_frame_data_buffer( + sde_crtc->frame_data.buf[sde_crtc->frame_data.cnt]); +} + +static void _sde_crtc_frame_data_notify(struct drm_crtc *crtc, + struct sde_drm_frame_data_packet *frame_data_packet) +{ + struct sde_crtc *sde_crtc; + struct sde_drm_frame_data_buf buf; + struct msm_gem_object *msm_gem; + u32 cur_buf; + + sde_crtc = to_sde_crtc(crtc); + cur_buf = sde_crtc->frame_data.idx; + msm_gem = to_msm_bo(sde_crtc->frame_data.buf[cur_buf]->gem); + + buf.fd = sde_crtc->frame_data.buf[cur_buf]->fd; + buf.offset = msm_gem->offset; + + sde_crtc_event_notify(crtc, DRM_EVENT_FRAME_DATA, sizeof(struct sde_drm_frame_data_buf), + (uint64_t)(&buf)); + + sde_crtc->frame_data.idx = ++sde_crtc->frame_data.idx % sde_crtc->frame_data.cnt; +} + +void sde_crtc_get_frame_data(struct drm_crtc *crtc) +{ + struct sde_crtc *sde_crtc; + struct drm_plane *plane; + struct sde_drm_frame_data_packet frame_data_packet = {0, 0}; + struct sde_drm_frame_data_packet *data; + struct sde_frame_data *frame_data; + int i = 0; + + if (!crtc || !crtc->state) + return; + + sde_crtc = to_sde_crtc(crtc); + frame_data = &sde_crtc->frame_data; + + if (frame_data->cnt) { + struct msm_gem_object *msm_gem; + + msm_gem = to_msm_bo(frame_data->buf[frame_data->cnt]->gem); + data = (struct sde_drm_frame_data_packet *) + (((u8 *)msm_gem->vaddr) + msm_gem->offset); + } else { + data = &frame_data_packet; + } + + data->commit_count = sde_crtc->play_count; + data->frame_count = sde_crtc->fps_info.frame_count; + + /* Collect plane specific data */ + drm_for_each_plane_mask(plane, crtc->dev, sde_crtc->plane_mask_old) + sde_plane_get_frame_data(plane, &data->plane_frame_data[i]); + + if (frame_data->cnt) + _sde_crtc_frame_data_notify(crtc, data); +} + static void sde_crtc_frame_event_cb(void *data, u32 event, ktime_t ts) { struct drm_crtc *crtc = (struct drm_crtc *)data; @@ -2320,8 +2475,6 @@ static void sde_crtc_frame_event_cb(void *data, u32 event, ktime_t ts) struct msm_drm_private *priv; struct sde_crtc_frame_event *fevent; struct sde_kms_frame_event_cb_data *cb_data; - struct drm_plane *plane; - u32 ubwc_error, meta_error; unsigned long flags; u32 crtc_id; @@ -2360,21 +2513,8 @@ static void sde_crtc_frame_event_cb(void *data, u32 event, ktime_t ts) /* log and clear plane ubwc errors if any */ if (event & (SDE_ENCODER_FRAME_EVENT_ERROR | SDE_ENCODER_FRAME_EVENT_PANEL_DEAD - | SDE_ENCODER_FRAME_EVENT_DONE)) { - drm_for_each_plane_mask(plane, crtc->dev, - sde_crtc->plane_mask_old) { - ubwc_error = sde_plane_get_ubwc_error(plane); - meta_error = sde_plane_get_meta_error(plane); - if (ubwc_error | meta_error) { - SDE_EVT32(DRMID(crtc), DRMID(plane), ubwc_error, - meta_error, SDE_EVTLOG_ERROR); - SDE_DEBUG("crtc%d plane %d ubwc_error %d meta_error %d\n", - DRMID(crtc), DRMID(plane), ubwc_error, meta_error); - sde_plane_clear_ubwc_error(plane); - sde_plane_clear_meta_error(plane); - } - } - } + | SDE_ENCODER_FRAME_EVENT_DONE)) + sde_crtc_get_frame_data(crtc); if ((event & SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE) && (sde_crtc && sde_crtc->retire_frame_event_sf)) { @@ -2654,23 +2794,6 @@ static void sde_crtc_frame_event_work(struct kthread_work *work) SDE_ATRACE_END("crtc_frame_event"); } -static void sde_crtc_event_notify(struct drm_crtc *crtc, uint32_t type, uint32_t len, uint32_t val) -{ - struct drm_event event; - - if (!crtc) { - SDE_ERROR("invalid crtc\n"); - return; - } - - event.type = type; - event.length = len; - msm_mode_object_event_notify(&crtc->base, crtc->dev, &event, (u8 *)&val); - - SDE_EVT32(DRMID(crtc), type, len, val); - SDE_DEBUG("crtc:%d event(%d) value(%d) notified\n", DRMID(crtc), type, val); -} - void sde_crtc_complete_commit(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { @@ -5707,6 +5830,10 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, sde_crtc_install_noise_layer_properties(sde_crtc, catalog, info); + if (catalog->has_ubwc_stats) + msm_property_install_range(&sde_crtc->property_info, "frame_data", + 0x0, 0, ~0, 0, CRTC_PROP_FRAME_DATA_BUF); + kfree(info); } @@ -5862,6 +5989,9 @@ static int sde_crtc_atomic_set_property(struct drm_crtc *crtc, _sde_crtc_set_noise_layer(sde_crtc, cstate, (void __user *)(uintptr_t)val); break; + case CRTC_PROP_FRAME_DATA_BUF: + _sde_crtc_set_frame_data_buffers(crtc, cstate, (void __user *)(uintptr_t)val); + break; default: /* nothing to do */ break; diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index be31eaaead..742794b3e8 100644 --- a/msm/sde/sde_crtc.h +++ b/msm/sde/sde_crtc.h @@ -224,6 +224,30 @@ struct sde_crtc_misr_info { */ #define SDE_CRTC_MAX_EVENT_COUNT 16 +/** + * struct sde_frame_data_buffer - defines frame data buffer structure + * @fd: framebuffer id associated with this buffer + * @fb: drm framebuffer for the buffer + * @gem: drm gem handle for he buffer + */ +struct sde_frame_data_buffer { + u32 fd; + struct drm_framebuffer *fb; + struct drm_gem_object *gem; +}; + +/** + * struct sde_frame_data - defines sde frame data structure + * @idx : currently used frame data buffe + * @cnt : rnumber of available frame data buffers + * @buf : list of frame data buffers + */ +struct sde_frame_data { + u32 idx; + u32 cnt; + struct sde_frame_data_buffer *buf[SDE_FRAME_DATA_BUFFER_MAX]; +}; + /** * struct sde_crtc - virtualized CRTC data structure * @base : Base drm crtc structure @@ -303,6 +327,7 @@ struct sde_crtc_misr_info { * @skip_blend_plane_w: skip blend plane width * @skip_blend_plane_h: skip blend plane height * @line_time_in_ns : current mode line time in nano sec is needed for QOS update + * @frame_data : Framedata data structure */ struct sde_crtc { struct drm_crtc base; @@ -402,6 +427,8 @@ struct sde_crtc { u32 skip_blend_plane_w; u32 skip_blend_plane_h; u32 line_time_in_ns; + + struct sde_frame_data frame_data; }; enum sde_crtc_dirty_flags { diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index fee160ccba..c156255802 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -1938,6 +1938,9 @@ static void sde_sspp_set_features(struct sde_mdss_cfg *sde_cfg, SSPP_MAX_PER_PIPE_BW_HIGH, i); else sblk->max_per_pipe_bw_high = sblk->max_per_pipe_bw; + + if (sde_cfg->has_ubwc_stats) + set_bit(SDE_SSPP_UBWC_STATS, &sspp->features); } } @@ -5071,6 +5074,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) set_bit(SDE_MDP_PERIPH_TOP_0_REMOVED, &sde_cfg->mdp[0].features); sde_cfg->has_precise_vsync_ts = true; sde_cfg->has_trusted_vm_support = true; + sde_cfg->has_ubwc_stats = true; } else if (IS_YUPIK_TARGET(hw_rev)) { sde_cfg->has_cwb_support = true; sde_cfg->has_qsync = true; diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 38710bf6b0..c9de968994 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -275,6 +275,7 @@ enum { * @SDE_SSPP_FP16_GC FP16 GC color processing block support * @SDE_SSPP_FP16_CSC FP16 CSC color processing block support * @SDE_SSPP_FP16_UNMULT FP16 alpha unmult color processing block support + * @SDE_SSPP_UBWC_STATS: Support for ubwc stats * @SDE_SSPP_MAX maximum value */ enum { @@ -311,6 +312,7 @@ enum { SDE_SSPP_FP16_GC, SDE_SSPP_FP16_CSC, SDE_SSPP_FP16_UNMULT, + SDE_SSPP_UBWC_STATS, SDE_SSPP_MAX }; @@ -1543,6 +1545,7 @@ struct sde_perf_cfg { * @has_vig_p010 indicates if vig pipe supports p010 format * @has_fp16 indicates if FP16 format is supported on SSPP pipes * @has_precise_vsync_ts indicates if HW has vsyc timestamp logging capability + * @has_ubwc_stats: indicates if ubwc stats feature is supported * @mdss_hw_block_size Max offset of MDSS_HW block (0 offset), used for debug * @inline_rot_formats formats supported by the inline rotator feature * @irq_offset_list list of sde_intr_irq_offsets to initialize irq table @@ -1627,6 +1630,8 @@ struct sde_mdss_cfg { bool has_vig_p010; bool has_fp16; bool has_precise_vsync_ts; + bool has_ubwc_stats; + u32 mdss_hw_block_size; u32 mdss_count; struct sde_mdss_base_cfg mdss[MAX_BLOCKS]; diff --git a/msm/sde/sde_hw_sspp.c b/msm/sde/sde_hw_sspp.c index bb72181728..135933be93 100644 --- a/msm/sde/sde_hw_sspp.c +++ b/msm/sde/sde_hw_sspp.c @@ -77,6 +77,10 @@ #define SSPP_SRC_ADDR_SW_STATUS 0x70 #define SSPP_CREQ_LUT_0 0x74 #define SSPP_CREQ_LUT_1 0x78 +#define SSPP_UBWC_STATS_ROI 0x7C +#define SSPP_UBWC_STATS_DATA 0x80 +#define SSPP_UBWC_STATS_ROI_REC1 0xB4 +#define SSPP_UBWC_STATS_DATA_REC1 0xB8 #define SSPP_SW_PIX_EXT_C0_LR 0x100 #define SSPP_SW_PIX_EXT_C0_TB 0x104 #define SSPP_SW_PIX_EXT_C0_REQ_PIXELS 0x108 @@ -96,8 +100,9 @@ #define SSPP_TRAFFIC_SHAPER_REC1 0x158 #define SSPP_EXCL_REC_SIZE 0x1B4 #define SSPP_EXCL_REC_XY 0x1B8 -#define SSPP_META_ERROR_STATUS_REC1 0x1C4 +#define SSPP_UBWC_STATIC_CTRL_REC1 0x1C0 #define SSPP_UBWC_ERROR_STATUS_REC1 0x1C8 +#define SSPP_META_ERROR_STATUS_REC1 0x1C4 #define SSPP_VIG_OP_MODE 0x0 #define SSPP_VIG_CSC_10_OP_MODE 0x0 #define SSPP_TRAFFIC_SHAPER_BPC_MAX 0xFF @@ -436,7 +441,8 @@ static void sde_hw_sspp_setup_format(struct sde_hw_pipe *ctx, SDE_REG_WRITE(c, SSPP_UBWC_ERROR_STATUS + idx, BIT(31)); } -static void sde_hw_sspp_clear_ubwc_error(struct sde_hw_pipe *ctx, uint32_t multirect_index) +static void sde_hw_sspp_clear_ubwc_error(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index) { struct sde_hw_blk_reg_map *c; @@ -445,7 +451,8 @@ static void sde_hw_sspp_clear_ubwc_error(struct sde_hw_pipe *ctx, uint32_t multi SDE_REG_WRITE(c, SSPP_UBWC_ERROR_STATUS, BIT(31)); } -static u32 sde_hw_sspp_get_ubwc_error(struct sde_hw_pipe *ctx, uint32_t multirect_index) +static u32 sde_hw_sspp_get_ubwc_error(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index) { struct sde_hw_blk_reg_map *c; u32 reg_code; @@ -457,7 +464,8 @@ static u32 sde_hw_sspp_get_ubwc_error(struct sde_hw_pipe *ctx, uint32_t multirec return reg_code; } -static void sde_hw_sspp_clear_ubwc_error_v1(struct sde_hw_pipe *ctx, uint32_t multirect_index) +static void sde_hw_sspp_clear_ubwc_error_v1(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index) { struct sde_hw_blk_reg_map *c; @@ -469,7 +477,8 @@ static void sde_hw_sspp_clear_ubwc_error_v1(struct sde_hw_pipe *ctx, uint32_t mu SDE_REG_WRITE(c, SSPP_UBWC_ERROR_STATUS, BIT(31)); } -static u32 sde_hw_sspp_get_ubwc_error_v1(struct sde_hw_pipe *ctx, uint32_t multirect_index) +static u32 sde_hw_sspp_get_ubwc_error_v1(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index) { struct sde_hw_blk_reg_map *c; u32 reg_code; @@ -484,7 +493,8 @@ static u32 sde_hw_sspp_get_ubwc_error_v1(struct sde_hw_pipe *ctx, uint32_t multi return reg_code; } -static void sde_hw_sspp_clear_meta_error(struct sde_hw_pipe *ctx, uint32_t multirect_index) +static void sde_hw_sspp_clear_meta_error(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index) { struct sde_hw_blk_reg_map *c; @@ -496,7 +506,8 @@ static void sde_hw_sspp_clear_meta_error(struct sde_hw_pipe *ctx, uint32_t multi SDE_REG_WRITE(c, SSPP_META_ERROR_STATUS, BIT(31)); } -static u32 sde_hw_sspp_get_meta_error(struct sde_hw_pipe *ctx, uint32_t multirect_index) +static u32 sde_hw_sspp_get_meta_error(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index) { struct sde_hw_blk_reg_map *c; u32 reg_code; @@ -511,6 +522,75 @@ static u32 sde_hw_sspp_get_meta_error(struct sde_hw_pipe *ctx, uint32_t multirec return reg_code; } +static void sde_hw_sspp_ubwc_stats_set_roi(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index, + struct sde_drm_ubwc_stats_roi *roi) +{ + struct sde_hw_blk_reg_map *c; + u32 idx, ctrl_off, roi_off; + u32 ctrl_val = 0, roi_val = 0; + + if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx)) + return; + + if (multirect_index == SDE_SSPP_RECT_SOLO || multirect_index == SDE_SSPP_RECT_0) { + ctrl_off = SSPP_UBWC_STATIC_CTRL + idx; + roi_off = SSPP_UBWC_STATS_ROI + idx; + } else { + ctrl_off = SSPP_UBWC_STATIC_CTRL_REC1 + idx; + roi_off = SSPP_UBWC_STATS_ROI_REC1 + idx; + } + + c = &ctx->hw; + + ctrl_val = SDE_REG_READ(c, ctrl_off); + + if (roi) { + ctrl_val |= BIT(24); + if (roi->y_coord0) { + ctrl_val |= BIT(25); + roi_val |= roi->y_coord0; + + if (roi->y_coord1) { + ctrl_val |= BIT(26); + roi_val |= (roi->y_coord1) << 0x10; + } + } + } else { + ctrl_val &= ~(BIT(24) | BIT(25) | BIT(26)); + } + + SDE_REG_WRITE(c, ctrl_off, ctrl_val); + SDE_REG_WRITE(c, roi_off, roi_val); +} + +static void sde_hw_sspp_ubwc_stats_get_data(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index, + struct sde_drm_ubwc_stats_data *data) +{ + struct sde_hw_blk_reg_map *c; + u32 idx, value = 0; + int i; + + if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx)) + return; + + if (multirect_index == SDE_SSPP_RECT_SOLO || multirect_index == SDE_SSPP_RECT_0) + idx += SSPP_UBWC_STATS_DATA; + else + idx += SSPP_UBWC_STATS_DATA_REC1; + + c = &ctx->hw; + + for (i = 0; i < UBWC_STATS_MAX_ROI; i++) { + value = SDE_REG_READ(c, idx); + data->worst_bw[i] = value & 0xFFFF; + data->worst_bw_y_coord[i] = (value >> 0x10) & 0xFFFF; + data->total_bw[i] = SDE_REG_READ(c, idx + 4); + idx += 8; + } +} + static void sde_hw_sspp_setup_secure(struct sde_hw_pipe *ctx, enum sde_sspp_multirect_index rect_mode, bool enable) @@ -1373,6 +1453,11 @@ static void _setup_layer_ops(struct sde_hw_pipe *c, c->ops.setup_inverse_pma = sde_hw_sspp_setup_dgm_inverse_pma; else if (test_bit(SDE_SSPP_INVERSE_PMA, &features)) c->ops.setup_inverse_pma = sde_hw_sspp_setup_inverse_pma; + + if (test_bit(SDE_SSPP_UBWC_STATS, &features)) { + c->ops.set_ubwc_stats_roi = sde_hw_sspp_ubwc_stats_set_roi; + c->ops.get_ubwc_stats_data = sde_hw_sspp_ubwc_stats_get_data; + } } static struct sde_sspp_cfg *_sspp_offset(enum sde_sspp sspp, diff --git a/msm/sde/sde_hw_sspp.h b/msm/sde/sde_hw_sspp.h index b5bd819183..d7d1797668 100644 --- a/msm/sde/sde_hw_sspp.h +++ b/msm/sde/sde_hw_sspp.h @@ -596,28 +596,52 @@ struct sde_hw_sspp_ops { * @ctx: Pointer to pipe context * @multirect_index: rec in use */ - void (*clear_meta_error)(struct sde_hw_pipe *ctx, uint32_t multirect_index); + void (*clear_meta_error)(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index); /** * get_meta_error - get the meta error-code * @ctx: Pointer to pipe context * @multirect_index: rec in use */ - u32 (*get_meta_error)(struct sde_hw_pipe *ctx, uint32_t multirect_index); + u32 (*get_meta_error)(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index); /** * clear_ubwc_error - clear the ubwc error-code registers * @ctx: Pointer to pipe context * @multirect_index: rec in use */ - void (*clear_ubwc_error)(struct sde_hw_pipe *ctx, uint32_t multirect_index); + void (*clear_ubwc_error)(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index); /** * get_ubwc_error - get the ubwc error-code * @ctx: Pointer to pipe context * @multirect_index: rec in use */ - u32 (*get_ubwc_error)(struct sde_hw_pipe *ctx, uint32_t multirect_index); + u32 (*get_ubwc_error)(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index); + + /** + * get_ubwc_stats_data - get ubwc stats data + * @ctx: Pointer to pipe context + * @multirect_index: rec in use + * @data: Pointer to ubwc data to populate + */ + void (*get_ubwc_stats_data)(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index, + struct sde_drm_ubwc_stats_data *data); + + /** + * set_ubwc_stats_roi - set ubwc stats roi + * @ctx: Pointer to pipe context + * @multirect_index: rec in use + * @roi: roi to be programmed + */ + void (*set_ubwc_stats_roi)(struct sde_hw_pipe *ctx, + enum sde_sspp_multirect_index multirect_index, + struct sde_drm_ubwc_stats_roi *roi); /** * setup_fp16_csc - set FP16 CSC cp block diff --git a/msm/sde/sde_plane.c b/msm/sde/sde_plane.c index 3c549fff51..a57382b3ca 100644 --- a/msm/sde/sde_plane.c +++ b/msm/sde/sde_plane.c @@ -2854,6 +2854,7 @@ static void _sde_plane_map_prop_to_dirty_bits(void) plane_prop_array[PLANE_PROP_SRC_CONFIG] = plane_prop_array[PLANE_PROP_ZPOS] = plane_prop_array[PLANE_PROP_EXCL_RECT_V1] = + plane_prop_array[PLANE_PROP_UBWC_STATS_ROI] = SDE_PLANE_DIRTY_RECTS; plane_prop_array[PLANE_PROP_CSC_V1] = @@ -3141,6 +3142,15 @@ static void _sde_plane_update_format_and_rects(struct sde_plane *psde, if (psde->pipe_hw->ops.setup_dgm_csc) psde->pipe_hw->ops.setup_dgm_csc(psde->pipe_hw, pstate->multirect_index, psde->csc_usr_ptr); + + if (psde->pipe_hw->ops.set_ubwc_stats_roi) { + if (SDE_FORMAT_IS_UBWC(fmt) && !SDE_FORMAT_IS_YUV(fmt)) + psde->pipe_hw->ops.set_ubwc_stats_roi(psde->pipe_hw, + pstate->multirect_index, &pstate->ubwc_stats_roi); + else + psde->pipe_hw->ops.set_ubwc_stats_roi(psde->pipe_hw, + pstate->multirect_index, NULL); + } } static void _sde_plane_update_sharpening(struct sde_plane *psde) @@ -3862,6 +3872,9 @@ static void _sde_plane_install_properties(struct drm_plane *plane, ARRAY_SIZE(e_fb_translation_mode), 0, PLANE_PROP_FB_TRANSLATION_MODE); + if (psde->pipe_hw->ops.set_ubwc_stats_roi) + msm_property_install_range(&psde->property_info, "ubwc_stats_roi", + 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_UBWC_STATS_ROI); kfree(info); } @@ -4077,6 +4090,30 @@ static void _sde_plane_set_excl_rect_v1(struct sde_plane *psde, pstate->excl_rect.w, pstate->excl_rect.h); } +static void _sde_plane_set_ubwc_stats_roi(struct sde_plane *psde, + struct sde_plane_state *pstate, uint64_t roi) +{ + uint16_t y0, y1; + + if (!psde || !pstate) { + SDE_ERROR("invalid argument(s)\n"); + return; + } + + y0 = roi & 0xFFFF; + y1 = (roi >> 0x10) & 0xFFFF; + + if (y0 > psde->pipe_cfg.src_rect.h || y1 > psde->pipe_cfg.src_rect.h) { + SDE_ERROR_PLANE(psde, "invalid ubwc roi y0 0x%x, y1 0x%x, src height 0x%x", + y0, y1, psde->pipe_cfg.src_rect.h); + y0 = 0; + y1 = 0; + } + + pstate->ubwc_stats_roi.y_coord0 = y0; + pstate->ubwc_stats_roi.y_coord1 = y1; +} + static int sde_plane_atomic_set_property(struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, uint64_t val) @@ -4118,6 +4155,9 @@ static int sde_plane_atomic_set_property(struct drm_plane *plane, _sde_plane_set_excl_rect_v1(psde, pstate, (void *)(uintptr_t)val); break; + case PLANE_PROP_UBWC_STATS_ROI: + _sde_plane_set_ubwc_stats_roi(psde, pstate, val); + break; default: /* nothing to do */ break; @@ -4385,76 +4425,51 @@ static void sde_plane_reset(struct drm_plane *plane) plane->state = &pstate->base; } -u32 sde_plane_get_ubwc_error(struct drm_plane *plane) -{ - u32 ubwc_error = 0; - struct sde_plane *psde; - struct sde_plane_state *pstate; - - if (!plane) { - SDE_ERROR("invalid plane\n"); - return 0; - } - psde = to_sde_plane(plane); - pstate = to_sde_plane_state(plane->state); - - if (!psde->is_virtual && psde->pipe_hw->ops.get_ubwc_error) - ubwc_error = psde->pipe_hw->ops.get_ubwc_error(psde->pipe_hw, - pstate->multirect_index); - - return ubwc_error; -} - -void sde_plane_clear_ubwc_error(struct drm_plane *plane) +void sde_plane_get_frame_data(struct drm_plane *plane, + struct sde_drm_plane_frame_data *data) { struct sde_plane *psde; struct sde_plane_state *pstate; + struct sde_drm_ubwc_stats_data *ubwc_stats; if (!plane) { SDE_ERROR("invalid plane\n"); return; } + psde = to_sde_plane(plane); pstate = to_sde_plane_state(plane->state); + ubwc_stats = &data->ubwc_stats; - if (psde->pipe_hw->ops.clear_ubwc_error) - psde->pipe_hw->ops.clear_ubwc_error(psde->pipe_hw, pstate->multirect_index); -} + data->plane_id = DRMID(plane); -u32 sde_plane_get_meta_error(struct drm_plane *plane) -{ - u32 meta_error = 0; - struct sde_plane *psde; - struct sde_plane_state *pstate; - - if (!plane) { - SDE_ERROR("invalid plane\n"); - return 0; + if (psde->pipe_hw->ops.get_ubwc_stats_data) { + memcpy(&ubwc_stats->roi, &pstate->ubwc_stats_roi, + sizeof(struct sde_drm_ubwc_stats_roi)); + psde->pipe_hw->ops.get_ubwc_stats_data(psde->pipe_hw, + pstate->multirect_index, ubwc_stats); } - psde = to_sde_plane(plane); - pstate = to_sde_plane_state(plane->state); + + if (psde->pipe_hw->ops.get_ubwc_error) + ubwc_stats->error = psde->pipe_hw->ops.get_ubwc_error(psde->pipe_hw, + pstate->multirect_index); + + if (psde->pipe_hw->ops.clear_ubwc_error && ubwc_stats->error) + psde->pipe_hw->ops.clear_ubwc_error(psde->pipe_hw, pstate->multirect_index); if (psde->pipe_hw->ops.get_meta_error) - meta_error = psde->pipe_hw->ops.get_meta_error(psde->pipe_hw, + ubwc_stats->meta_error = psde->pipe_hw->ops.get_meta_error(psde->pipe_hw, pstate->multirect_index); - return meta_error; -} - -void sde_plane_clear_meta_error(struct drm_plane *plane) -{ - struct sde_plane *psde; - struct sde_plane_state *pstate; - - if (!plane) { - SDE_ERROR("invalid plane\n"); - return; - } - psde = to_sde_plane(plane); - pstate = to_sde_plane_state(plane->state); - - if (psde->pipe_hw->ops.clear_meta_error) + if (psde->pipe_hw->ops.clear_meta_error && ubwc_stats->meta_error) psde->pipe_hw->ops.clear_meta_error(psde->pipe_hw, pstate->multirect_index); + + if (ubwc_stats->error || ubwc_stats->meta_error) { + SDE_EVT32(DRMID(plane), ubwc_stats->error, ubwc_stats->meta_error, + SDE_EVTLOG_ERROR); + SDE_DEBUG_PLANE(psde, "plane%d ubwc_error %d meta_error %d\n", + ubwc_stats->error, ubwc_stats->meta_error); + } } #ifdef CONFIG_DEBUG_FS diff --git a/msm/sde/sde_plane.h b/msm/sde/sde_plane.h index ffb3164a11..a0a22afe45 100644 --- a/msm/sde/sde_plane.h +++ b/msm/sde/sde_plane.h @@ -111,6 +111,7 @@ enum sde_plane_sclcheck_state { * @static_cache_state: plane cache state for static image * @cdp_cfg: CDP configuration * @cont_splash_populated: State was populated as part of cont. splash + * @ubwc_stats_roi: cached roi for ubwc stats */ struct sde_plane_state { struct drm_plane_state base; @@ -144,6 +145,8 @@ struct sde_plane_state { struct sde_hw_pipe_cdp_cfg cdp_cfg; bool cont_splash_populated; + + struct sde_drm_ubwc_stats_roi ubwc_stats_roi; }; /** @@ -314,30 +317,13 @@ bool sde_plane_is_sec_ui_allowed(struct drm_plane *plane); */ void sde_plane_secure_ctrl_xin_client(struct drm_plane *plane, struct drm_crtc *crtc); - /* - * sde_plane_get_ubwc_error - gets the ubwc error code + * sde_plane_get_frame_data - gets the plane frame data * @plane: Pointer to DRM plane object + * @frame_data: Pointer to plane frame data structure */ -u32 sde_plane_get_ubwc_error(struct drm_plane *plane); - -/* - * sde_plane_clear_ubwc_error - clears the ubwc error code - * @plane: Pointer to DRM plane object - */ -void sde_plane_clear_ubwc_error(struct drm_plane *plane); - -/* - * sde_plane_get_meta_error - gets the meta error code - * @plane: Pointer to DRM plane object - */ -u32 sde_plane_get_meta_error(struct drm_plane *plane); - -/* - * sde_plane_clear_meta_error - clears the meta error code - * @plane: Pointer to DRM plane object - */ -void sde_plane_clear_meta_error(struct drm_plane *plane); +void sde_plane_get_frame_data(struct drm_plane *plane, + struct sde_drm_plane_frame_data *frame_data); /* * sde_plane_setup_src_split_order - enable/disable pipe's src_split_order