diff --git a/msm/Makefile b/msm/Makefile index 51b0edbf245a..18eb12c35530 100644 --- a/msm/Makefile +++ b/msm/Makefile @@ -70,6 +70,7 @@ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \ sde/sde_hw_qdss.o \ sde_dbg.o \ sde_dbg_evtlog.o \ + sde/sde_hw_rc.o \ msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \ sde/sde_encoder_phys_wb.o \ diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 20969665356c..4ba732d86d7c 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -138,6 +138,7 @@ enum msm_mdp_crtc_property { CRTC_PROP_DEST_SCALER_LUT_ED, CRTC_PROP_DEST_SCALER_LUT_CIR, CRTC_PROP_DEST_SCALER_LUT_SEP, + CRTC_PROP_DSPP_INFO, /* # of blob properties */ CRTC_PROP_BLOBCOUNT, diff --git a/msm/sde/sde_color_processing.c b/msm/sde/sde_color_processing.c index 6777d0e4c31b..4cf87d4dca96 100644 --- a/msm/sde/sde_color_processing.c +++ b/msm/sde/sde_color_processing.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ #define pr_fmt(fmt) "%s: " fmt, __func__ #include +#include #include #include "sde_color_processing.h" #include "sde_kms.h" @@ -29,6 +30,7 @@ struct sde_cp_node { struct list_head active_list; struct list_head dirty_list; bool is_dspp_feature; + bool lm_flush_override; u32 prop_blob_sz; struct sde_irq_callback *irq; }; @@ -55,6 +57,8 @@ static void dspp_ad_install_property(struct drm_crtc *crtc); static void dspp_ltm_install_property(struct drm_crtc *crtc); +static void dspp_rc_install_property(struct drm_crtc *crtc); + static void dspp_vlut_install_property(struct drm_crtc *crtc); static void dspp_gamut_install_property(struct drm_crtc *crtc); @@ -111,6 +115,7 @@ do { \ func[SDE_DSPP_IGC] = dspp_igc_install_property; \ func[SDE_DSPP_HIST] = dspp_hist_install_property; \ func[SDE_DSPP_DITHER] = dspp_dither_install_property; \ + func[SDE_DSPP_RC] = dspp_rc_install_property; \ } while (0) typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc); @@ -122,7 +127,7 @@ static void lm_gc_install_property(struct drm_crtc *crtc); #define setup_lm_prop_install_funcs(func) \ (func[SDE_MIXER_GC] = lm_gc_install_property) -enum { +enum sde_cp_crtc_features { /* Append new DSPP features before SDE_CP_CRTC_DSPP_MAX */ /* DSPP Features start */ SDE_CP_CRTC_DSPP_IGC, @@ -158,6 +163,7 @@ enum { SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF2, SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3, SDE_CP_CRTC_DSPP_LTM_VLUT, + SDE_CP_CRTC_DSPP_RC_MASK, SDE_CP_CRTC_DSPP_MAX, /* DSPP features end */ @@ -169,12 +175,52 @@ enum { SDE_CP_CRTC_MAX_FEATURES, }; +enum sde_cp_crtc_pu_features { + SDE_CP_CRTC_DSPP_RC_PU, + SDE_CP_CRTC_MAX_PU_FEATURES, +}; + +typedef void (*dspp_cap_update_func_t)(struct sde_crtc *crtc, + struct sde_kms_info *info); +enum sde_dspp_caps_features { + SDE_CP_RC_CAPS, + SDE_CP_CAPS_MAX, +}; + +static void rc_caps_update(struct sde_crtc *crtc, struct sde_kms_info *info); +static dspp_cap_update_func_t dspp_cap_update_func[SDE_CP_CAPS_MAX]; + +#define setup_dspp_caps_funcs(func) \ + (func[SDE_CP_RC_CAPS] = rc_caps_update) + static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc); -typedef int (*set_feature_wrapper)(struct sde_hw_dspp *hw_dspp, +typedef int (*feature_wrapper)(struct sde_hw_dspp *hw_dspp, struct sde_hw_cp_cfg *hw_cfg, struct sde_crtc *hw_crtc); + +static struct sde_kms *get_kms(struct drm_crtc *crtc) +{ + struct msm_drm_private *priv = crtc->dev->dev_private; + + return to_sde_kms(priv->kms); +} + +static void update_pu_feature_enable(struct sde_crtc *sde_crtc, + u32 feature, bool enable) +{ + if (!sde_crtc || feature > SDE_CP_CRTC_MAX_PU_FEATURES) { + DRM_ERROR("invalid args feature %d\n", feature); + return; + } + + if (enable) + sde_crtc->cp_pu_feature_mask |= BIT(feature); + else + sde_crtc->cp_pu_feature_mask &= ~BIT(feature); +} + static int set_dspp_vlut_feature(struct sde_hw_dspp *hw_dspp, struct sde_hw_cp_cfg *hw_cfg, struct sde_crtc *hw_crtc) @@ -655,9 +701,127 @@ static int set_ltm_hist_crtl_feature(struct sde_hw_dspp *hw_dspp, return ret; } -set_feature_wrapper crtc_feature_wrappers[SDE_CP_CRTC_MAX_FEATURES]; +static int check_rc_mask_feature(struct sde_hw_dspp *hw_dspp, + struct sde_hw_cp_cfg *hw_cfg, + struct sde_crtc *sde_crtc) +{ + int ret = 0; -#define setup_crtc_feature_wrappers(wrappers) \ + if (!hw_dspp || !hw_cfg || !sde_crtc) { + DRM_ERROR("invalid arguments"); + return -EINVAL; + } + + if (!hw_dspp->ops.validate_rc_mask) { + DRM_ERROR("invalid rc ops"); + return -EINVAL; + } + + ret = hw_dspp->ops.validate_rc_mask(hw_dspp, hw_cfg); + if (ret) + DRM_ERROR("failed to validate rc mask %d", ret); + + return ret; +} + +static int set_rc_mask_feature(struct sde_hw_dspp *hw_dspp, + struct sde_hw_cp_cfg *hw_cfg, + struct sde_crtc *sde_crtc) +{ + int ret = 0; + + if (!hw_dspp || !hw_cfg || !sde_crtc) { + DRM_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (!hw_dspp->ops.setup_rc_mask || !hw_dspp->ops.setup_rc_data) { + DRM_ERROR("invalid rc ops\n"); + return -EINVAL; + } + + DRM_DEBUG_DRIVER("dspp %d setup mask for rc instance %u\n", + hw_dspp->idx, hw_dspp->cap->sblk->rc.idx); + + ret = hw_dspp->ops.setup_rc_mask(hw_dspp, hw_cfg); + if (ret) { + DRM_ERROR("failed to setup rc mask, ret %d\n", ret); + goto exit; + } + + /* rc data should be programmed once if dspp are in multi-pipe mode */ + if (hw_dspp->cap->sblk->rc.idx % hw_cfg->num_of_mixers == 0) { + ret = hw_dspp->ops.setup_rc_data(hw_dspp, hw_cfg); + if (ret) { + DRM_ERROR("failed to setup rc data, ret %d\n", ret); + goto exit; + } + } + + update_pu_feature_enable(sde_crtc, SDE_CP_CRTC_DSPP_RC_PU, + hw_cfg->payload != NULL); +exit: + return ret; +} + +static int set_rc_pu_feature(struct sde_hw_dspp *hw_dspp, + struct sde_hw_cp_cfg *hw_cfg, + struct sde_crtc *sde_crtc) +{ + int ret = 0; + + if (!hw_dspp || !hw_cfg || !sde_crtc) { + DRM_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (!hw_dspp->ops.setup_rc_pu_roi) { + DRM_ERROR("invalid rc ops\n"); + return -EINVAL; + } + + DRM_DEBUG_DRIVER("dspp %d setup pu roi for rc instance %u\n", + hw_dspp->idx, hw_dspp->cap->sblk->rc.idx); + + ret = hw_dspp->ops.setup_rc_pu_roi(hw_dspp, hw_cfg); + if (ret < 0) + DRM_ERROR("failed to setup rc pu roi, ret %d\n", ret); + + return ret; +} + +static int check_rc_pu_feature(struct sde_hw_dspp *hw_dspp, + struct sde_hw_cp_cfg *hw_cfg, + struct sde_crtc *sde_crtc) +{ + int ret = 0; + + if (!hw_dspp || !hw_cfg || !sde_crtc) { + DRM_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (!hw_dspp->ops.validate_rc_pu_roi) { + SDE_ERROR("invalid rc ops"); + return -EINVAL; + } + + ret = hw_dspp->ops.validate_rc_pu_roi(hw_dspp, hw_cfg); + if (ret) + SDE_ERROR("failed to validate rc pu roi, ret %d", ret); + + return ret; +} + +feature_wrapper check_crtc_feature_wrappers[SDE_CP_CRTC_MAX_FEATURES]; +#define setup_check_crtc_feature_wrappers(wrappers) \ +do { \ + memset(wrappers, 0, sizeof(wrappers)); \ + wrappers[SDE_CP_CRTC_DSPP_RC_MASK] = check_rc_mask_feature; \ +} while (0) + +feature_wrapper set_crtc_feature_wrappers[SDE_CP_CRTC_MAX_FEATURES]; +#define setup_set_crtc_feature_wrappers(wrappers) \ do { \ memset(wrappers, 0, sizeof(wrappers)); \ wrappers[SDE_CP_CRTC_DSPP_VLUT] = set_dspp_vlut_feature; \ @@ -697,6 +861,21 @@ do { \ wrappers[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF2] = set_ltm_queue_buf_feature; \ wrappers[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3] = set_ltm_queue_buf_feature; \ wrappers[SDE_CP_CRTC_DSPP_LTM_HIST_CTL] = set_ltm_hist_crtl_feature; \ + wrappers[SDE_CP_CRTC_DSPP_RC_MASK] = set_rc_mask_feature; \ +} while (0) + +feature_wrapper set_crtc_pu_feature_wrappers[SDE_CP_CRTC_MAX_PU_FEATURES]; +#define setup_set_crtc_pu_feature_wrappers(wrappers) \ +do { \ + memset(wrappers, 0, sizeof(wrappers)); \ + wrappers[SDE_CP_CRTC_DSPP_RC_PU] = set_rc_pu_feature; \ +} while (0) + +feature_wrapper check_crtc_pu_feature_wrappers[SDE_CP_CRTC_MAX_PU_FEATURES]; +#define setup_check_crtc_pu_feature_wrappers(wrappers) \ +do { \ + memset(wrappers, 0, sizeof(wrappers)); \ + wrappers[SDE_CP_CRTC_DSPP_RC_PU] = check_rc_pu_feature; \ } while (0) #define INIT_PROP_ATTACH(p, crtc, prop, node, feature, val) \ @@ -886,12 +1065,6 @@ static int sde_cp_enable_crtc_property(struct drm_crtc *crtc, return ret; } -static struct sde_kms *get_kms(struct drm_crtc *crtc) -{ - struct msm_drm_private *priv = crtc->dev->dev_private; - - return to_sde_kms(priv->kms); -} static void sde_cp_crtc_prop_attach(struct sde_cp_prop_attach *prop_attach) { @@ -938,6 +1111,9 @@ void sde_cp_crtc_init(struct drm_crtc *crtc) if (IS_ERR(sde_crtc->hist_blob)) sde_crtc->hist_blob = NULL; + msm_property_install_blob(&sde_crtc->property_info, + "dspp_caps", DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_DSPP_INFO); + mutex_init(&sde_crtc->crtc_cp_lock); INIT_LIST_HEAD(&sde_crtc->active_list); INIT_LIST_HEAD(&sde_crtc->dirty_list); @@ -948,6 +1124,7 @@ void sde_cp_crtc_init(struct drm_crtc *crtc) spin_lock_init(&sde_crtc->ltm_lock); INIT_LIST_HEAD(&sde_crtc->ltm_buf_free); INIT_LIST_HEAD(&sde_crtc->ltm_buf_busy); + sde_cp_crtc_disable(crtc); } static void sde_cp_crtc_install_immutable_property(struct drm_crtc *crtc, @@ -1182,6 +1359,70 @@ static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc) spin_unlock_irqrestore(&node->state_lock, flags); } +static int sde_cp_crtc_checkfeature(struct sde_cp_node *prop_node, + struct sde_crtc *sde_crtc, struct sde_crtc_state *sde_crtc_state) +{ + struct sde_hw_cp_cfg hw_cfg; + struct sde_hw_mixer *hw_lm; + struct sde_hw_dspp *hw_dspp; + u32 num_mixers; + int i = 0, ret = 0; + bool feature_enabled = false; + feature_wrapper check_feature = NULL; + + if (!prop_node || !sde_crtc || !sde_crtc_state) { + DRM_ERROR("invalid arguments"); + return -EINVAL; + } + + num_mixers = sde_crtc->num_mixers; + + if (prop_node->feature >= SDE_CP_CRTC_MAX_FEATURES) { + DRM_ERROR("invalid feature %d\n", prop_node->feature); + return -EINVAL; + } + + check_feature = check_crtc_feature_wrappers[prop_node->feature]; + if (check_feature == NULL) + return 0; + + memset(&hw_cfg, 0, sizeof(hw_cfg)); + sde_cp_get_hw_payload(prop_node, &hw_cfg, &feature_enabled); + hw_cfg.num_of_mixers = sde_crtc->num_mixers; + hw_cfg.last_feature = 0; + + for (i = 0; i < num_mixers; i++) { + hw_dspp = sde_crtc->mixers[i].hw_dspp; + if (!hw_dspp || i >= DSPP_MAX) + continue; + hw_cfg.dspp[i] = hw_dspp; + } + + for (i = 0; i < num_mixers && !ret; i++) { + hw_lm = sde_crtc->mixers[i].hw_lm; + hw_dspp = sde_crtc->mixers[i].hw_dspp; + if (!hw_lm) { + ret = -EINVAL; + continue; + } + hw_cfg.ctl = sde_crtc->mixers[i].hw_ctl; + hw_cfg.mixer_info = hw_lm; + + /* use incoming state information */ + hw_cfg.displayh = num_mixers * + sde_crtc_state->lm_roi[i].w; + hw_cfg.displayv = sde_crtc_state->lm_roi[i].h; + + DRM_DEBUG_DRIVER("check cp feature %d on mixer %d\n", + prop_node->feature, hw_lm->idx - LM_0); + ret = check_feature(hw_dspp, &hw_cfg, sde_crtc); + if (ret) + break; + } + + return ret; +} + static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, struct sde_crtc *sde_crtc) { @@ -1206,11 +1447,11 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, } if ((prop_node->feature >= SDE_CP_CRTC_MAX_FEATURES) || - crtc_feature_wrappers[prop_node->feature] == NULL) { + set_crtc_feature_wrappers[prop_node->feature] == NULL) { ret = -EINVAL; } else { - set_feature_wrapper set_feature = - crtc_feature_wrappers[prop_node->feature]; + feature_wrapper set_feature = + set_crtc_feature_wrappers[prop_node->feature]; catalog = get_kms(&sde_crtc->base)->catalog; hw_cfg.broadcast_disabled = catalog->dma_cfg.broadcast_disabled; @@ -1252,6 +1493,250 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node, list_del_init(&prop_node->dirty_list); } +static int sde_cp_crtc_check_pu_features(struct drm_crtc *crtc) +{ + int ret = 0, i = 0, j = 0; + struct sde_crtc *sde_crtc; + struct sde_crtc_state *sde_crtc_state; + struct sde_hw_cp_cfg hw_cfg; + struct sde_hw_dspp *hw_dspp; + + if (!crtc) { + DRM_ERROR("invalid crtc %pK\n", crtc); + return -EINVAL; + } + + sde_crtc = to_sde_crtc(crtc); + if (!sde_crtc) { + DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc); + return -EINVAL; + } + + sde_crtc_state = to_sde_crtc_state(crtc->state); + if (!sde_crtc_state) { + DRM_ERROR("invalid sde_crtc_state %pK\n", sde_crtc_state); + return -EINVAL; + } + + for (i = 0; i < sde_crtc->num_mixers; i++) { + if (!sde_crtc->mixers[i].hw_lm) { + DRM_ERROR("invalid lm in mixer %d\n", i); + return -EINVAL; + } + + if (!sde_crtc->mixers[i].hw_ctl) { + DRM_ERROR("invalid ctl in mixer %d\n", i); + return -EINVAL; + } + } + + /* early return when not a partial update frame */ + if (sde_crtc_state->user_roi_list.num_rects == 0) { + DRM_DEBUG_DRIVER("no partial update required\n"); + return 0; + } + + memset(&hw_cfg, 0, sizeof(hw_cfg)); + hw_cfg.num_of_mixers = sde_crtc->num_mixers; + hw_cfg.payload = &sde_crtc_state->user_roi_list; + hw_cfg.len = sizeof(sde_crtc_state->user_roi_list); + for (i = 0; i < hw_cfg.num_of_mixers; i++) + hw_cfg.dspp[i] = sde_crtc->mixers[i].hw_dspp; + + for (i = 0; i < SDE_CP_CRTC_MAX_PU_FEATURES; i++) { + feature_wrapper check_pu_feature = + check_crtc_pu_feature_wrappers[i]; + + if (!check_pu_feature || + !(sde_crtc->cp_pu_feature_mask & BIT(i))) + continue; + + for (j = 0; j < hw_cfg.num_of_mixers; j++) { + hw_dspp = sde_crtc->mixers[j].hw_dspp; + + /* use incoming state information */ + hw_cfg.mixer_info = sde_crtc->mixers[j].hw_lm; + hw_cfg.ctl = sde_crtc->mixers[j].hw_ctl; + hw_cfg.displayh = hw_cfg.num_of_mixers * + sde_crtc_state->lm_roi[j].w; + hw_cfg.displayv = sde_crtc_state->lm_roi[j].h; + + ret = check_pu_feature(hw_dspp, &hw_cfg, sde_crtc); + if (ret) { + DRM_ERROR("failed pu feature %d in mixer %d\n", + i, j); + return -EAGAIN; + } + } + } + + return ret; +} + +int sde_cp_crtc_check_properties(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct sde_crtc *sde_crtc = NULL; + struct sde_crtc_state *sde_crtc_state = NULL; + struct sde_cp_node *prop_node = NULL, *n = NULL; + int ret = 0; + + if (!crtc || !crtc->dev || !state) { + DRM_ERROR("invalid crtc %pK dev %pK state %pK\n", crtc, + (crtc ? crtc->dev : NULL), state); + return -EINVAL; + } + + sde_crtc = to_sde_crtc(crtc); + if (!sde_crtc) { + DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc); + return -EINVAL; + } + + sde_crtc_state = to_sde_crtc_state(state); + if (!sde_crtc_state) { + DRM_ERROR("invalid sde_crtc_state %pK\n", sde_crtc_state); + return -EINVAL; + } + mutex_lock(&sde_crtc->crtc_cp_lock); + + ret = sde_cp_crtc_check_pu_features(crtc); + if (ret) { + DRM_ERROR("failed check pu features, ret %d\n", ret); + goto exit; + } + + if (list_empty(&sde_crtc->dirty_list)) { + DRM_DEBUG_DRIVER("Dirty list is empty\n"); + goto exit; + } + + list_for_each_entry_safe(prop_node, n, &sde_crtc->dirty_list, + dirty_list) { + ret = sde_cp_crtc_checkfeature(prop_node, sde_crtc, + sde_crtc_state); + if (ret) { + DRM_ERROR("failed check on prop_node %u\n", + prop_node->property_id); + /* remove invalid feature from dirty list */ + list_del_init(&prop_node->dirty_list); + goto exit; + } + } + +exit: + mutex_unlock(&sde_crtc->crtc_cp_lock); + return ret; +} + +static int sde_cp_crtc_set_pu_features(struct drm_crtc *crtc, bool *need_flush) +{ + int ret = 0, i = 0, j = 0; + struct sde_crtc *sde_crtc; + struct sde_crtc_state *sde_crtc_state; + struct sde_hw_cp_cfg hw_cfg; + struct sde_hw_dspp *hw_dspp; + struct sde_hw_mixer *hw_lm; + struct sde_mdss_cfg *catalog; + struct sde_rect user_rect, cached_rect; + + if (!need_flush) { + DRM_ERROR("invalid need_flush %pK\n", need_flush); + return -EINVAL; + } + + if (!crtc) { + DRM_ERROR("invalid crtc %pK\n", crtc); + return -EINVAL; + } + + sde_crtc = to_sde_crtc(crtc); + if (!sde_crtc) { + DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc); + return -EINVAL; + } + + sde_crtc_state = to_sde_crtc_state(crtc->state); + if (!sde_crtc_state) { + DRM_ERROR("invalid sde_crtc_state %pK\n", sde_crtc_state); + return -EINVAL; + } + + for (i = 0; i < sde_crtc->num_mixers; i++) { + if (!sde_crtc->mixers[i].hw_lm) { + DRM_ERROR("invalid lm in mixer %d\n", i); + return -EINVAL; + } + + if (!sde_crtc->mixers[i].hw_ctl) { + DRM_ERROR("invalid ctl in mixer %d\n", i); + return -EINVAL; + } + } + + /* early return if not a partial update frame or no change in rois */ + if (sde_crtc_state->user_roi_list.num_rects == 0) { + DRM_DEBUG_DRIVER("no partial update required\n"); + memset(&sde_crtc_state->cached_user_roi_list, 0, + sizeof(struct msm_roi_list)); + return 0; + } + + sde_kms_rect_merge_rectangles(&sde_crtc_state->user_roi_list, + &user_rect); + sde_kms_rect_merge_rectangles(&sde_crtc_state->cached_user_roi_list, + &cached_rect); + if (sde_kms_rect_is_equal(&user_rect, &cached_rect)) { + DRM_DEBUG_DRIVER("no change in list of ROIs\n"); + return 0; + } + + catalog = get_kms(&sde_crtc->base)->catalog; + memset(&hw_cfg, 0, sizeof(hw_cfg)); + hw_cfg.num_of_mixers = sde_crtc->num_mixers; + hw_cfg.broadcast_disabled = catalog->dma_cfg.broadcast_disabled; + hw_cfg.payload = &sde_crtc_state->user_roi_list; + hw_cfg.len = sizeof(sde_crtc_state->user_roi_list); + for (i = 0; i < hw_cfg.num_of_mixers; i++) + hw_cfg.dspp[i] = sde_crtc->mixers[i].hw_dspp; + + for (i = 0; i < SDE_CP_CRTC_MAX_PU_FEATURES; i++) { + feature_wrapper set_pu_feature = + set_crtc_pu_feature_wrappers[i]; + + if (!set_pu_feature || + !(sde_crtc->cp_pu_feature_mask & BIT(i))) + continue; + + for (j = 0; j < hw_cfg.num_of_mixers; j++) { + hw_lm = sde_crtc->mixers[j].hw_lm; + hw_dspp = sde_crtc->mixers[j].hw_dspp; + + hw_cfg.mixer_info = hw_lm; + hw_cfg.ctl = sde_crtc->mixers[j].hw_ctl; + hw_cfg.displayh = hw_cfg.num_of_mixers * + hw_lm->cfg.out_width; + hw_cfg.displayv = hw_lm->cfg.out_height; + + ret = set_pu_feature(hw_dspp, &hw_cfg, sde_crtc); + /* feature does not need flush when ret > 0 */ + if (ret < 0) { + DRM_ERROR("failed pu feature %d in mixer %d\n", + i, j); + return ret; + } else if (ret == 0) { + *need_flush = true; + } + } + } + + memcpy(&sde_crtc_state->cached_user_roi_list, + &sde_crtc_state->user_roi_list, + sizeof(struct msm_roi_list)); + + return 0; +} + void sde_cp_crtc_apply_properties(struct drm_crtc *crtc) { struct sde_crtc *sde_crtc = NULL; @@ -1259,6 +1744,8 @@ void sde_cp_crtc_apply_properties(struct drm_crtc *crtc) struct sde_cp_node *prop_node = NULL, *n = NULL; struct sde_hw_ctl *ctl; u32 num_mixers = 0, i = 0; + int rc = 0; + bool need_flush = false; if (!crtc || !crtc->dev) { DRM_ERROR("invalid crtc %pK dev %pK\n", crtc, @@ -1274,42 +1761,49 @@ void sde_cp_crtc_apply_properties(struct drm_crtc *crtc) num_mixers = sde_crtc->num_mixers; if (!num_mixers) { - DRM_DEBUG_DRIVER("no mixers for this crtc\n"); + DRM_ERROR("no mixers for this crtc\n"); return; } mutex_lock(&sde_crtc->crtc_cp_lock); - /* Check if dirty lists are empty and ad features are disabled for - * early return. If ad properties are active then we need to issue - * dspp flush. - **/ if (list_empty(&sde_crtc->dirty_list) && - list_empty(&sde_crtc->ad_dirty)) { - if (list_empty(&sde_crtc->ad_active)) { - DRM_DEBUG_DRIVER("Dirty list is empty\n"); - goto exit; - } - set_dspp_flush = true; + list_empty(&sde_crtc->ad_dirty) && + list_empty(&sde_crtc->ad_active) && + list_empty(&sde_crtc->active_list)) { + DRM_DEBUG_DRIVER("all lists are empty\n"); + goto exit; } - if (!list_empty(&sde_crtc->ad_active)) - sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESET); + rc = sde_cp_crtc_set_pu_features(crtc, &need_flush); + if (rc) { + DRM_ERROR("failed set pu features, skip cp updates\n"); + goto exit; + } else if (need_flush) { + set_dspp_flush = true; + set_lm_flush = true; + } list_for_each_entry_safe(prop_node, n, &sde_crtc->dirty_list, - dirty_list) { + dirty_list) { sde_cp_crtc_setfeature(prop_node, sde_crtc); /* Set the flush flag to true */ - if (prop_node->is_dspp_feature) + if (prop_node->is_dspp_feature && + !prop_node->lm_flush_override) set_dspp_flush = true; else set_lm_flush = true; } - list_for_each_entry_safe(prop_node, n, &sde_crtc->ad_dirty, - dirty_list) { + if (!list_empty(&sde_crtc->ad_active)) { + sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESET); set_dspp_flush = true; + } + + list_for_each_entry_safe(prop_node, n, &sde_crtc->ad_dirty, + dirty_list) { sde_cp_crtc_setfeature(prop_node, sde_crtc); + set_dspp_flush = true; } for (i = 0; i < num_mixers; i++) { @@ -1381,7 +1875,13 @@ void sde_cp_crtc_install_properties(struct drm_crtc *crtc) SDE_CP_CRTC_MAX_FEATURES), GFP_KERNEL); setup_dspp_prop_install_funcs(dspp_prop_install_func); setup_lm_prop_install_funcs(lm_prop_install_func); - setup_crtc_feature_wrappers(crtc_feature_wrappers); + setup_set_crtc_feature_wrappers(set_crtc_feature_wrappers); + setup_check_crtc_feature_wrappers(check_crtc_feature_wrappers); + setup_set_crtc_pu_feature_wrappers( + set_crtc_pu_feature_wrappers); + setup_check_crtc_pu_feature_wrappers( + check_crtc_pu_feature_wrappers); + setup_dspp_caps_funcs(dspp_cap_update_func); } if (!priv->cp_property) goto exit; @@ -1944,6 +2444,57 @@ static void dspp_ltm_install_property(struct drm_crtc *crtc) } } +static void dspp_rc_install_property(struct drm_crtc *crtc) +{ + char feature_name[256]; + struct sde_kms *kms = NULL; + struct sde_mdss_cfg *catalog = NULL; + struct sde_cp_node *prop_node = NULL; + struct sde_crtc *sde_crtc = NULL; + u32 version; + + if (!crtc) { + DRM_ERROR("invalid arguments"); + return; + } + + sde_crtc = to_sde_crtc(crtc); + if (!sde_crtc) { + DRM_ERROR("invalid sde_crtc %pK\n", sde_crtc); + return; + } + + kms = get_kms(crtc); + catalog = kms->catalog; + version = catalog->dspp[0].sblk->rc.version >> 16; + switch (version) { + case 1: + snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d", + "SDE_DSPP_RC_MASK_V", version); + sde_cp_crtc_install_blob_property(crtc, feature_name, + SDE_CP_CRTC_DSPP_RC_MASK, + sizeof(struct drm_msm_rc_mask_cfg)); + + /* Override flush mechanism to use layer mixer flush bits */ + if (!catalog->rc_lm_flush_override) + break; + + DRM_DEBUG("rc using lm flush override\n"); + list_for_each_entry(prop_node, &sde_crtc->feature_list, + feature_list) { + if (prop_node->feature == SDE_CP_CRTC_DSPP_RC_MASK) { + prop_node->lm_flush_override = true; + break; + } + } + + break; + default: + DRM_ERROR("version %d not supported\n", version); + break; + } +} + static void lm_gc_install_property(struct drm_crtc *crtc) { char feature_name[256]; @@ -3373,3 +3924,72 @@ void sde_cp_mode_switch_prop_dirty(struct drm_crtc *crtc_drm) } mutex_unlock(&crtc->crtc_cp_lock); } + +static void rc_caps_update(struct sde_crtc *crtc, struct sde_kms_info *info) +{ + struct sde_mdss_cfg *catalog = get_kms(&crtc->base)->catalog; + u32 i, num_mixers = crtc->num_mixers; + char blk_name[256]; + + if (!catalog->rc_count || num_mixers > catalog->rc_count) + return; + + for (i = 0; i < num_mixers; i++) { + struct sde_hw_dspp *dspp = crtc->mixers[i].hw_dspp; + + if (!dspp || (dspp->idx - DSPP_0) >= catalog->rc_count) + continue; + snprintf(blk_name, sizeof(blk_name), "rc%u", + (dspp->idx - DSPP_0)); + sde_kms_info_add_keyint(info, blk_name, 1); + } +} + +void sde_cp_crtc_enable(struct drm_crtc *drm_crtc) +{ + struct sde_crtc *crtc; + struct sde_kms_info *info = NULL; + u32 i, num_mixers; + + if (!drm_crtc) { + DRM_ERROR("invalid crtc handle"); + return; + } + crtc = to_sde_crtc(drm_crtc); + num_mixers = crtc->num_mixers; + if (!num_mixers) + return; + mutex_lock(&crtc->crtc_cp_lock); + info = kzalloc(sizeof(struct sde_kms_info), GFP_KERNEL); + if (info) { + for (i = 0; i < ARRAY_SIZE(dspp_cap_update_func); i++) + dspp_cap_update_func[i](crtc, info); + msm_property_set_blob(&crtc->property_info, + &crtc->dspp_blob_info, + info->data, SDE_KMS_INFO_DATALEN(info), + CRTC_PROP_DSPP_INFO); + } + kfree(info); + mutex_unlock(&crtc->crtc_cp_lock); +} + +void sde_cp_crtc_disable(struct drm_crtc *drm_crtc) +{ + struct sde_kms_info *info = NULL; + struct sde_crtc *crtc; + + if (!drm_crtc) { + DRM_ERROR("invalid crtc handle"); + return; + } + crtc = to_sde_crtc(drm_crtc); + mutex_lock(&crtc->crtc_cp_lock); + info = kzalloc(sizeof(struct sde_kms_info), GFP_KERNEL); + if (info) + msm_property_set_blob(&crtc->property_info, + &crtc->dspp_blob_info, + info->data, SDE_KMS_INFO_DATALEN(info), + CRTC_PROP_DSPP_INFO); + mutex_unlock(&crtc->crtc_cp_lock); + kfree(info); +} diff --git a/msm/sde/sde_color_processing.h b/msm/sde/sde_color_processing.h index 0b831977d740..277e3e6f368b 100644 --- a/msm/sde/sde_color_processing.h +++ b/msm/sde/sde_color_processing.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 2021, The Linux Foundation. All rights reserved. */ #ifndef _SDE_COLOR_PROCESSING_H @@ -93,6 +93,15 @@ void sde_cp_crtc_destroy_properties(struct drm_crtc *crtc); */ int sde_cp_crtc_set_property(struct drm_crtc *crtc, struct drm_property *property, uint64_t val); +/** + * sde_cp_crtc_check_properties: Verify color processing properties for a crtc. + * Should be called during atomic check call. + * @crtc: Pointer to crtc. + * @state: Pointer to crtc state. + * @returns: 0 on success, non-zero otherwise + */ +int sde_cp_crtc_check_properties(struct drm_crtc *crtc, + struct drm_crtc_state *state); /** * sde_cp_crtc_apply_properties: Enable/disable properties @@ -196,4 +205,18 @@ int sde_cp_ltm_off_event_handler(struct drm_crtc *crtc_drm, bool en, * @crtc_drm: Pointer to crtc. */ void sde_cp_mode_switch_prop_dirty(struct drm_crtc *crtc_drm); + +/** + * sde_cp_crtc_enable(): enable color processing info in the crtc. + * Should be called during crtc enable. + * @crtc: Pointer to drm_crtc. + */ +void sde_cp_crtc_enable(struct drm_crtc *crtc); + +/** + * sde_cp_crtc_disable(): disable color processing info in the crtc. + * Should be called during crtc disable. + * @crtc: Pointer to drm_crtc. + */ +void sde_cp_crtc_disable(struct drm_crtc *crtc); #endif /*_SDE_COLOR_PROCESSING_H */ diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index 5f3aede1f8ca..46bb184a5152 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -781,6 +781,9 @@ static int _sde_crtc_set_crtc_roi(struct drm_crtc *crtc, } sde_kms_rect_merge_rectangles(&sde_conn_state->rois, &conn_roi); + SDE_DEBUG("conn_roi x:%u, y:%u, w:%u, h:%u\n", + conn_roi.x, conn_roi.y, + conn_roi.w, conn_roi.h); SDE_EVT32_VERBOSE(DRMID(crtc), DRMID(conn), conn_roi.x, conn_roi.y, conn_roi.w, conn_roi.h); @@ -4079,6 +4082,7 @@ static void sde_crtc_disable(struct drm_crtc *crtc) /* disable clk & bw control until clk & bw properties are set */ cstate->bw_control = false; cstate->bw_split_vote = false; + sde_cp_crtc_disable(crtc); mutex_unlock(&sde_crtc->crtc_lock); } @@ -4150,7 +4154,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc, } sde_crtc->enabled = true; - + sde_cp_crtc_enable(crtc); /* update color processing on resume */ event.type = DRM_EVENT_CRTC_POWER; event.length = sizeof(u32); @@ -4903,6 +4907,12 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc, goto end; } + rc = sde_cp_crtc_check_properties(crtc, state); + if (rc) { + SDE_ERROR("crtc%d failed cp properties check %d\n", + crtc->base.id, rc); + goto end; + } end: kfree(pstates); kfree(multirect_plane); @@ -4989,6 +4999,26 @@ static void sde_kms_add_ubwc_info(struct sde_kms_info *info, sde_kms_info_add_keystr(info, "DDR version", "DDR5"); } +/** + * _sde_crtc_install_cp_properties - install color properties for crtc + * @info: Pointer to kms info structure + * @catalog: Pointer to catalog structure + */ +static void _sde_crtc_install_cp_properties(struct sde_kms_info *info, + struct sde_mdss_cfg *catalog) +{ + if (!info || !catalog) { + SDE_ERROR("invalid info or catalog\n"); + return; + } + + sde_kms_info_add_keyint(info, "has_hdr", catalog->has_hdr); + sde_kms_info_add_keyint(info, "has_hdr_plus", catalog->has_hdr_plus); + if (catalog->dspp_count && catalog->rc_count) + sde_kms_info_add_keyint(info, "rc_mem_size", + catalog->dspp[0].sblk->rc.mem_total_size); +} + /** * sde_crtc_install_properties - install all drm properties for crtc * @crtc: Pointer to drm crtc structure @@ -5189,8 +5219,7 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc, } sde_kms_info_add_keyint(info, "has_src_split", catalog->has_src_split); - sde_kms_info_add_keyint(info, "has_hdr", catalog->has_hdr); - sde_kms_info_add_keyint(info, "has_hdr_plus", catalog->has_hdr_plus); + _sde_crtc_install_cp_properties(info, catalog); if (catalog->perf.max_bw_low) sde_kms_info_add_keyint(info, "max_bandwidth_low", catalog->perf.max_bw_low * 1000LL); diff --git a/msm/sde/sde_crtc.h b/msm/sde/sde_crtc.h index e7330f7fba1a..da1aa8b6c6f5 100644 --- a/msm/sde/sde_crtc.h +++ b/msm/sde/sde_crtc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2021 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark * @@ -276,6 +276,7 @@ struct sde_crtc_misr_info { * @cur_perf : current performance committed to clock/bandwidth driver * @plane_mask_old: keeps track of the planes used in the previous commit * @frame_trigger_mode: frame trigger mode + * @cp_pu_feature_mask: mask indicating cp feature enable for partial update * @ltm_buffer_cnt : number of ltm buffers * @ltm_buffers : struct stores ltm buffer related data * @ltm_buf_free : list of LTM buffers that are available @@ -285,6 +286,7 @@ struct sde_crtc_misr_info { * @ltm_lock : Spinlock to protect ltm buffer_cnt, hist_en and ltm lists * @needs_hw_reset : Initiate a hw ctl reset * @comp_ratio : Compression ratio + * @dspp_blob_info : blob containing dspp hw capability information */ struct sde_crtc { struct drm_crtc base; @@ -358,6 +360,8 @@ struct sde_crtc { struct drm_property_blob *hist_blob; enum frame_trigger_mode_type frame_trigger_mode; + u32 cp_pu_feature_mask; + u32 ltm_buffer_cnt; struct sde_ltm_buffer *ltm_buffers[LTM_BUFFER_SIZE]; struct list_head ltm_buf_free; @@ -369,6 +373,8 @@ struct sde_crtc { bool needs_hw_reset; int comp_ratio; + + struct drm_property_blob *dspp_blob_info; }; #define to_sde_crtc(x) container_of(x, struct sde_crtc, base) @@ -389,6 +395,7 @@ struct sde_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 + * @cached_user_roi_list : Copy of user_roi_list from previous PU frame * @property_state: Local storage for msm_prop properties * @property_values: Current crtc property values * @input_fence_timeout_ns : Cached input fence timeout, in ns @@ -416,7 +423,7 @@ struct sde_crtc_state { struct sde_rect crtc_roi; struct sde_rect lm_bounds[CRTC_DUAL_MIXERS]; struct sde_rect lm_roi[CRTC_DUAL_MIXERS]; - struct msm_roi_list user_roi_list; + struct msm_roi_list user_roi_list, cached_user_roi_list; struct msm_property_state property_state; struct msm_property_value property_values[CRTC_PROP_COUNT]; diff --git a/msm/sde/sde_hw_catalog.c b/msm/sde/sde_hw_catalog.c index cab731a06f11..6a20a15c55d0 100644 --- a/msm/sde/sde_hw_catalog.c +++ b/msm/sde/sde_hw_catalog.c @@ -383,6 +383,14 @@ enum { LTM_PROP_MAX, }; +enum { + RC_OFF, + RC_LEN, + RC_VERSION, + RC_MEM_TOTAL_SIZE, + RC_PROP_MAX, +}; + enum { MIXER_OFF, MIXER_LEN, @@ -692,6 +700,13 @@ static struct sde_prop_type ltm_prop[] = { {LTM_VERSION, "qcom,sde-dspp-ltm-version", false, PROP_TYPE_U32}, }; +static struct sde_prop_type rc_prop[] = { + {RC_OFF, "qcom,sde-dspp-rc-off", false, PROP_TYPE_U32_ARRAY}, + {RC_LEN, "qcom,sde-dspp-rc-size", false, PROP_TYPE_U32}, + {RC_VERSION, "qcom,sde-dspp-rc-version", false, PROP_TYPE_U32}, + {RC_MEM_TOTAL_SIZE, "qcom,sde-dspp-rc-mem-size", false, PROP_TYPE_U32}, +}; + static struct sde_prop_type ds_top_prop[] = { {DS_TOP_OFF, "qcom,sde-dest-scaler-top-off", false, PROP_TYPE_U32}, {DS_TOP_LEN, "qcom,sde-dest-scaler-top-size", false, PROP_TYPE_U32}, @@ -2357,14 +2372,15 @@ static int sde_dspp_parse_dt(struct device_node *np, { int rc, prop_count[DSPP_PROP_MAX], i; int ad_prop_count[AD_PROP_MAX]; - int ltm_prop_count[LTM_PROP_MAX]; + int ltm_prop_count[LTM_PROP_MAX], rc_prop_count[RC_PROP_MAX]; bool prop_exists[DSPP_PROP_MAX], ad_prop_exists[AD_PROP_MAX]; - bool ltm_prop_exists[LTM_PROP_MAX]; + bool ltm_prop_exists[LTM_PROP_MAX], rc_prop_exists[RC_PROP_MAX]; bool blocks_prop_exists[DSPP_BLOCKS_PROP_MAX]; struct sde_prop_value *ad_prop_value = NULL, *ltm_prop_value = NULL; + struct sde_prop_value *rc_prop_value = NULL; int blocks_prop_count[DSPP_BLOCKS_PROP_MAX]; struct sde_prop_value *prop_value = NULL, *blocks_prop_value = NULL; - u32 off_count, ad_off_count, ltm_off_count; + u32 off_count, ad_off_count, ltm_off_count, rc_off_count; struct sde_dspp_cfg *dspp; struct sde_dspp_sub_blks *sblk; struct device_node *snp = NULL; @@ -2426,6 +2442,22 @@ static int sde_dspp_parse_dt(struct device_node *np, if (rc) goto end; + /* Parse RC dtsi entries */ + rc_prop_value = kcalloc(RC_PROP_MAX, + sizeof(struct sde_prop_value), GFP_KERNEL); + if (!rc_prop_value) { + rc = -ENOMEM; + goto end; + } + rc = _validate_dt_entry(np, rc_prop, ARRAY_SIZE(rc_prop), + rc_prop_count, &rc_off_count); + if (rc) + goto end; + rc = _read_dt_entry(np, rc_prop, ARRAY_SIZE(rc_prop), rc_prop_count, + rc_prop_exists, rc_prop_value); + if (rc) + goto end; + /* get DSPP feature dt properties if they exist */ snp = of_get_child_by_name(np, dspp_prop[DSPP_BLOCKS].prop_name); if (snp) { @@ -2489,6 +2521,22 @@ static int sde_dspp_parse_dt(struct device_node *np, set_bit(SDE_DSPP_LTM, &dspp->features); } + sblk->rc.id = SDE_DSPP_RC; + sde_cfg->rc_count = rc_off_count; + if (rc_prop_value && (i < rc_off_count) && + rc_prop_exists[RC_OFF]) { + sblk->rc.base = PROP_VALUE_ACCESS(rc_prop_value, + RC_OFF, i); + sblk->rc.len = PROP_VALUE_ACCESS(rc_prop_value, + RC_LEN, 0); + sblk->rc.version = PROP_VALUE_ACCESS(rc_prop_value, + RC_VERSION, 0); + sblk->rc.mem_total_size = PROP_VALUE_ACCESS( + rc_prop_value, RC_MEM_TOTAL_SIZE, + 0); + sblk->rc.idx = i; + set_bit(SDE_DSPP_RC, &dspp->features); + } } end: @@ -2496,6 +2544,8 @@ end: kfree(ad_prop_value); kfree(ltm_prop_value); kfree(blocks_prop_value); + kfree(rc_prop_value); + return rc; } @@ -4399,6 +4449,7 @@ static int _sde_hardware_pre_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev) sde_cfg->allow_gdsc_toggle = true; clear_bit(MDSS_INTR_AD4_0_INTR, sde_cfg->mdss_irqs); clear_bit(MDSS_INTR_AD4_1_INTR, sde_cfg->mdss_irqs); + sde_cfg->rc_lm_flush_override = true; } else { SDE_ERROR("unsupported chipset id:%X\n", hw_rev); sde_cfg->perf.min_prefill_lines = 0xffff; diff --git a/msm/sde/sde_hw_catalog.h b/msm/sde/sde_hw_catalog.h index 7d1f99a8c1f6..8a4d1930c1ef 100644 --- a/msm/sde/sde_hw_catalog.h +++ b/msm/sde/sde_hw_catalog.h @@ -315,6 +315,7 @@ enum { * @SDE_DSPP_VLUT PA VLUT block * @SDE_DSPP_AD AD block * @SDE_DSPP_LTM LTM block + * @SDE_DSPP_RC RC block * @SDE_DSPP_MAX maximum value */ enum { @@ -330,6 +331,7 @@ enum { SDE_DSPP_VLUT, SDE_DSPP_AD, SDE_DSPP_LTM, + SDE_DSPP_RC, SDE_DSPP_MAX }; @@ -664,6 +666,20 @@ struct sde_lm_sub_blks { struct sde_pp_blk gc; }; +/** + * struct sde_dspp_rc: Pixel processing rounded corner sub-blk information + * @info: HW register and features supported by this sub-blk. + * @version: HW Algorithm version. + * @idx: HW block instance id. + * @mem_total_size: data memory size. + */ +struct sde_dspp_rc { + SDE_HW_SUBBLK_INFO; + u32 version; + u32 idx; + u32 mem_total_size; +}; + struct sde_dspp_sub_blks { struct sde_pp_blk igc; struct sde_pp_blk pcc; @@ -677,6 +693,7 @@ struct sde_dspp_sub_blks { struct sde_pp_blk ad; struct sde_pp_blk ltm; struct sde_pp_blk vlut; + struct sde_dspp_rc rc; }; struct sde_pingpong_sub_blks { @@ -1271,6 +1288,7 @@ struct sde_limit_cfg { * @has_base_layer Supports staging layer as base layer * @allow_gdsc_toggle Flag to check if gdsc toggle is needed after crtc is * disabled when external vote is present + * @rc_lm_flush_override Support Rounded Corner using layer mixer flush * @sc_cfg: system cache configuration * @uidle_cfg Settings for uidle feature * @sui_misr_supported indicate if secure-ui-misr is supported @@ -1287,6 +1305,7 @@ struct sde_limit_cfg { * @has_vig_p010 indicates if vig pipe supports p010 format * @inline_rot_formats formats supported by the inline rotator feature * @mdss_irqs bitmap with the irqs supported by the target + * @rc_count number of rounded corner hardware instances */ struct sde_mdss_cfg { u32 hwversion; @@ -1329,6 +1348,7 @@ struct sde_mdss_cfg { bool update_tcsr_disp_glitch; bool has_base_layer; bool allow_gdsc_toggle; + bool rc_lm_flush_override; struct sde_sc_cfg sc_cfg; @@ -1394,6 +1414,7 @@ struct sde_mdss_cfg { u32 ad_count; u32 ltm_count; + u32 rc_count; u32 merge_3d_count; struct sde_merge_3d_cfg merge_3d[MAX_BLOCKS]; @@ -1441,6 +1462,7 @@ struct sde_mdss_hw_cfg_handler { #define BLK_WB(s) ((s)->wb) #define BLK_AD(s) ((s)->ad) #define BLK_LTM(s) ((s)->ltm) +#define BLK_RC(s) ((s)->rc) /** * sde_hw_set_preference: populate the individual hw lm preferences, diff --git a/msm/sde/sde_hw_dspp.c b/msm/sde/sde_hw_dspp.c index 5b66b83be806..d45dc5b8d832 100644 --- a/msm/sde/sde_hw_dspp.c +++ b/msm/sde/sde_hw_dspp.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, 2021, The Linux Foundation. All rights reserved. */ + #include #include "sde_hw_mdss.h" #include "sde_hwio.h" @@ -10,6 +11,7 @@ #include "sde_hw_color_processing.h" #include "sde_dbg.h" #include "sde_ad4.h" +#include "sde_hw_rc.h" #include "sde_kms.h" static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp, @@ -229,6 +231,37 @@ static void dspp_ltm(struct sde_hw_dspp *c) } } +static void dspp_rc(struct sde_hw_dspp *c) +{ + int ret = 0; + + if (!c) { + SDE_ERROR("invalid arguments\n"); + return; + } + + if (c->cap->sblk->rc.version == SDE_COLOR_PROCESS_VER(0x1, 0x0)) { + ret = sde_hw_rc_init(c); + if (ret) { + SDE_ERROR("rc init failed, ret %d\n", ret); + return; + } + + ret = reg_dmav1_init_dspp_op_v4(SDE_DSPP_RC, c->idx); + if (!ret) + c->ops.setup_rc_data = + sde_hw_rc_setup_data_dma; + else + c->ops.setup_rc_data = + sde_hw_rc_setup_data_ahb; + + c->ops.validate_rc_mask = sde_hw_rc_check_mask; + c->ops.setup_rc_mask = sde_hw_rc_setup_mask; + c->ops.validate_rc_pu_roi = sde_hw_rc_check_pu_roi; + c->ops.setup_rc_pu_roi = sde_hw_rc_setup_pu_roi; + } +} + static void (*dspp_blocks[SDE_DSPP_MAX])(struct sde_hw_dspp *c); static void _init_dspp_ops(void) @@ -245,6 +278,7 @@ static void _init_dspp_ops(void) dspp_blocks[SDE_DSPP_VLUT] = dspp_vlut; dspp_blocks[SDE_DSPP_AD] = dspp_ad; dspp_blocks[SDE_DSPP_LTM] = dspp_ltm; + dspp_blocks[SDE_DSPP_RC] = dspp_rc; } static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features) @@ -274,6 +308,7 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx, struct sde_hw_dspp *c; struct sde_dspp_cfg *cfg; int rc; + char buf[256]; if (!addr || !m) return ERR_PTR(-EINVAL); @@ -317,6 +352,13 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx, c->hw.xin_id); } + if ((cfg->sblk->rc.id == SDE_DSPP_RC) && cfg->sblk->rc.base) { + snprintf(buf, ARRAY_SIZE(buf), "%s_%d", "rc", c->idx - DSPP_0); + sde_dbg_reg_register_dump_range(SDE_DBG_NAME, buf, + c->hw.blk_off + cfg->sblk->rc.base, + c->hw.blk_off + cfg->sblk->rc.base + + cfg->sblk->rc.len, c->hw.xin_id); + } return c; blk_init_error: diff --git a/msm/sde/sde_hw_dspp.h b/msm/sde/sde_hw_dspp.h index dfc2ff1e4927..f44e617601a3 100644 --- a/msm/sde/sde_hw_dspp.h +++ b/msm/sde/sde_hw_dspp.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, 2021, The Linux Foundation. All rights reserved. */ #ifndef _SDE_HW_DSPP_H @@ -209,6 +209,46 @@ struct sde_hw_dspp_ops { * @status: Pointer to u32 where ltm status value is dumped. */ void (*ltm_read_intr_status)(struct sde_hw_dspp *ctx, u32 *status); + + /** + * validate_rc_mask - Validate RC mask configuration + * @ctx: Pointer to dspp context. + * @cfg: Pointer to configuration. + * Return: 0 on success, non-zero otherwise. + */ + int (*validate_rc_mask)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * setup_rc_mask - Setup RC mask configuration + * @ctx: Pointer to dspp context. + * @cfg: Pointer to configuration. + * Return: 0 on success, non-zero otherwise. + */ + int (*setup_rc_mask)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * validate_rc_pu_roi - Validate RC regions in during partial update. + * @ctx: Pointer to dspp context. + * @cfg: Pointer to configuration. + * Return: 0 on success, non-zero otherwise. + */ + int (*validate_rc_pu_roi)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * setup_rc_pu_roi - Setup RC regions in during partial update. + * @ctx: Pointer to dspp context. + * @cfg: Pointer to configuration. + * Return: 0 on success, non-zero otherwise. + */ + int (*setup_rc_pu_roi)(struct sde_hw_dspp *ctx, void *cfg); + + /** + * setup_rc_data - Program RC mask data + * @ctx: Pointer to dspp context. + * @cfg: Pointer to configuration. + * Return: 0 on success, non-zero otherwise. + */ + int (*setup_rc_data)(struct sde_hw_dspp *ctx, void *cfg); }; /** diff --git a/msm/sde/sde_hw_mdss.h b/msm/sde/sde_hw_mdss.h index 6a75881176ce..15f0b91d3c2a 100644 --- a/msm/sde/sde_hw_mdss.h +++ b/msm/sde/sde_hw_mdss.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2019, 2021, The Linux Foundation. All rights reserved. */ #ifndef _SDE_HW_MDSS_H @@ -171,6 +171,7 @@ enum sde_stage { SDE_STAGE_10, SDE_STAGE_MAX }; + enum sde_dspp { DSPP_0 = 1, DSPP_1, @@ -185,6 +186,12 @@ enum sde_ltm { LTM_MAX }; +enum sde_rc { + RC_0 = DSPP_0, + RC_1, + RC_MAX +}; + enum sde_ds { DS_TOP, DS_0, diff --git a/msm/sde/sde_hw_rc.c b/msm/sde/sde_hw_rc.c new file mode 100644 index 000000000000..cd9c8681e045 --- /dev/null +++ b/msm/sde/sde_hw_rc.c @@ -0,0 +1,1051 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#include +#include "sde_kms.h" +#include "sde_reg_dma.h" +#include "sde_hw_rc.h" +#include "sde_hw_catalog.h" +#include "sde_hw_util.h" +#include "sde_hw_dspp.h" +#include "sde_hw_reg_dma_v1_color_proc.h" + +/** + * Hardware register set + */ +#define SDE_HW_RC_REG0 0x00 +#define SDE_HW_RC_REG1 0x04 +#define SDE_HW_RC_REG2 0x08 +#define SDE_HW_RC_REG3 0x0C +#define SDE_HW_RC_REG4 0x10 +#define SDE_HW_RC_REG5 0x14 +#define SDE_HW_RC_REG6 0x18 +#define SDE_HW_RC_REG7 0x1C +#define SDE_HW_RC_REG8 0x20 +#define SDE_HW_RC_REG9 0x24 +#define SDE_HW_RC_REG10 0x28 +#define SDE_HW_RC_REG11 0x2C +#define SDE_HW_RC_REG12 0x30 +#define SDE_HW_RC_REG13 0x34 + +#define SDE_HW_RC_DATA_REG_SIZE 18 +#define SDE_HW_RC_SKIP_DATA_PROG 0x1 + +#define SDE_HW_RC_DISABLE_R1 0x01E +#define SDE_HW_RC_DISABLE_R2 0x1E0 + +#define SDE_HW_RC_PU_SKIP_OP 0x1 + +/** + * struct sde_hw_rc_state - rounded corner cached state per RC instance + * + * @last_rc_mask_cfg: cached value of most recent programmed mask. + * @mask_programmed: true if mask was programmed at least once to RC hardware. + * @last_roi_list: cached value of most recent processed list of ROIs. + * @roi_programmed: true if list of ROIs were processed at least once. + */ +struct sde_hw_rc_state { + struct drm_msm_rc_mask_cfg *last_rc_mask_cfg; + bool mask_programmed; + struct msm_roi_list *last_roi_list; + bool roi_programmed; +}; + +static struct sde_hw_rc_state rc_state[RC_MAX - RC_0] = { + { + .last_rc_mask_cfg = NULL, + .last_roi_list = NULL, + .mask_programmed = false, + .roi_programmed = false, + }, + { + .last_rc_mask_cfg = NULL, + .last_roi_list = NULL, + .mask_programmed = false, + .roi_programmed = false, + }, +}; +#define RC_STATE(hw_dspp) rc_state[hw_dspp->cap->sblk->rc.idx] + +enum rc_param_r { + RC_PARAM_R0 = 0x0, + RC_PARAM_R1 = 0x1, + RC_PARAM_R2 = 0x2, + RC_PARAM_R1R2 = (RC_PARAM_R1 | RC_PARAM_R2), +}; + +enum rc_param_a { + RC_PARAM_A0 = 0x2, + RC_PARAM_A1 = 0x4, +}; + +enum rc_param_b { + RC_PARAM_B0 = 0x0, + RC_PARAM_B1 = 0x1, + RC_PARAM_B2 = 0x2, + RC_PARAM_B1B2 = (RC_PARAM_B1 | RC_PARAM_B2), +}; + +enum rc_param_c { + RC_PARAM_C0 = (BIT(8)), + RC_PARAM_C1 = (BIT(10)), + RC_PARAM_C2 = (BIT(10) | BIT(11)), + RC_PARAM_C3 = (BIT(8) | BIT(10)), + RC_PARAM_C4 = (BIT(8) | BIT(9)), + RC_PARAM_C5 = (BIT(8) | BIT(9) | BIT(10) | BIT(11)), +}; + +enum rc_merge_mode { + RC_MERGE_SINGLE_PIPE = 0x0, + RC_MERGE_DUAL_PIPE = 0x1 +}; + +struct rc_config_table { + enum rc_param_a param_a; + enum rc_param_b param_b; + enum rc_param_c param_c; + enum rc_merge_mode merge_mode; + enum rc_merge_mode merge_mode_en; +}; + +static struct rc_config_table config_table[] = { + /* RC_PARAM_A0 configurations */ + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B0, + .param_c = RC_PARAM_C5, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B1B2, + .param_c = RC_PARAM_C3, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B1, + .param_c = RC_PARAM_C0, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B2, + .param_c = RC_PARAM_C1, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B0, + .param_c = RC_PARAM_C5, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_DUAL_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B1B2, + .param_c = RC_PARAM_C3, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_DUAL_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B1, + .param_c = RC_PARAM_C0, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A0, + .param_b = RC_PARAM_B2, + .param_c = RC_PARAM_C1, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + }, + + /* RC_PARAM_A1 configurations */ + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B0, + .param_c = RC_PARAM_C5, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B1B2, + .param_c = RC_PARAM_C5, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B1, + .param_c = RC_PARAM_C4, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B2, + .param_c = RC_PARAM_C2, + .merge_mode = RC_MERGE_SINGLE_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B0, + .param_c = RC_PARAM_C5, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_DUAL_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B1B2, + .param_c = RC_PARAM_C5, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_DUAL_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B1, + .param_c = RC_PARAM_C4, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, + { + .param_a = RC_PARAM_A1, + .param_b = RC_PARAM_B2, + .param_c = RC_PARAM_C2, + .merge_mode = RC_MERGE_DUAL_PIPE, + .merge_mode_en = RC_MERGE_SINGLE_PIPE, + + }, +}; + +static inline void _sde_hw_rc_reg_write( + struct sde_hw_dspp *hw_dspp, + int offset, + u32 value) +{ + u32 address = hw_dspp->cap->sblk->rc.base + offset; + + SDE_DEBUG("rc:%u, address:0x%08X, value:0x%08X\n", + hw_dspp->cap->sblk->rc.idx, + hw_dspp->hw.blk_off + address, value); + SDE_REG_WRITE(&hw_dspp->hw, address, value); +} + +static int _sde_hw_rc_get_enable_bits( + enum rc_param_a param_a, + enum rc_param_b param_b, + enum rc_param_c *param_c, + u32 merge_mode, + u32 *merge_mode_en) +{ + int i = 0; + + if (!param_c || !merge_mode_en) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(config_table); i++) { + if (merge_mode == config_table[i].merge_mode && + param_a == config_table[i].param_a && + param_b == config_table[i].param_b) { + *param_c = config_table[i].param_c; + *merge_mode_en = config_table[i].merge_mode_en; + SDE_DEBUG("found param_c:0x%08X, merge_mode_en:%d\n", + *param_c, *merge_mode_en); + return 0; + } + } + SDE_ERROR("configuration not supported"); + + return -EINVAL; +} + +static int _sde_hw_rc_get_merge_mode( + const struct sde_hw_cp_cfg *hw_cfg, + u32 *merge_mode) +{ + int rc = 0; + + if (!hw_cfg || !merge_mode) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (hw_cfg->num_of_mixers == 1) + *merge_mode = RC_MERGE_SINGLE_PIPE; + else if (hw_cfg->num_of_mixers == 2) + *merge_mode = RC_MERGE_DUAL_PIPE; + else { + SDE_ERROR("invalid number of mixers:%d\n", + hw_cfg->num_of_mixers); + return -EINVAL; + } + + SDE_DEBUG("number mixers:%u, merge mode:%u\n", + hw_cfg->num_of_mixers, *merge_mode); + + return rc; +} + +static int _sde_hw_rc_get_ajusted_roi( + const struct sde_hw_cp_cfg *hw_cfg, + const struct sde_rect *pu_roi, + struct sde_rect *rc_roi) +{ + int rc = 0; + + if (!hw_cfg || !pu_roi || !rc_roi) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + /*when partial update is disabled, use full screen ROI*/ + if (pu_roi->w == 0 && pu_roi->h == 0) { + rc_roi->x = pu_roi->x; + rc_roi->y = pu_roi->y; + rc_roi->w = hw_cfg->displayh; + rc_roi->h = hw_cfg->displayv; + } else { + memcpy(rc_roi, pu_roi, sizeof(struct sde_rect)); + } + + SDE_DEBUG("displayh:%u, displayv:%u\n", hw_cfg->displayh, + hw_cfg->displayv); + SDE_DEBUG("pu_roi x:%u, y:%u, w:%u, h:%u\n", pu_roi->x, pu_roi->y, + pu_roi->w, pu_roi->h); + SDE_DEBUG("rc_roi x:%u, y:%u, w:%u, h:%u\n", rc_roi->x, rc_roi->y, + rc_roi->w, rc_roi->h); + + return rc; +} + +static int _sde_hw_rc_get_param_rb( + const struct drm_msm_rc_mask_cfg *rc_mask_cfg, + const struct sde_rect *rc_roi, + enum rc_param_r *param_r, + enum rc_param_b *param_b) +{ + int rc = 0; + int half_panel_x = 0, half_panel_w = 0; + int cfg_param_01 = 0, cfg_param_02 = 0; + int x1 = 0, x2 = 0, y1 = 0, y2 = 0; + + if (!rc_mask_cfg || !rc_roi || !param_r || !param_b) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A1) + half_panel_w = rc_mask_cfg->cfg_param_04[0] + + rc_mask_cfg->cfg_param_04[1]; + else if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A0) + half_panel_w = rc_mask_cfg->cfg_param_04[0]; + else { + SDE_ERROR("invalid cfg_param_03:%u\n", + rc_mask_cfg->cfg_param_03); + return -EINVAL; + } + + cfg_param_01 = rc_mask_cfg->cfg_param_01; + cfg_param_02 = rc_mask_cfg->cfg_param_02; + x1 = rc_roi->x; + x2 = rc_roi->x + rc_roi->w - 1; + y1 = rc_roi->y; + y2 = rc_roi->y + rc_roi->h - 1; + half_panel_x = half_panel_w - 1; + + SDE_DEBUG("x1:%u y1:%u x2:%u y2:%u\n", x1, y1, x2, y2); + SDE_DEBUG("cfg_param_01:%u cfg_param_02:%u half_panel_x:%u", + cfg_param_01, cfg_param_02, half_panel_x); + SDE_DEBUG("param_r:0x%08X param_b:0x%08X\n", + *param_r, *param_b); + + if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0 || half_panel_x < 0 || + x1 >= x2 || y1 >= y2) { + SDE_ERROR("invalid coordinates\n"); + return -EINVAL; + } + + if (y1 <= cfg_param_01) { + *param_r |= RC_PARAM_R1; + if (x1 <= half_panel_x && x2 <= half_panel_x) + *param_b |= RC_PARAM_B1; + else if (x1 > half_panel_x && x2 > half_panel_x) + *param_b |= RC_PARAM_B2; + else + *param_b |= RC_PARAM_B1B2; + } + + if (y2 >= cfg_param_02) { + *param_r |= RC_PARAM_R2; + if (x1 <= half_panel_x && x2 <= half_panel_x) + *param_b |= RC_PARAM_B1; + else if (x1 > half_panel_x && x2 > half_panel_x) + *param_b |= RC_PARAM_B2; + else + *param_b |= RC_PARAM_B1B2; + } + + return rc; +} + +static int _sde_hw_rc_program_enable_bits( + struct sde_hw_dspp *hw_dspp, + struct drm_msm_rc_mask_cfg *rc_mask_cfg, + enum rc_param_a param_a, + enum rc_param_b param_b, + int merge_mode, + struct sde_rect *rc_roi) +{ + int rc = 0; + u32 val = 0, param_c = 0, rc_merge_mode = 0, ystart = 0; + u64 flags = 0; + bool r1_enable = false, r2_enable = false; + + if (!hw_dspp || !rc_mask_cfg || !rc_roi) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + rc = _sde_hw_rc_get_enable_bits(param_a, param_b, ¶m_c, + merge_mode, &rc_merge_mode); + if (rc) { + SDE_ERROR("invalid enable bits, rc:%d\n", rc); + return rc; + } + + flags = rc_mask_cfg->flags; + r1_enable = ((flags & SDE_HW_RC_DISABLE_R1) == SDE_HW_RC_DISABLE_R1) ? + false : true; + r2_enable = ((flags & SDE_HW_RC_DISABLE_R2) == SDE_HW_RC_DISABLE_R2) ? + false : true; + + if (r1_enable) { + val |= BIT(0); + SDE_DEBUG("enable R1\n"); + } else { + SDE_DEBUG("disable R1\n"); + } + + if (r2_enable) { + val |= BIT(4); + SDE_DEBUG("enable R2\n"); + } else { + SDE_DEBUG("disable R2\n"); + } + + /*corner case for partial update in R2 region*/ + if (!r1_enable && r2_enable) { + ystart = rc_roi->y; + SDE_DEBUG("set partial update ystart:%u\n", ystart); + } + + val |= param_c; + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG1, val); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG13, ystart); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG9, rc_merge_mode); + + return rc; +} + +static int _sde_hw_rc_program_roi( + struct sde_hw_dspp *hw_dspp, + struct drm_msm_rc_mask_cfg *rc_mask_cfg, + int merge_mode, + struct sde_rect *rc_roi) +{ + int rc = 0; + u32 val2 = 0, val3 = 0, val4 = 0; + enum rc_param_r param_r = RC_PARAM_R0; + enum rc_param_a param_a = RC_PARAM_A0; + enum rc_param_b param_b = RC_PARAM_B0; + + if (!hw_dspp || !rc_mask_cfg || !rc_roi) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + rc = _sde_hw_rc_get_param_rb(rc_mask_cfg, rc_roi, ¶m_r, + ¶m_b); + if (rc) { + SDE_ERROR("invalid rc roi, rc:%d\n", rc); + return rc; + } + + param_a = rc_mask_cfg->cfg_param_03; + rc = _sde_hw_rc_program_enable_bits(hw_dspp, rc_mask_cfg, + param_a, param_b, merge_mode, rc_roi); + if (rc) { + SDE_ERROR("failed to program enable bits, rc:%d\n", rc); + return rc; + } + + val2 = ((rc_mask_cfg->cfg_param_01 & 0x0000FFFF) | + ((rc_mask_cfg->cfg_param_02 << 16) & 0xFFFF0000)); + if (param_a == RC_PARAM_A1) { + val3 = (rc_mask_cfg->cfg_param_04[0] | + (rc_mask_cfg->cfg_param_04[1] << 16)); + val4 = (rc_mask_cfg->cfg_param_04[2] | + (rc_mask_cfg->cfg_param_04[3] << 16)); + } else if (param_a == RC_PARAM_A0) { + val3 = (rc_mask_cfg->cfg_param_04[0]); + val4 = (rc_mask_cfg->cfg_param_04[1]); + } + + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG2, val2); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG3, val3); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG4, val4); + + return 0; +} + +static int _sde_hw_rc_program_data_offset( + struct sde_hw_dspp *hw_dspp, + struct drm_msm_rc_mask_cfg *rc_mask_cfg) +{ + int rc = 0; + u32 val5 = 0, val6 = 0, val7 = 0, val8 = 0; + u32 cfg_param_07; + + if (!hw_dspp || !rc_mask_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + cfg_param_07 = rc_mask_cfg->cfg_param_07; + if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A1) { + val5 = ((rc_mask_cfg->cfg_param_05[0] + cfg_param_07) | + ((rc_mask_cfg->cfg_param_05[1] + cfg_param_07) + << 16)); + val6 = ((rc_mask_cfg->cfg_param_05[2] + cfg_param_07)| + ((rc_mask_cfg->cfg_param_05[3] + cfg_param_07) + << 16)); + val7 = ((rc_mask_cfg->cfg_param_06[0] + cfg_param_07) | + ((rc_mask_cfg->cfg_param_06[1] + cfg_param_07) + << 16)); + val8 = ((rc_mask_cfg->cfg_param_06[2] + cfg_param_07) | + ((rc_mask_cfg->cfg_param_06[3] + cfg_param_07) + << 16)); + } else if (rc_mask_cfg->cfg_param_03 == RC_PARAM_A0) { + val5 = (rc_mask_cfg->cfg_param_05[0] + cfg_param_07); + val6 = (rc_mask_cfg->cfg_param_05[1] + cfg_param_07); + val7 = (rc_mask_cfg->cfg_param_06[0] + cfg_param_07); + val8 = (rc_mask_cfg->cfg_param_06[1] + cfg_param_07); + } + + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG5, val5); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG6, val6); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG7, val7); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG8, val8); + + return rc; +} + +static int sde_hw_rc_check_mask_cfg( + struct sde_hw_dspp *hw_dspp, + struct sde_hw_cp_cfg *hw_cfg, + struct drm_msm_rc_mask_cfg *rc_mask_cfg) +{ + int rc = 0; + u32 i = 0; + u32 half_panel_width; + u64 flags; + u32 cfg_param_01, cfg_param_02, cfg_param_03; + u32 cfg_param_07, cfg_param_08; + u32 *cfg_param_04, *cfg_param_05, *cfg_param_06; + bool r1_enable, r2_enable; + + if (!hw_dspp || !hw_cfg || !rc_mask_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + flags = rc_mask_cfg->flags; + cfg_param_01 = rc_mask_cfg->cfg_param_01; + cfg_param_02 = rc_mask_cfg->cfg_param_02; + cfg_param_03 = rc_mask_cfg->cfg_param_03; + cfg_param_04 = rc_mask_cfg->cfg_param_04; + cfg_param_05 = rc_mask_cfg->cfg_param_05; + cfg_param_06 = rc_mask_cfg->cfg_param_06; + cfg_param_07 = rc_mask_cfg->cfg_param_07; + cfg_param_08 = rc_mask_cfg->cfg_param_08; + r1_enable = ((flags & SDE_HW_RC_DISABLE_R1) == SDE_HW_RC_DISABLE_R1) ? + false : true; + r2_enable = ((flags & SDE_HW_RC_DISABLE_R2) == SDE_HW_RC_DISABLE_R2) ? + false : true; + + if (cfg_param_07 > hw_dspp->cap->sblk->rc.mem_total_size) { + SDE_ERROR("invalid cfg_param_07:%d\n", cfg_param_07); + return -EINVAL; + } + + if (cfg_param_08 > RC_DATA_SIZE_MAX) { + SDE_ERROR("invalid cfg_param_08:%d\n", cfg_param_08); + return -EINVAL; + } + + if ((cfg_param_07 + cfg_param_08) > + hw_dspp->cap->sblk->rc.mem_total_size) { + SDE_ERROR("invalid cfg_param_08:%d, cfg_param_07:%d, max:%u\n", + cfg_param_08, cfg_param_07, + hw_dspp->cap->sblk->rc.mem_total_size); + return -EINVAL; + } + + if (!(cfg_param_03 == RC_PARAM_A1 || cfg_param_03 == RC_PARAM_A0)) { + SDE_ERROR("invalid cfg_param_03:%d\n", cfg_param_03); + return -EINVAL; + } + + for (i = 0; i < cfg_param_03; i++) { + if (cfg_param_04[i] < 4) { + SDE_ERROR("invalid cfg_param_04[%d]:%d\n", i, + cfg_param_04[i]); + return -EINVAL; + } + } + + half_panel_width = hw_cfg->displayh / cfg_param_03 * 2; + for (i = 0; i < cfg_param_03; i += 2) { + if (cfg_param_04[i] + cfg_param_04[i+1] != half_panel_width) { + SDE_ERROR("invalid ratio [%d]:%d, [%d]:%d, %d\n", + i, cfg_param_04[i], i+1, + cfg_param_04[i+1], half_panel_width); + return -EINVAL; + } + } + + if (r1_enable && r2_enable) { + if (cfg_param_01 > cfg_param_02) { + SDE_ERROR("invalid cfg_param_01:%d, cfg_param_02:%d\n", + cfg_param_01, cfg_param_02); + return -EINVAL; + } + } else { + SDE_DEBUG("R1 or R2 disabled, skip overlap check"); + } + + if (r1_enable) { + if (cfg_param_01 < 1) { + SDE_ERROR("invalid min cfg_param_01:%d\n", + cfg_param_01); + return -EINVAL; + } + + for (i = 0; i < cfg_param_03 - 1; i++) { + if (cfg_param_05[i] >= cfg_param_05[i+1]) { + SDE_ERROR("invalid cfg_param_05 %d, %d\n", + cfg_param_05[i], + cfg_param_05[i+1]); + return -EINVAL; + } + } + + for (i = 0; i < cfg_param_03; i++) { + if (cfg_param_05[i] > RC_DATA_SIZE_MAX) { + SDE_ERROR("invalid cfg_param_05[%d]:%d\n", i, + cfg_param_05[i]); + return -EINVAL; + } + + } + } else { + SDE_DEBUG("R1 is disabled, skip parameter checks\n"); + } + + if (r2_enable) { + if ((hw_cfg->displayv - cfg_param_02) < 1) { + SDE_ERROR("invalid max cfg_param_02:%d\n", + cfg_param_02); + return -EINVAL; + } + + for (i = 0; i < cfg_param_03 - 1; i++) { + if (cfg_param_06[i] >= cfg_param_06[i+1]) { + SDE_ERROR("invalid cfg_param_06 %d, %d\n", + cfg_param_06[i], + cfg_param_06[i+1]); + return -EINVAL; + } + } + + for (i = 0; i < cfg_param_03; i++) { + if (cfg_param_06[i] > RC_DATA_SIZE_MAX) { + SDE_ERROR("invalid cfg_param_06[%d]:%d\n", i, + cfg_param_06[i]); + return -EINVAL; + } + + } + } else { + SDE_DEBUG("R2 is disabled, skip parameter checks\n"); + } + + return rc; +} + +int sde_hw_rc_check_mask(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) { + SDE_DEBUG("rc feature disabled, skip mask checks\n"); + return 0; + } + + if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) || + !hw_cfg->payload) { + SDE_ERROR("invalid payload\n"); + return -EINVAL; + } + + rc_mask_cfg = hw_cfg->payload; + if (hw_cfg->num_of_mixers != 1 && hw_cfg->num_of_mixers != 2) { + SDE_ERROR("invalid number of mixers:%d\n", + hw_cfg->num_of_mixers); + return -EINVAL; + } + + rc = sde_hw_rc_check_mask_cfg(hw_dspp, hw_cfg, rc_mask_cfg); + if (rc) { + SDE_ERROR("invalid rc mask configuration, rc:%d\n", rc); + return rc; + } + + return 0; +} + +int sde_hw_rc_check_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct msm_roi_list *roi_list; + struct sde_rect rc_roi, merged_roi; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + bool mask_programmed = false; + enum rc_param_r param_r = RC_PARAM_R0; + enum rc_param_b param_b = RC_PARAM_B0; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (hw_cfg->len != sizeof(struct sde_drm_roi_v1)) { + SDE_ERROR("invalid payload size\n"); + return -EINVAL; + } + + rc_mask_cfg = RC_STATE(hw_dspp).last_rc_mask_cfg; + mask_programmed = RC_STATE(hw_dspp).mask_programmed; + + /* early return when there is no mask in memory */ + if (!mask_programmed || !rc_mask_cfg) { + SDE_DEBUG("no previous rc mask programmed\n"); + return SDE_HW_RC_PU_SKIP_OP; + } + + rc = sde_hw_rc_check_mask_cfg(hw_dspp, hw_cfg, rc_mask_cfg); + if (rc) { + SDE_ERROR("invalid rc mask configuration, rc:%d\n", rc); + return rc; + } + + roi_list = hw_cfg->payload; + sde_kms_rect_merge_rectangles(roi_list, &merged_roi); + rc = _sde_hw_rc_get_ajusted_roi(hw_cfg, &merged_roi, &rc_roi); + if (rc) { + SDE_ERROR("failed to get adjusted roi, rc:%d\n", rc); + return rc; + } + + rc = _sde_hw_rc_get_param_rb(rc_mask_cfg, &rc_roi, + ¶m_r, ¶m_b); + if (rc) { + SDE_ERROR("invalid rc roi, rc:%d\n", rc); + return rc; + } + + return 0; +} + +int sde_hw_rc_setup_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct msm_roi_list *roi_list; + struct sde_rect rc_roi, merged_roi; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + enum rc_param_r param_r = RC_PARAM_R0; + enum rc_param_a param_a = RC_PARAM_A0; + enum rc_param_b param_b = RC_PARAM_B0; + u32 merge_mode = 0; + bool mask_programmed = false; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if (hw_cfg->len != sizeof(struct sde_drm_roi_v1)) { + SDE_ERROR("invalid payload size\n"); + return -EINVAL; + } + + rc_mask_cfg = RC_STATE(hw_dspp).last_rc_mask_cfg; + mask_programmed = RC_STATE(hw_dspp).mask_programmed; + + /* early return when there is no mask in memory */ + if (!mask_programmed || !rc_mask_cfg) { + SDE_DEBUG("no previous rc mask programmed\n"); + return SDE_HW_RC_PU_SKIP_OP; + } + + roi_list = hw_cfg->payload; + sde_kms_rect_merge_rectangles(roi_list, &merged_roi); + rc = _sde_hw_rc_get_ajusted_roi(hw_cfg, &merged_roi, &rc_roi); + if (rc) { + SDE_ERROR("failed to get adjusted roi, rc:%d\n", rc); + return rc; + } + + rc = _sde_hw_rc_get_merge_mode(hw_cfg, &merge_mode); + if (rc) { + SDE_ERROR("invalid merge_mode, rc:%d\n"); + return rc; + } + + rc = _sde_hw_rc_get_param_rb(rc_mask_cfg, &rc_roi, ¶m_r, + ¶m_b); + if (rc) { + SDE_ERROR("invalid roi, rc:%d\n", rc); + return rc; + } + + param_a = rc_mask_cfg->cfg_param_03; + rc = _sde_hw_rc_program_enable_bits(hw_dspp, rc_mask_cfg, + param_a, param_b, merge_mode, &rc_roi); + if (rc) { + SDE_ERROR("failed to program enable bits, rc:%d\n", rc); + return rc; + } + + memcpy(RC_STATE(hw_dspp).last_roi_list, + roi_list, sizeof(struct msm_roi_list)); + RC_STATE(hw_dspp).roi_programmed = true; + + return 0; +} + +int sde_hw_rc_setup_mask(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + struct sde_rect rc_roi, merged_roi; + struct msm_roi_list *last_roi_list; + u32 merge_mode = 0; + bool roi_programmed = false; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) { + SDE_DEBUG("rc feature disabled\n"); + + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG1, 0); + + memset(RC_STATE(hw_dspp).last_rc_mask_cfg, 0, + sizeof(struct drm_msm_rc_mask_cfg)); + RC_STATE(hw_dspp).mask_programmed = false; + + return 0; + } + + if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) || + !hw_cfg->payload) { + SDE_ERROR("invalid payload\n"); + return -EINVAL; + } + + rc_mask_cfg = hw_cfg->payload; + last_roi_list = RC_STATE(hw_dspp).last_roi_list; + roi_programmed = RC_STATE(hw_dspp).roi_programmed; + + if (!roi_programmed) { + SDE_DEBUG("full frame update\n"); + memset(&merged_roi, 0, sizeof(struct sde_rect)); + } else { + SDE_DEBUG("partial frame update\n"); + sde_kms_rect_merge_rectangles(last_roi_list, &merged_roi); + } + + rc = _sde_hw_rc_get_ajusted_roi(hw_cfg, &merged_roi, &rc_roi); + if (rc) { + SDE_ERROR("failed to get adjusted roi, rc:%d\n", rc); + return rc; + } + + rc = _sde_hw_rc_get_merge_mode(hw_cfg, &merge_mode); + if (rc) { + SDE_ERROR("invalid merge_mode, rc:%d\n"); + return rc; + } + + rc = _sde_hw_rc_program_roi(hw_dspp, rc_mask_cfg, + merge_mode, &rc_roi); + if (rc) { + SDE_ERROR("unable to program rc roi, rc:%d\n", rc); + return rc; + } + + rc = _sde_hw_rc_program_data_offset(hw_dspp, rc_mask_cfg); + if (rc) { + SDE_ERROR("unable to program data offsets, rc:%d\n", rc); + return rc; + } + + memcpy(RC_STATE(hw_dspp).last_rc_mask_cfg, rc_mask_cfg, + sizeof(struct drm_msm_rc_mask_cfg)); + RC_STATE(hw_dspp).mask_programmed = true; + + return 0; +} + +int sde_hw_rc_setup_data_dma(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) { + SDE_DEBUG("rc feature disabled, skip data programming\n"); + return 0; + } + + if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) || + !hw_cfg->payload) { + SDE_ERROR("invalid payload\n"); + return -EINVAL; + } + + rc_mask_cfg = hw_cfg->payload; + + if (rc_mask_cfg->flags & SDE_HW_RC_SKIP_DATA_PROG) { + SDE_DEBUG("skip data programming\n"); + return 0; + } + + rc = reg_dmav1_setup_rc_datav1(hw_dspp, cfg); + if (rc) { + SDE_ERROR("unable to setup rc with dma, rc:%d\n", rc); + return rc; + } + + return rc; +} + +int sde_hw_rc_setup_data_ahb(struct sde_hw_dspp *hw_dspp, void *cfg) +{ + int rc = 0, i = 0; + u32 data = 0, cfg_param_07 = 0; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + + if (!hw_dspp || !hw_cfg) { + SDE_ERROR("invalid arguments\n"); + return -EINVAL; + } + + if ((hw_cfg->len == 0 && hw_cfg->payload == NULL)) { + SDE_DEBUG("rc feature disabled, skip data programming\n"); + return 0; + } + + if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg) || + !hw_cfg->payload) { + SDE_ERROR("invalid payload\n"); + return -EINVAL; + } + + rc_mask_cfg = hw_cfg->payload; + + if (rc_mask_cfg->flags & SDE_HW_RC_SKIP_DATA_PROG) { + SDE_DEBUG("skip data programming\n"); + return 0; + } + + cfg_param_07 = rc_mask_cfg->cfg_param_07; + SDE_DEBUG("cfg_param_07:%u\n", cfg_param_07); + + for (i = 0; i < rc_mask_cfg->cfg_param_08; i++) { + SDE_DEBUG("cfg_param_09[%d] = 0x%016lX at %u\n", i, + rc_mask_cfg->cfg_param_09[i], i + cfg_param_07); + + data = (i == 0) ? (BIT(30) | (cfg_param_07 << 18)) : 0; + data |= (rc_mask_cfg->cfg_param_09[i] & 0x3FFFF); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG10, data); + data = ((rc_mask_cfg->cfg_param_09[i] >> + SDE_HW_RC_DATA_REG_SIZE) & 0x3FFFF); + _sde_hw_rc_reg_write(hw_dspp, SDE_HW_RC_REG10, data); + } + + return rc; +} + +int sde_hw_rc_init(struct sde_hw_dspp *hw_dspp) +{ + int rc = 0; + + RC_STATE(hw_dspp).last_roi_list = kzalloc( + sizeof(struct msm_roi_list), GFP_KERNEL); + if (!RC_STATE(hw_dspp).last_roi_list) + return -ENOMEM; + + RC_STATE(hw_dspp).last_rc_mask_cfg = kzalloc( + sizeof(struct drm_msm_rc_mask_cfg), GFP_KERNEL); + if (!RC_STATE(hw_dspp).last_rc_mask_cfg) + return -ENOMEM; + + return rc; +} diff --git a/msm/sde/sde_hw_rc.h b/msm/sde/sde_hw_rc.h new file mode 100644 index 000000000000..9fc6d71c867d --- /dev/null +++ b/msm/sde/sde_hw_rc.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#ifndef _SDE_HW_RC_H +#define _SDE_HW_RC_H + +#include "sde_hw_mdss.h" + + +/** + * sde_hw_rc_init - Initialize RC internal state object and ops + * @hw_dspp: DSPP instance. + * Return: 0 on success, non-zero otherwise. + */ +int sde_hw_rc_init(struct sde_hw_dspp *hw_dspp); + +/** + * sde_hw_rc_check_mask - Validate RC mask configuration + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success, non-zero otherwise. + */ +int sde_hw_rc_check_mask(struct sde_hw_dspp *hw_dspp, void *cfg); + +/** + * sde_hw_rc_setup_mask - Setup RC mask configuration + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success, non-zero otherwise. + */ +int sde_hw_rc_setup_mask(struct sde_hw_dspp *hw_dspp, void *cfg); + +/** + * sde_hw_rc_check_pu_roi - Validate RC partial update region of interest + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success. + * > 0 on early return. + * < 0 on error. + */ +int sde_hw_rc_check_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg); + +/** + * sde_hw_rc_setup_pu_roi - Setup RC partial update region of interest + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success. + * > 0 on early return. + * < 0 on error. + */ +int sde_hw_rc_setup_pu_roi(struct sde_hw_dspp *hw_dspp, void *cfg); + +/** + * sde_hw_rc_setup_data_ahb - Program mask data with AHB + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success, non-zero otherwise. + */ +int sde_hw_rc_setup_data_ahb(struct sde_hw_dspp *hw_dspp, void *cfg); + +/** + * sde_hw_rc_setup_data_dma - Program mask data with DMA + * @hw_dspp: DSPP instance. + * @cfg: Pointer to configuration blob. + * Return: 0 on success, non-zero otherwise. + */ +int sde_hw_rc_setup_data_dma(struct sde_hw_dspp *hw_dspp, void *cfg); + +#endif diff --git a/msm/sde/sde_hw_reg_dma_v1.c b/msm/sde/sde_hw_reg_dma_v1.c index e8911cf80661..f28e73b9d19f 100644 --- a/msm/sde/sde_hw_reg_dma_v1.c +++ b/msm/sde/sde_hw_reg_dma_v1.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, 2021, The Linux Foundation. All rights reserved. */ + #include #include "sde_hw_mdss.h" #include "sde_hw_ctl.h" @@ -14,6 +15,7 @@ #define ALIGNED_OFFSET (U32_MAX & ~(GUARD_BYTES)) #define ADDR_ALIGN BIT(8) #define MAX_RELATIVE_OFF (BIT(20) - 1) +#define ABSOLUTE_RANGE BIT(27) #define DECODE_SEL_OP (BIT(HW_BLK_SELECT)) #define REG_WRITE_OP ((BIT(REG_SINGLE_WRITE)) | (BIT(REG_BLK_WRITE_SINGLE)) | \ @@ -39,6 +41,7 @@ #define GRP_DMA_HW_BLK_SELECT (DMA0 | DMA1 | DMA2 | DMA3) #define GRP_DSPP_HW_BLK_SELECT (DSPP0 | DSPP1 | DSPP2 | DSPP3) #define GRP_LTM_HW_BLK_SELECT (LTM0 | LTM1) +#define GRP_MDSS_HW_BLK_SELECT (MDSS) #define BUFFER_SPACE_LEFT(cfg) ((cfg)->dma_buf->buffer_size - \ (cfg)->dma_buf->index) @@ -162,7 +165,7 @@ static void get_decode_sel(unsigned long blk, u32 *decode_sel) int i = 0; *decode_sel = 0; - for_each_set_bit(i, &blk, 31) { + for_each_set_bit(i, &blk, REG_DMA_BLK_MAX) { switch (BIT(i)) { case VIG0: *decode_sel |= BIT(0); @@ -212,6 +215,9 @@ static void get_decode_sel(unsigned long blk, u32 *decode_sel) case LTM1: *decode_sel |= BIT(23); break; + case MDSS: + *decode_sel |= BIT(31); + break; default: DRM_ERROR("block not supported %zx\n", (size_t)BIT(i)); break; @@ -240,6 +246,9 @@ int write_multi_reg_index(struct sde_reg_dma_setup_ops_cfg *cfg) cfg->dma_buf->index); loc[0] = HW_INDEX_REG_WRITE_OPCODE; loc[0] |= (cfg->blk_offset & MAX_RELATIVE_OFF); + if (cfg->blk == MDSS) + loc[0] |= ABSOLUTE_RANGE; + loc[1] = SIZE_DWORD(cfg->data_size); cfg->dma_buf->index += ops_mem_size[cfg->ops]; @@ -253,6 +262,9 @@ int write_multi_reg_inc(struct sde_reg_dma_setup_ops_cfg *cfg) loc = (u32 *)((u8 *)cfg->dma_buf->vaddr + cfg->dma_buf->index); loc[0] = AUTO_INC_REG_WRITE_OPCODE; + if (cfg->blk == MDSS) + loc[0] |= ABSOLUTE_RANGE; + loc[0] |= (cfg->blk_offset & MAX_RELATIVE_OFF); loc[1] = SIZE_DWORD(cfg->data_size); cfg->dma_buf->index += ops_mem_size[cfg->ops]; @@ -268,6 +280,9 @@ static int write_multi_lut_reg(struct sde_reg_dma_setup_ops_cfg *cfg) cfg->dma_buf->index); loc[0] = BLK_REG_WRITE_OPCODE; loc[0] |= (cfg->blk_offset & MAX_RELATIVE_OFF); + if (cfg->blk == MDSS) + loc[0] |= ABSOLUTE_RANGE; + loc[1] = (cfg->inc) ? 0 : BIT(31); loc[1] |= (cfg->wrap_size & WRAP_MAX_SIZE) << 16; loc[1] |= ((SIZE_DWORD(cfg->data_size)) & MAX_DWORDS_SZ); @@ -285,6 +300,9 @@ static int write_single_reg(struct sde_reg_dma_setup_ops_cfg *cfg) cfg->dma_buf->index); loc[0] = SINGLE_REG_WRITE_OPCODE; loc[0] |= (cfg->blk_offset & MAX_RELATIVE_OFF); + if (cfg->blk == MDSS) + loc[0] |= ABSOLUTE_RANGE; + loc[1] = *cfg->data; cfg->dma_buf->index += ops_mem_size[cfg->ops]; cfg->dma_buf->ops_completed |= REG_WRITE_OP; @@ -301,6 +319,9 @@ static int write_single_modify(struct sde_reg_dma_setup_ops_cfg *cfg) cfg->dma_buf->index); loc[0] = SINGLE_REG_MODIFY_OPCODE; loc[0] |= (cfg->blk_offset & MAX_RELATIVE_OFF); + if (cfg->blk == MDSS) + loc[0] |= ABSOLUTE_RANGE; + loc[1] = cfg->mask; loc[2] = *cfg->data; cfg->dma_buf->index += ops_mem_size[cfg->ops]; @@ -381,6 +402,7 @@ static int validate_write_reg(struct sde_reg_dma_setup_ops_cfg *cfg) static int validate_write_decode_sel(struct sde_reg_dma_setup_ops_cfg *cfg) { u32 remain_len; + bool vig_blk, dma_blk, dspp_blk, mdss_blk; remain_len = BUFFER_SPACE_LEFT(cfg); if (remain_len < ops_mem_size[HW_BLK_SELECT]) { @@ -393,15 +415,16 @@ static int validate_write_decode_sel(struct sde_reg_dma_setup_ops_cfg *cfg) DRM_ERROR("blk set as 0\n"); return -EINVAL; } - /* VIG, DMA and DSPP can't be combined */ - if (((cfg->blk & GRP_VIG_HW_BLK_SELECT) && - (cfg->blk & GRP_DSPP_HW_BLK_SELECT)) || - ((cfg->blk & GRP_DMA_HW_BLK_SELECT) && - (cfg->blk & GRP_DSPP_HW_BLK_SELECT)) || - ((cfg->blk & GRP_VIG_HW_BLK_SELECT) && - (cfg->blk & GRP_DMA_HW_BLK_SELECT))) { - DRM_ERROR("invalid blk combination %x\n", - cfg->blk); + + vig_blk = (cfg->blk & GRP_VIG_HW_BLK_SELECT) ? true : false; + dma_blk = (cfg->blk & GRP_DMA_HW_BLK_SELECT) ? true : false; + dspp_blk = (cfg->blk & GRP_DSPP_HW_BLK_SELECT) ? true : false; + mdss_blk = (cfg->blk & MDSS) ? true : false; + + if ((vig_blk && dspp_blk) || (dma_blk && dspp_blk) || + (vig_blk && dma_blk) || + (mdss_blk && (vig_blk | dma_blk | dspp_blk))) { + DRM_ERROR("invalid blk combination %x\n", cfg->blk); return -EINVAL; } @@ -672,6 +695,8 @@ int init_v12(struct sde_hw_reg_dma *cfg) v1_supported[LTM_INIT] = GRP_LTM_HW_BLK_SELECT; v1_supported[LTM_ROI] = GRP_LTM_HW_BLK_SELECT; v1_supported[LTM_VLUT] = GRP_LTM_HW_BLK_SELECT; + v1_supported[RC_DATA] = (GRP_DSPP_HW_BLK_SELECT | + GRP_MDSS_HW_BLK_SELECT); return 0; } @@ -685,7 +710,8 @@ static int check_support_v1(enum sde_reg_dma_features feature, if (!is_supported) return -EINVAL; - if (feature >= REG_DMA_FEATURES_MAX || blk >= MDSS) { + if (feature >= REG_DMA_FEATURES_MAX + || blk >= BIT_ULL(REG_DMA_BLK_MAX)) { *is_supported = false; return ret; } @@ -805,7 +831,8 @@ static struct sde_reg_dma_buffer *alloc_reg_dma_buf_v1(u32 size) int rc = 0; if (!size || SIZE_DWORD(size) > MAX_DWORDS_SZ) { - DRM_ERROR("invalid buffer size %d\n", size); + DRM_ERROR("invalid buffer size %d, max %d\n", + SIZE_DWORD(size), MAX_DWORDS_SZ); return ERR_PTR(-EINVAL); } diff --git a/msm/sde/sde_hw_reg_dma_v1_color_proc.c b/msm/sde/sde_hw_reg_dma_v1_color_proc.c index 4d7a4d009069..54629ab004fd 100644 --- a/msm/sde/sde_hw_reg_dma_v1_color_proc.c +++ b/msm/sde/sde_hw_reg_dma_v1_color_proc.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ + #include #include "sde_reg_dma.h" #include "sde_hw_reg_dma_v1_color_proc.h" @@ -65,6 +66,9 @@ #define MEMCOLOR_MEM_SIZE ((sizeof(struct drm_msm_memcol)) + \ REG_DMA_HEADERS_BUFFER_SZ) +#define RC_MEM_SIZE ((RC_DATA_SIZE_MAX * 2 * sizeof(u32)) + \ + REG_DMA_HEADERS_BUFFER_SZ) + #define QSEED3_MEM_SIZE (sizeof(struct sde_hw_scaler3_cfg) + \ (450 * sizeof(u32)) + \ REG_DMA_HEADERS_BUFFER_SZ) @@ -131,6 +135,7 @@ static u32 feature_map[SDE_DSPP_MAX] = { [SDE_DSPP_DITHER] = REG_DMA_FEATURES_MAX, [SDE_DSPP_HIST] = REG_DMA_FEATURES_MAX, [SDE_DSPP_AD] = REG_DMA_FEATURES_MAX, + [SDE_DSPP_RC] = RC_DATA, }; static u32 sspp_feature_map[SDE_SSPP_MAX] = { @@ -157,6 +162,7 @@ static u32 feature_reg_dma_sz[SDE_DSPP_MAX] = { [SDE_DSPP_HSIC] = HSIC_MEM_SIZE, [SDE_DSPP_SIXZONE] = SIXZONE_MEM_SIZE, [SDE_DSPP_MEMCOLOR] = MEMCOLOR_MEM_SIZE, + [SDE_DSPP_RC] = RC_MEM_SIZE, }; static u32 sspp_feature_reg_dma_sz[SDE_SSPP_MAX] = { @@ -1103,6 +1109,89 @@ void reg_dmav1_setup_dspp_igcv31(struct sde_hw_dspp *ctx, void *cfg) DRM_ERROR("failed to kick off ret %d\n", rc); } +int reg_dmav1_setup_rc_datav1(struct sde_hw_dspp *ctx, void *cfg) +{ + struct drm_msm_rc_mask_cfg *rc_mask_cfg; + struct sde_hw_reg_dma_ops *dma_ops; + struct sde_reg_dma_kickoff_cfg kick_off; + struct sde_hw_cp_cfg *hw_cfg = cfg; + struct sde_reg_dma_setup_ops_cfg dma_write_cfg; + int rc = 0; + u32 i = 0; + u32 *data = NULL; + u32 buf_sz = 0, abs_offset = 0; + u32 cfg_param_07; + u64 cfg_param_09; + + rc = reg_dma_dspp_check(ctx, cfg, RC_DATA); + if (rc) { + DRM_ERROR("invalid dma dspp check rc = %d\n"); + return -EINVAL; + } + + if (hw_cfg->len != sizeof(struct drm_msm_rc_mask_cfg)) { + DRM_ERROR("invalid size of payload len %d exp %zd\n", + hw_cfg->len, sizeof(struct drm_msm_rc_mask_cfg)); + return -EINVAL; + } + + rc_mask_cfg = hw_cfg->payload; + buf_sz = rc_mask_cfg->cfg_param_08 * 2 * sizeof(u32); + abs_offset = ctx->hw.blk_off + ctx->cap->sblk->rc.base + 0x28; + + dma_ops = sde_reg_dma_get_ops(); + dma_ops->reset_reg_dma_buf(dspp_buf[RC_DATA][ctx->idx]); + REG_DMA_INIT_OPS(dma_write_cfg, MDSS, RC_DATA, + dspp_buf[RC_DATA][ctx->idx]); + + REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0, 0); + rc = dma_ops->setup_payload(&dma_write_cfg); + if (rc) { + DRM_ERROR("write decode select failed ret %d\n", rc); + return -ENOMEM; + } + + DRM_DEBUG_DRIVER("allocating %u bytes of memory for dma\n", buf_sz); + data = kzalloc(buf_sz, GFP_KERNEL); + if (!data) { + DRM_ERROR("memory allocation failed ret %d\n", rc); + return -ENOMEM; + } + + cfg_param_07 = rc_mask_cfg->cfg_param_07; + for (i = 0; i < rc_mask_cfg->cfg_param_08; i++) { + cfg_param_09 = rc_mask_cfg->cfg_param_09[i]; + DRM_DEBUG_DRIVER("cfg_param_09[%d] = 0x%016lX at %u\n", i, + cfg_param_09, + i + cfg_param_07); + data[i * 2] = (i == 0) ? (BIT(30) | (cfg_param_07 << 18)) : 0; + data[i * 2] |= (cfg_param_09 & 0x3FFFF); + data[i * 2 + 1] = ((cfg_param_09 >> 18) & 0x3FFFF); + } + + REG_DMA_SETUP_OPS(dma_write_cfg, abs_offset, data, buf_sz, + REG_BLK_WRITE_INC, 0, 0, 0); + rc = dma_ops->setup_payload(&dma_write_cfg); + if (rc) { + DRM_ERROR("rc dma write failed ret %d\n", rc); + goto exit; + } + + /* defer trigger to kickoff phase */ + REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, + dspp_buf[RC_DATA][ctx->idx], REG_DMA_WRITE, + DMA_CTL_QUEUE0, WRITE_TRIGGER); + rc = dma_ops->kick_off(&kick_off); + if (rc) { + DRM_ERROR("failed to kick off ret %d\n", rc); + goto exit; + } + +exit: + kfree(data); + return rc; +} + static void _dspp_pccv4_off(struct sde_hw_dspp *ctx, void *cfg) { struct sde_reg_dma_kickoff_cfg kick_off; diff --git a/msm/sde/sde_hw_reg_dma_v1_color_proc.h b/msm/sde/sde_hw_reg_dma_v1_color_proc.h index cad0787ec473..41c382a96883 100644 --- a/msm/sde/sde_hw_reg_dma_v1_color_proc.h +++ b/msm/sde/sde_hw_reg_dma_v1_color_proc.h @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, 2021, The Linux Foundation. All rights reserved. */ + #ifndef _SDE_HW_REG_DMA_V1_COLOR_PROC_H #define _SDE_HW_REG_DMA_V1_COLOR_PROC_H @@ -242,6 +243,14 @@ void reg_dmav1_setup_ltm_roiv1(struct sde_hw_dspp *ctx, void *cfg); */ void reg_dmav1_setup_ltm_vlutv1(struct sde_hw_dspp *ctx, void *cfg); +/** + * reg_dmav1_setup_rc_datav1() - RC DATA v1 implementation using reg dma v1. + * @ctx: dspp ctx info + * @cfg: pointer to struct sde_hw_cp_cfg + * @returns: 0 if success, non-zero otherwise + */ +int reg_dmav1_setup_rc_datav1(struct sde_hw_dspp *ctx, void *cfg); + /** * reg_dmav1_deinit_ltm_ops() - deinitialize the ltm feature op for sde v4 * which were initialized. diff --git a/msm/sde/sde_reg_dma.h b/msm/sde/sde_reg_dma.h index 1454e41457b3..9941d8a30d42 100644 --- a/msm/sde/sde_reg_dma.h +++ b/msm/sde/sde_reg_dma.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2019, 2021, The Linux Foundation. All rights reserved. */ #ifndef _SDE_REG_DMA_H @@ -57,6 +57,7 @@ enum sde_reg_dma_read_sel { * @LTM_INIT: LTM INIT * @LTM_ROI: LTM ROI * @LTM_VLUT: LTM VLUT + * @RC_DATA: Rounded corner data * @REG_DMA_FEATURES_MAX: invalid selection */ enum sde_reg_dma_features { @@ -75,6 +76,7 @@ enum sde_reg_dma_features { LTM_INIT, LTM_ROI, LTM_VLUT, + RC_DATA, REG_DMA_FEATURES_MAX, }; @@ -131,6 +133,8 @@ enum sde_reg_dma_setup_ops { REG_DMA_SETUP_OPS_MAX, }; +#define REG_DMA_BLK_MAX 32 + /** * enum sde_reg_dma_blk - defines blocks for which reg dma op should be * performed