diff --git a/msm/msm_drv.h b/msm/msm_drv.h index c98b71cd95..1f7be9ffd3 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -244,6 +244,8 @@ enum msm_mdp_conn_property { CONNECTOR_PROP_CACHE_STATE, CONNECTOR_PROP_DSC_MODE, CONNECTOR_PROP_WB_USAGE_TYPE, + CONNECTOR_PROP_WB_ROT_TYPE, + CONNECTOR_PROP_WB_ROT_BYTES_PER_CLK, /* total # of properties */ CONNECTOR_PROP_COUNT diff --git a/msm/sde/sde_encoder_phys_wb.c b/msm/sde/sde_encoder_phys_wb.c index 81c00e4f75..6f28523541 100644 --- a/msm/sde/sde_encoder_phys_wb.c +++ b/msm/sde/sde_encoder_phys_wb.c @@ -31,6 +31,13 @@ ((SDE_FORMAT_IS_UBWC(fmt) || SDE_FORMAT_IS_YUV(fmt)) ? wb_cfg->sblk->maxlinewidth : \ wb_cfg->sblk->maxlinewidth_linear) +/* a5x mini-tile width and height */ +#define MINI_TILE_W 4 +#define MINI_TILE_H 4 + +#define SDE_WB_ROT_MAX_SRCW 4096 +#define SDE_WB_ROT_MAX_SRCH 4096 + static const u32 cwb_irq_tbl[PINGPONG_MAX] = {SDE_NONE, INTR_IDX_PP1_OVFL, INTR_IDX_PP2_OVFL, INTR_IDX_PP3_OVFL, INTR_IDX_PP4_OVFL, INTR_IDX_PP5_OVFL, SDE_NONE, SDE_NONE}; @@ -94,8 +101,7 @@ static void sde_encoder_phys_wb_set_ot_limit(struct sde_encoder_phys *phys_enc) ot_params.num = hw_wb->idx - WB_0; ot_params.width = wb_enc->wb_roi.w; ot_params.height = wb_enc->wb_roi.h; - ot_params.is_wfd = ((phys_enc->in_clone_mode) || (usage_type == WB_USAGE_OFFLINE_WB)) ? - false : true; + ot_params.is_wfd = (usage_type == WB_USAGE_WFD); ot_params.frame_rate = drm_mode_vrefresh(&phys_enc->cached_mode); ot_params.vbif_idx = hw_wb->caps->vbif_idx; ot_params.clk_ctrl = hw_wb->caps->clk_ctrl; @@ -146,7 +152,7 @@ static void sde_encoder_phys_wb_set_qos_remap(struct sde_encoder_phys *phys_enc) qos_params.num = hw_wb->idx - WB_0; if (phys_enc->in_clone_mode) qos_params.client_type = VBIF_CWB_CLIENT; - else if (usage_type == WB_USAGE_OFFLINE_WB) + else if (usage_type == WB_USAGE_OFFLINE_WB || usage_type == WB_USAGE_ROT) qos_params.client_type = VBIF_OFFLINE_WB_CLIENT; else qos_params.client_type = VBIF_NRT_CLIENT; @@ -199,13 +205,19 @@ static void sde_encoder_phys_wb_set_qos(struct sde_encoder_phys *phys_enc) } qos_cfg.danger_safe_en = true; + if (usage_type == WB_USAGE_ROT) { + qos_cfg.danger_safe_en = false; + qos_cfg.qos_mode = SDE_WB_QOS_MODE_DYNAMIC; + qos_cfg.bytes_per_clk = sde_connector_get_property(conn_state, + CONNECTOR_PROP_WB_ROT_BYTES_PER_CLK); + } if (phys_enc->in_clone_mode) lut_index = (SDE_FORMAT_IS_TILE(wb_enc->wb_fmt) || SDE_FORMAT_IS_UBWC(wb_enc->wb_fmt)) ? SDE_QOS_LUT_USAGE_CWB_TILE : SDE_QOS_LUT_USAGE_CWB; else - lut_index = (usage_type == WB_USAGE_OFFLINE_WB) ? + lut_index = (usage_type == WB_USAGE_OFFLINE_WB || usage_type == WB_USAGE_ROT) ? SDE_QOS_LUT_USAGE_OFFLINE_WB : SDE_QOS_LUT_USAGE_NRT; creq_index = lut_index * SDE_CREQ_LUT_TYPE_MAX; @@ -336,9 +348,11 @@ static void _sde_enc_phys_wb_get_out_resolution(struct drm_crtc_state *crtc_stat const struct drm_display_mode *mode = &crtc_state->mode; struct sde_io_res ds_res = {0, }, dnsc_blur_res = {0, }; u32 ds_tap_pt = sde_crtc_get_property(cstate, CRTC_PROP_CAPTURE_OUTPUT); + enum sde_wb_rot_type rotation_type; sde_crtc_get_ds_io_res(crtc_state, &ds_res); sde_connector_get_dnsc_blur_io_res(conn_state, &dnsc_blur_res); + rotation_type = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_ROT_TYPE); if (dnsc_blur_res.enabled) { *out_width = dnsc_blur_res.dst_w; @@ -354,10 +368,13 @@ static void _sde_enc_phys_wb_get_out_resolution(struct drm_crtc_state *crtc_stat *out_width = mode->hdisplay; *out_height = mode->vdisplay; } - } else { + } else { *out_width = mode->hdisplay; *out_height = mode->vdisplay; } + + if (rotation_type != WB_ROT_NONE) + swap(*out_width, *out_height); } static void _sde_encoder_phys_wb_setup_cdp(struct sde_encoder_phys *phys_enc, @@ -469,9 +486,10 @@ static void sde_encoder_phys_wb_setup_fb(struct sde_encoder_phys *phys_enc, struct sde_hw_wb *hw_wb; struct sde_hw_wb_cfg *wb_cfg; const struct msm_format *format; - int ret; + enum sde_wb_rot_type rotation_type; struct msm_gem_address_space *aspace; u32 fb_mode; + int ret; if (!phys_enc || !phys_enc->sde_kms || !phys_enc->sde_kms->catalog || !phys_enc->connector) { @@ -514,6 +532,17 @@ static void sde_encoder_phys_wb_setup_fb(struct sde_encoder_phys *phys_enc, return; } + rotation_type = sde_connector_get_property(phys_enc->connector->state, + CONNECTOR_PROP_WB_ROT_TYPE); + wb_cfg->rotate_90 = (rotation_type != WB_ROT_NONE); + + SDE_DEBUG("[enc:%d wb:%d] conn:%d rotation_type:%d format %4.4s and modifier 0x%llX\n", + DRMID(phys_enc->parent), WBID(wb_enc), DRMID(phys_enc->connector), + rotation_type, (char *)&format->pixel_format, fb->modifier); + + SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), rotation_type, out_width, out_height, + fb->width, fb->height); + wb_cfg->dest.format = sde_get_sde_format_ext(format->pixel_format, fb->modifier); if (!wb_cfg->dest.format) { /* this error should be detected during atomic_check */ @@ -803,13 +832,16 @@ static int _sde_enc_phys_wb_validate_dnsc_blur_ds(struct drm_crtc_state *crtc_st struct sde_crtc_state *cstate = to_sde_crtc_state(crtc_state); const struct drm_display_mode *mode = &crtc_state->mode; struct sde_io_res ds_res = {0, }, dnsc_blur_res = {0, }; + enum sde_wb_rot_type rotation_type; u32 ds_tap_pt = sde_crtc_get_property(cstate, CRTC_PROP_CAPTURE_OUTPUT); sde_crtc_get_ds_io_res(crtc_state, &ds_res); sde_connector_get_dnsc_blur_io_res(conn_state, &dnsc_blur_res); + rotation_type = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_ROT_TYPE); /* wb_roi should match with mode w/h if none of these features are enabled */ - if ((!ds_res.enabled && !dnsc_blur_res.enabled && !cstate->cwb_enc_mask) + if ((rotation_type == WB_ROT_NONE) && + (!ds_res.enabled && !dnsc_blur_res.enabled && !cstate->cwb_enc_mask) && ((wb_roi->w && (wb_roi->w != mode->hdisplay)) || (wb_roi->h && (wb_roi->h != mode->vdisplay)))) { SDE_ERROR("invalid wb-roi {%u,%u,%u,%u} mode:%ux%u\n", @@ -849,8 +881,16 @@ static int _sde_enc_phys_wb_validate_dnsc_blur_ds(struct drm_crtc_state *crtc_st } else if (SDE_FORMAT_IS_YUV(fmt)) { SDE_ERROR("YUV output not supported with dnsc_blur\n"); return -EINVAL; - } else if ((wb_roi->w && (wb_roi->w != dnsc_blur_res.dst_w)) || - (wb_roi->h && (wb_roi->h != dnsc_blur_res.dst_h))) { + } else if ((rotation_type != WB_ROT_NONE) && + ((wb_roi->w && (wb_roi->w != dnsc_blur_res.dst_h)) || + (wb_roi->h && (wb_roi->h != dnsc_blur_res.dst_w)))) { + SDE_ERROR("invalid WB ROI for dnsc and rotate, roi:{%d,%d,%d,%d}, dnsc dst:%ux%u\n", + wb_roi->x, wb_roi->y, wb_roi->w, wb_roi->h, + dnsc_blur_res.dst_w, dnsc_blur_res.dst_h); + return -EINVAL; + } else if ((rotation_type == WB_ROT_NONE) && + ((wb_roi->w && (wb_roi->w != dnsc_blur_res.dst_w)) || + (wb_roi->h && (wb_roi->h != dnsc_blur_res.dst_h)))) { SDE_ERROR("invalid WB ROI with dnsc_blur, roi:{%d,%d,%d,%d}, dnsc_blur dst:%ux%u\n", wb_roi->x, wb_roi->y, wb_roi->w, wb_roi->h, dnsc_blur_res.dst_w, dnsc_blur_res.dst_h); @@ -966,6 +1006,138 @@ static int _sde_enc_phys_wb_validate_cwb(struct sde_encoder_phys *phys_enc, return ret; } +static int _sde_encoder_phys_wb_validate_rotation(struct sde_encoder_phys *phys_enc, + struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) +{ + enum sde_wb_rot_type rotation_type; + int ret = 0; + u32 src_w, src_h; + u32 bytes_per_clk; + struct sde_rect wb_src, wb_roi = {0,}; + struct sde_io_res dnsc_res = {0,}; + const struct sde_rect *crtc_roi = NULL; + struct drm_display_mode *mode; + enum sde_wb_usage_type usage_type; + struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc); + + rotation_type = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_ROT_TYPE); + if (rotation_type == WB_ROT_NONE) + return ret; + + usage_type = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_USAGE_TYPE); + if (usage_type != WB_USAGE_ROT) { + SDE_ERROR("[enc:%d wb:%d] invalid WB usage_ype:%d for rotation_type:%d\n", + DRMID(phys_enc->parent), WBID(wb_enc), usage_type, rotation_type); + return -EINVAL; + } + + bytes_per_clk = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_ROT_BYTES_PER_CLK); + if (!bytes_per_clk) { + SDE_ERROR("[enc:%d wb:%d] WB output bytes per XO clock is must for rotation\n", + DRMID(phys_enc->parent), WBID(wb_enc)); + return -EINVAL; + } + + ret = sde_wb_connector_state_get_output_roi(conn_state, &wb_roi); + if (ret) { + SDE_ERROR("[enc:%d wb:%d] failed to get WB output roi, ret:%d\n", + DRMID(phys_enc->parent), WBID(wb_enc), ret); + return ret; + } + + sde_crtc_get_crtc_roi(crtc_state, &crtc_roi); + if (!crtc_roi) { + SDE_ERROR("[enc:%d wb:%d] could not get crtc roi\n", + DRMID(phys_enc->parent), WBID(wb_enc)); + return -EINVAL; + } else if (!sde_kms_rect_is_null(crtc_roi)) { + SDE_ERROR("[enc:%d wb:%d] not supporting pu scenario on wb\n", + DRMID(phys_enc->parent), WBID(wb_enc)); + return -EINVAL; + } + + mode = &crtc_state->mode; + sde_crtc_get_resolution(crtc_state->crtc, crtc_state, mode, &src_w, &src_h); + if (!src_w || !src_h) { + SDE_ERROR("[enc:%d wb:%d] invalid wb input dimensions src_w:%d src_h:%d\n", + DRMID(phys_enc->parent), WBID(wb_enc), src_w, src_h); + return -EINVAL; + } + + sde_connector_get_dnsc_blur_io_res(conn_state, &dnsc_res); + wb_src.w = dnsc_res.enabled ? dnsc_res.dst_w : src_w; + wb_src.h = dnsc_res.enabled ? dnsc_res.dst_h : src_h; + + SDE_DEBUG("[enc:%d wb:%d] wb_src=[%dx%d] dnsc_dst=[%dx%d] wb_roi=[%dx%d]\n", + DRMID(phys_enc->parent), WBID(wb_enc), wb_src.w, wb_src.h, + dnsc_res.dst_w, dnsc_res.dst_h, wb_roi.w, wb_roi.h); + + if (((wb_src.w != wb_roi.h) || (wb_src.h != wb_roi.w))) { + SDE_ERROR("[enc:%d wb:%d] invalid dimension for rotation src:%dx%d vs out:%dx%d\n", + DRMID(phys_enc->parent), WBID(wb_enc), wb_src.w, wb_src.h, + wb_roi.w, wb_roi.h); + return -EINVAL; + } else if ((wb_roi.x % MINI_TILE_W) || (wb_roi.y % MINI_TILE_H)) { + SDE_ERROR("[enc:%d wb:%d] unaligned x,y offsets for rotation:%d x:%d y:%d\n", + DRMID(phys_enc->parent), WBID(wb_enc), rotation_type, + wb_roi.x, wb_roi.y); + return -EINVAL; + } else if ((rotation_type == WB_ROT_JOB1) && (wb_roi.h % MINI_TILE_H)) { + SDE_ERROR("[enc:%d wb:%d] job1 rotation height:%d is not tile aligned\n", + DRMID(phys_enc->parent), WBID(wb_enc), wb_roi.h); + return -EINVAL; + } else if (wb_src.w > SDE_WB_ROT_MAX_SRCW || wb_src.h > SDE_WB_ROT_MAX_SRCH) { + SDE_ERROR("[enc:%d wb:%d] rotate limit exceeded srcw:[%d vs %d], srch:[%d vs %d]\n", + DRMID(phys_enc->parent), WBID(wb_enc), wb_src.w, SDE_WB_ROT_MAX_SRCW, + wb_src.h, SDE_WB_ROT_MAX_SRCH); + return -EINVAL; + } + + return ret; +} + +static int _sde_encoder_phys_wb_validate_output_fmt(struct sde_encoder_phys *phys_enc, + struct drm_framebuffer *fb, enum sde_wb_rot_type rotation_type) +{ + int ret = 0; + struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc); + const struct sde_format *fmt; + const struct sde_format_extended *format_list; + struct sde_hw_wb *hw_wb = wb_enc->hw_wb; + const struct sde_wb_cfg *wb_cfg = hw_wb->caps; + struct sde_kms *sde_kms = phys_enc->sde_kms; + + fmt = sde_get_sde_format_ext(fb->format->format, fb->modifier); + if (!fmt) { + SDE_ERROR("[enc:%d wb:%d] invalid output pixel format:0x%x mod:0x%x\n", + DRMID(phys_enc->parent), WBID(wb_enc), + fb->format->format, fb->modifier); + return -EINVAL; + } + + /* find if sde format is listed as supported format on WB */ + format_list = (rotation_type != WB_ROT_NONE) ? + wb_cfg->rot_format_list : wb_cfg->format_list; + + ret = sde_format_validate_fmt(&sde_kms->base, fmt, format_list); + if (ret) { + SDE_ERROR("[enc:%d wb:%d] unsupported format for wb rotate:%d fmt:0x%x mod:0x%x\n", + DRMID(phys_enc->parent), WBID(wb_enc), rotation_type, + fb->format->format, fb->modifier); + return ret; + } else if (fmt->chroma_sample == SDE_CHROMA_H2V1 || fmt->chroma_sample == SDE_CHROMA_H1V2) { + SDE_ERROR("[enc:%d wb:%d] invalid chroma sample type in output format:%x\n", + DRMID(phys_enc->parent), WBID(wb_enc), fmt->base.pixel_format); + return -EINVAL; + } else if (SDE_FORMAT_IS_UBWC(fmt) && !(wb_cfg->features & BIT(SDE_WB_UBWC))) { + SDE_ERROR("[enc:%d wb:%d] invalid output format:%x\n", + DRMID(phys_enc->parent), WBID(wb_enc), fmt->base.pixel_format); + return -EINVAL; + } + + return ret; +} + /** * sde_encoder_phys_wb_atomic_check - verify and fixup given atomic states * @phys_enc: Pointer to physical encoder @@ -987,6 +1159,7 @@ static int sde_encoder_phys_wb_atomic_check(struct sde_encoder_phys *phys_enc, const struct drm_display_mode *mode = &crtc_state->mode; int rc; bool clone_mode_curr = false; + enum sde_wb_rot_type rotation_type; SDE_DEBUG("[enc:%d wb:%d] atomic_check:\"%s\",%d,%d]\n", DRMID(phys_enc->parent), WBID(wb_enc), mode->name, mode->hdisplay, mode->vdisplay); @@ -1028,28 +1201,27 @@ static int sde_encoder_phys_wb_atomic_check(struct sde_encoder_phys *phys_enc, return -EINVAL; } + rotation_type = sde_connector_get_property(conn_state, CONNECTOR_PROP_WB_ROT_TYPE); + fmt = sde_get_sde_format_ext(fb->format->format, fb->modifier); if (!fmt) { - SDE_ERROR("[enc:%d wb:%d] unsupported output pixel format:%x\n", - DRMID(phys_enc->parent), WBID(wb_enc), fb->format->format); + SDE_ERROR("[enc:%d wb:%d] invalid output pixel format:0x%x mod:0x%x\n", + DRMID(phys_enc->parent), WBID(wb_enc), + fb->format->format, fb->modifier); return -EINVAL; } - SDE_DEBUG("[enc:%d enc:%d] fb_id:%u, wxh:%ux%u, fb_fmt:%x,%llx, roi:{%d,%d,%d,%d}\n", + SDE_DEBUG("[enc:%d wb:%d] fb_id:%u, wxh:%ux%u, fb_fmt:%x,%llx, roi:{%d,%d,%d,%d}, rot:%d\n", DRMID(phys_enc->parent), WBID(wb_enc), fb->base.id, fb->width, fb->height, - fb->format->format, fb->modifier, wb_roi.x, wb_roi.y, wb_roi.w, wb_roi.h); + fb->format->format, fb->modifier, wb_roi.x, wb_roi.y, wb_roi.w, wb_roi.h, + rotation_type); - if (fmt->chroma_sample == SDE_CHROMA_H2V1 || - fmt->chroma_sample == SDE_CHROMA_H1V2) { - SDE_ERROR("[enc:%d wb:%d] invalid chroma sample type in output format:%x\n", - DRMID(phys_enc->parent), WBID(wb_enc), fmt->base.pixel_format); - return -EINVAL; - } - - if (SDE_FORMAT_IS_UBWC(fmt) && !(wb_cfg->features & BIT(SDE_WB_UBWC))) { - SDE_ERROR("[enc:%d wb:%d] invalid output format:%x\n", - DRMID(phys_enc->parent), WBID(wb_enc), fmt->base.pixel_format); - return -EINVAL; + rc = _sde_encoder_phys_wb_validate_output_fmt(phys_enc, fb, rotation_type); + if (rc) { + SDE_ERROR("[enc:%d wb:%d] output fmt validation failed fb:%u fmt:0x%x mod:0x%x\n", + DRMID(phys_enc->parent), WBID(wb_enc), fb->base.id, + fb->format->format, fb->modifier, rotation_type); + return rc; } if (SDE_FORMAT_IS_YUV(fmt) != !!phys_enc->hw_cdm) @@ -1072,6 +1244,15 @@ static int sde_encoder_phys_wb_atomic_check(struct sde_encoder_phys *phys_enc, return rc; } + if (rotation_type != WB_ROT_NONE) { + rc = _sde_encoder_phys_wb_validate_rotation(phys_enc, crtc_state, conn_state); + if (rc) { + SDE_ERROR("[enc:%d wb:%d] failed in WB rotation validation %d\n", + DRMID(phys_enc->parent), WBID(wb_enc), rc); + return rc; + } + } + _sde_enc_phys_wb_get_out_resolution(crtc_state, conn_state, &out_width, &out_height); if (!wb_roi.w || !wb_roi.h) { wb_roi.x = 0; @@ -1080,17 +1261,17 @@ static int sde_encoder_phys_wb_atomic_check(struct sde_encoder_phys *phys_enc, wb_roi.h = out_height; } - if ((wb_roi.x + wb_roi.w > fb->width) || (wb_roi.x + wb_roi.w > out_width)) { + if ((wb_roi.x + wb_roi.w > fb->width) || (wb_roi.w > out_width)) { SDE_ERROR("[enc:%d wb:%d] invalid roi x:%d, w:%d, fb_w:%d, mode_w:%d, out_w:%d\n", DRMID(phys_enc->parent), WBID(wb_enc), wb_roi.x, wb_roi.w, fb->width, mode->hdisplay, out_width); return -EINVAL; - } else if ((wb_roi.y + wb_roi.h > fb->height) || (wb_roi.y + wb_roi.h > out_height)) { + } else if ((wb_roi.y + wb_roi.h > fb->height) || (wb_roi.h > out_height)) { SDE_ERROR("[enc:%d wb:%d] invalid roi y:%d, h:%d, fb_h:%d, mode_h%d, out_h:%d\n", DRMID(phys_enc->parent), WBID(wb_enc), wb_roi.y, wb_roi.h, fb->height, mode->vdisplay, out_height); return -EINVAL; - } else if ((out_width > mode->hdisplay) || (out_height > mode->vdisplay)) { + } else if ((rotation_type == WB_ROT_NONE) && ((out_width > mode->hdisplay) || (out_height > mode->vdisplay))) { SDE_ERROR("[enc:%d wb:%d] invalid o w/h o_w:%d, mode_w:%d, o_h:%d, mode_h:%d\n", DRMID(phys_enc->parent), WBID(wb_enc), out_width, mode->hdisplay, out_height, mode->vdisplay); diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index 9821a04831..592bf0eb06 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -2550,6 +2550,11 @@ static int sde_wb_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg) sde_cfg->mdp[0].clk_ctrls[wb->clk_ctrl].bit_off); } + if (test_bit(SDE_FEATURE_WB_ROTATION, sde_cfg->features)) { + set_bit(SDE_WB_LINEAR_ROTATION, &wb->features); + wb->rot_format_list = sde_cfg->wb_rot_formats; + } + wb->format_list = sde_cfg->wb_formats; } @@ -4665,7 +4670,7 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) { int rc = 0; - uint32_t dma_list_size, vig_list_size, wb2_list_size; + uint32_t dma_list_size, vig_list_size, wb2_list_size, wb_rot_fmt_list_size; uint32_t virt_vig_list_size, in_rot_list_size = 0; uint32_t index = 0; uint32_t in_rot_restricted_list_size = 0; @@ -4746,6 +4751,18 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg, index = sde_copy_formats(sde_cfg->wb_formats, wb2_list_size, 0, wb2_formats, ARRAY_SIZE(wb2_formats)); + /* WB rotation output formats */ + wb_rot_fmt_list_size = ARRAY_SIZE(wb_rot_formats); + sde_cfg->wb_rot_formats = kcalloc(wb_rot_fmt_list_size, + sizeof(struct sde_format_extended), GFP_KERNEL); + if (!sde_cfg->wb_rot_formats) { + rc = -ENOMEM; + goto free_wb; + } + + index = sde_copy_formats(sde_cfg->wb_rot_formats, wb_rot_fmt_list_size, + 0, wb_rot_formats, ARRAY_SIZE(wb_rot_formats)); + /* Rotation enabled input formats */ if (IS_SDE_INLINE_ROT_REV_100(sde_cfg->true_inline_rot_rev)) { inline_fmt_tbl = true_inline_rot_v1_fmts; @@ -4766,7 +4783,7 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg, if (!sde_cfg->inline_rot_formats) { SDE_ERROR("failed to alloc inline rot format list\n"); rc = -ENOMEM; - goto free_wb; + goto free_wb_rot; } index = sde_copy_formats(sde_cfg->inline_rot_formats, @@ -4790,6 +4807,8 @@ static int sde_hardware_format_caps(struct sde_mdss_cfg *sde_cfg, return 0; free_in_rot: kfree(sde_cfg->inline_rot_formats); +free_wb_rot: + kfree(sde_cfg->wb_rot_formats); free_wb: kfree(sde_cfg->wb_formats); free_virt: @@ -5234,6 +5253,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) set_bit(SDE_FEATURE_TRUSTED_VM, sde_cfg->features); set_bit(SDE_SYS_CACHE_DISP, sde_cfg->sde_sys_cache_type_map); set_bit(SDE_SYS_CACHE_DISP_WB, sde_cfg->sde_sys_cache_type_map); + set_bit(SDE_FEATURE_WB_ROTATION, sde_cfg->features); sde_cfg->allowed_dsc_reservation_switch = SDE_DP_DSC_RESERVATION_SWITCH; sde_cfg->autorefresh_disable_seq = AUTOREFRESH_DISABLE_SEQ2; sde_cfg->perf.min_prefill_lines = 40; @@ -5418,6 +5438,7 @@ void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg) kfree(sde_cfg->dma_formats); kfree(sde_cfg->vig_formats); kfree(sde_cfg->wb_formats); + kfree(sde_cfg->wb_rot_formats); kfree(sde_cfg->virt_vig_formats); kfree(sde_cfg->inline_rot_formats); diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 90541b7fcc..84cb0efd0a 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -637,6 +637,7 @@ enum { * @SDE_WB_DCWB_CTRL Separate DCWB control is available for configuring * @SDE_WB_CWB_DITHER_CTRL CWB dither is available for configuring * @SDE_WB_PROG_LINE Writeback block supports programmable line ptr + * @SDE_WB_LINEAR_ROTATION Writeback block supports line mode image rotation * @SDE_WB_MAX maximum value */ enum { @@ -661,6 +662,7 @@ enum { SDE_WB_DCWB_CTRL, SDE_WB_CWB_DITHER_CTRL, SDE_WB_PROG_LINE, + SDE_WB_LINEAR_ROTATION, SDE_WB_MAX }; @@ -708,7 +710,7 @@ enum { * @SDE_FEATURE_CWB_DITHER CWB dither is supported * @SDE_FEATURE_DEDICATED_CWB Dedicated-CWB supported * @SDE_FEATURE_DUAL_DEDICATED_CWB Dual Dedicated-CWB supported - * @SDE_FEATURE_IDLE_PC Idle Power Collapse supported + * @SDE_FEATURE_WB_ROTATION Support for image rotation through WB block * @SDE_FEATURE_3D_MERGE_RESET 3D merge reset supported * @SDE_FEATURE_DECIMATION Decimation supported * @SDE_FEATURE_COMBINED_ALPHA Combined Alpha supported @@ -751,6 +753,7 @@ enum sde_mdss_features { SDE_FEATURE_CWB_DITHER, SDE_FEATURE_DEDICATED_CWB, SDE_FEATURE_DUAL_DEDICATED_CWB, + SDE_FEATURE_WB_ROTATION, SDE_FEATURE_IDLE_PC, SDE_FEATURE_3D_MERGE_RESET, SDE_FEATURE_DECIMATION, @@ -1484,7 +1487,8 @@ struct sde_intf_cfg { * @base register offset of this block * @features bit mask identifying sub-blocks/features * @sblk sub-block information - * @format_list: Pointer to list of supported formats + * @format_list: Pointer to list of supported output formats + * @rot_format_list: Pointer to list of supported output formats in WB rotation * @vbif_idx vbif identifier * @xin_id client interface identifier * @clk_ctrl clock control identifier @@ -1493,6 +1497,7 @@ struct sde_wb_cfg { SDE_HW_BLK_INFO; const struct sde_wb_sub_blocks *sblk; const struct sde_format_extended *format_list; + const struct sde_format_extended *rot_format_list; u32 vbif_idx; u32 xin_id; enum sde_clk_ctrl_type clk_ctrl; @@ -1864,6 +1869,7 @@ struct sde_perf_cfg { * @dma_formats supported formats for dma pipe * @vig_formats supported formats for vig pipe * @wb_formats supported formats for wb + * @wb_rot_formats supported output formats for wb rotation operation * @virt_vig_formats supported formats for virtual vig pipe * @inline_rot_formats supported formats for inline rotation * @inline_rot_restricted_formats restricted formats for inline rotation @@ -1982,6 +1988,7 @@ struct sde_mdss_cfg { struct sde_format_extended *dma_formats; struct sde_format_extended *vig_formats; struct sde_format_extended *wb_formats; + struct sde_format_extended *wb_rot_formats; struct sde_format_extended *virt_vig_formats; struct sde_format_extended *inline_rot_formats; struct sde_format_extended *inline_rot_restricted_formats; diff --git a/msm/sde/sde_hw_catalog_format.h b/msm/sde/sde_hw_catalog_format.h index caeb7fd6b3..063054ea68 100644 --- a/msm/sde/sde_hw_catalog_format.h +++ b/msm/sde/sde_hw_catalog_format.h @@ -94,6 +94,10 @@ static const struct sde_format_extended wb2_formats[] = { {DRM_FORMAT_ARGB8888, 0}, {DRM_FORMAT_RGBA8888, 0}, {DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, + {DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_TILE}, + {DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_TILE}, + {DRM_FORMAT_ABGR2101010, DRM_FORMAT_MOD_QCOM_TILE}, + {DRM_FORMAT_XBGR2101010, DRM_FORMAT_MOD_QCOM_TILE}, {DRM_FORMAT_XRGB8888, 0}, {DRM_FORMAT_RGBX8888, 0}, {DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_COMPRESSED}, @@ -131,6 +135,14 @@ static const struct sde_format_extended wb2_formats[] = { {0, 0}, }; +static const struct sde_format_extended wb_rot_formats[] = { + {DRM_FORMAT_ABGR8888, DRM_FORMAT_MOD_QCOM_TILE}, + {DRM_FORMAT_XBGR8888, DRM_FORMAT_MOD_QCOM_TILE}, + {DRM_FORMAT_ABGR2101010, DRM_FORMAT_MOD_QCOM_TILE}, + {DRM_FORMAT_XBGR2101010, DRM_FORMAT_MOD_QCOM_TILE}, + {0, 0}, +}; + static const struct sde_format_extended p010_ubwc_formats[] = { P010_UBWC_FMTS, }; diff --git a/msm/sde/sde_hw_mdss.h b/msm/sde/sde_hw_mdss.h index 6249c4dd04..d349cf0cb4 100644 --- a/msm/sde/sde_hw_mdss.h +++ b/msm/sde/sde_hw_mdss.h @@ -503,11 +503,27 @@ enum sde_sys_cache_state { * WB_USAGE_WFD: WB connector used for WFD * WB_USAGE_CWB: WB connector used for concurrent writeback * WB_USAGE_OFFLINE_WB: WB connector used for 2-pass composition + * WB_USAGE_ROT: WB connector used for image rotation for 2 pass composition */ enum sde_wb_usage_type { WB_USAGE_WFD, WB_USAGE_CWB, WB_USAGE_OFFLINE_WB, + WB_USAGE_ROT, +}; + +/** + * enum sde_wb_rot_type: Type of rotation use case of the WB connector + * WB_ROT_NONE : WB Rotation not in use + * WB_ROT_SINGLE: WB Rotation used in single job mode for full image rotation + * WB_ROT_JOB1: WB Rotation used for rotating half image as first-job + * WB_ROT_JOB2: WB Rotation used for rotating half image as second-job + */ +enum sde_wb_rot_type { + WB_ROT_NONE, + WB_ROT_SINGLE, + WB_ROT_JOB1, + WB_ROT_JOB2, }; /** struct sde_format - defines the format configuration which diff --git a/msm/sde/sde_hw_wb.c b/msm/sde/sde_hw_wb.c index 42383b0c43..4c010451ee 100644 --- a/msm/sde/sde_hw_wb.c +++ b/msm/sde/sde_hw_wb.c @@ -23,6 +23,7 @@ #define WB_DST3_ADDR 0x018 #define WB_DST_YSTRIDE0 0x01C #define WB_DST_YSTRIDE1 0x020 +#define WB_TS_WR_CLIENT 0x040 #define WB_DST_WRITE_CONFIG 0x048 #define WB_OUT_SIZE 0x074 #define WB_ALPHA_X_VALUE 0x078 @@ -200,6 +201,13 @@ static void sde_hw_wb_setup_format(struct sde_hw_wb *ctx, if (SDE_FORMAT_IS_DX(fmt)) dst_format |= BIT(21); + /* Set A5x tile bit for uncompressed tile formats also */ + if (SDE_FORMAT_IS_TILE(fmt)) + dst_format |= BIT(31); + + if (data->rotate_90) + dst_format |= BIT(11); + pattern = (fmt->element[3] << 24) | (fmt->element[2] << 16) | (fmt->element[1] << 8) | @@ -300,6 +308,11 @@ static void sde_hw_wb_setup_qos_lut(struct sde_hw_wb *ctx, if (cfg->danger_safe_en) qos_ctrl |= WB_QOS_CTRL_DANGER_SAFE_EN; + if (test_bit(SDE_WB_LINEAR_ROTATION, &ctx->caps->features)) { + SDE_REG_WRITE(c, WB_TS_WR_CLIENT, cfg->bytes_per_clk & 0xFF); + qos_ctrl |= (cfg->qos_mode << 1); + } + SDE_REG_WRITE(c, WB_QOS_CTRL, qos_ctrl); } diff --git a/msm/sde/sde_hw_wb.h b/msm/sde/sde_hw_wb.h index 0ace743978..87c5400038 100644 --- a/msm/sde/sde_hw_wb.h +++ b/msm/sde/sde_hw_wb.h @@ -22,6 +22,7 @@ struct sde_hw_wb_cfg { struct sde_rect roi; struct sde_rect crop; bool is_secure; + bool rotate_90; }; /** @@ -48,17 +49,31 @@ struct sde_hw_wb_cdp_cfg { u32 preload_ahead; }; +/** + * enum sde_hw_wb_qos_mode: enumeration of available QOS modes for WB + * @SDE_WB_QOS_MODE_STATIC: static qos mode same as existing NRT qos mode + * @SDE_WB_QOS_MODE_DYNAMIC: new qos mode to support rotation for real time + */ +enum sde_hw_wb_qos_mode { + SDE_WB_QOS_MODE_STATIC, + SDE_WB_QOS_MODE_DYNAMIC, +}; + /** * struct sde_hw_wb_qos_cfg : Writeback pipe QoS configuration * @danger_lut: LUT for generate danger level based on fill level * @safe_lut: LUT for generate safe level based on fill level * @creq_lut: LUT for generate creq level based on fill level + * @bytes_per_clk: WB output bytes per XO clock value used in rotation + * @qos_mode: enum value mapped for selecting WB QOS mode * @danger_safe_en: enable danger safe generation */ struct sde_hw_wb_qos_cfg { u32 danger_lut; u32 safe_lut; u64 creq_lut; + u32 bytes_per_clk; + enum sde_hw_wb_qos_mode qos_mode; bool danger_safe_en; }; diff --git a/msm/sde/sde_wb.c b/msm/sde/sde_wb.c index 27a1537b57..8bd5d3a37c 100644 --- a/msm/sde/sde_wb.c +++ b/msm/sde/sde_wb.c @@ -532,6 +532,18 @@ int sde_wb_connector_set_info_blob(struct drm_connector *connector, sde_kms_info_stop(info); } + /* Populate info buffer with WB rotation output formats */ + format_list = wb_dev->wb_cfg->rot_format_list; + if (format_list) { + sde_kms_info_start(info, "rot_output_formats"); + while (format_list->fourcc_format) { + sde_kms_info_append_format(info, format_list->fourcc_format, + format_list->modifier); + ++format_list; + } + sde_kms_info_stop(info); + } + sde_kms_info_add_keyint(info, "wb_intf_index", wb_dev->wb_idx - WB_0); sde_kms_info_add_keyint(info, "maxlinewidth", wb_dev->wb_cfg->sblk->maxlinewidth); sde_kms_info_add_keyint(info, "maxlinewidth_linear", @@ -609,6 +621,14 @@ int sde_wb_connector_post_init(struct drm_connector *connector, void *display) {WB_USAGE_WFD, "wb_usage_wfd"}, {WB_USAGE_CWB, "wb_usage_cwb"}, {WB_USAGE_OFFLINE_WB, "wb_usage_offline_wb"}, + {WB_USAGE_ROT, "wb_usage_rot"}, + }; + + static const struct drm_prop_enum_list e_wb_rotate_type[] = { + {WB_ROT_NONE, "wb_rot_none"}, + {WB_ROT_SINGLE, "wb_rot_single"}, + {WB_ROT_JOB1, "wb_rot_job1"}, + {WB_ROT_JOB2, "wb_rot_job2"}, }; if (!connector || !display || !wb_dev->wb_cfg || !wb_dev->drm_dev->dev_private) { @@ -662,6 +682,15 @@ int sde_wb_connector_post_init(struct drm_connector *connector, void *display) msm_property_install_range(&c_conn->property_info, "dnsc_blur", 0x0, 0, ~0, 0, CONNECTOR_PROP_DNSC_BLUR); + if (wb_dev->wb_cfg->features & BIT(SDE_WB_LINEAR_ROTATION)) { + msm_property_install_enum(&c_conn->property_info, "wb_rotate_type", + 0x0, 0, e_wb_rotate_type, ARRAY_SIZE(e_wb_rotate_type), + 0, CONNECTOR_PROP_WB_ROT_TYPE); + + msm_property_install_range(&c_conn->property_info, "wb_rot_bytes_per_clk", + 0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_WB_ROT_BYTES_PER_CLK); + } + msm_property_install_enum(&c_conn->property_info, "wb_usage_type", 0x0, 0, e_wb_usage_type, ARRAY_SIZE(e_wb_usage_type), 0, CONNECTOR_PROP_WB_USAGE_TYPE);