disp: msm: sde: add support for hardware based rounded corner

Add support for hardware based rounded corner part of
color processing framework.

Change-Id: I3e5f4dac6ffc759bb940215b7621ac716f255169
Signed-off-by: Amine Najahi <anajahi@codeaurora.org>
Signed-off-by: Steve Cohen <cohens@codeaurora.org>
This commit is contained in:
Amine Najahi
2019-10-10 11:19:15 -04:00
committed by Steve Cohen
parent 818651c2f1
commit af07b8a5d4
14 changed files with 1470 additions and 7 deletions

View File

@@ -76,6 +76,7 @@ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \
sde/sde_hw_qdss.o \
sde_dsc_helper.o \
sde_vdc_helper.o \
sde/sde_hw_rc.o \
msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \
sde/sde_encoder_phys_wb.o \

View File

@@ -56,6 +56,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);
@@ -112,6 +114,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);
@@ -160,6 +163,7 @@ enum sde_cp_crtc_features {
SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3,
SDE_CP_CRTC_DSPP_LTM_VLUT,
SDE_CP_CRTC_DSPP_SB,
SDE_CP_CRTC_DSPP_RC_MASK,
SDE_CP_CRTC_DSPP_MAX,
/* DSPP features end */
@@ -172,11 +176,15 @@ enum sde_cp_crtc_features {
};
enum sde_cp_crtc_pu_features {
SDE_CP_CRTC_DSPP_RC_PU,
SDE_CP_CRTC_MAX_PU_FEATURES,
};
static enum sde_cp_crtc_pu_features
sde_cp_crtc_pu_to_feature[SDE_CP_CRTC_MAX_PU_FEATURES];
sde_cp_crtc_pu_to_feature[SDE_CP_CRTC_MAX_PU_FEATURES] = {
[SDE_CP_CRTC_DSPP_RC_PU] =
(enum sde_cp_crtc_pu_features) SDE_CP_CRTC_DSPP_RC_MASK,
};
static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc);
@@ -672,10 +680,122 @@ static int set_ltm_hist_crtl_feature(struct sde_hw_dspp *hw_dspp,
return ret;
}
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;
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;
}
}
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) \
memset(wrappers, 0, sizeof(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) \
@@ -718,15 +838,22 @@ 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) \
memset(wrappers, 0, sizeof(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) \
memset(wrappers, 0, sizeof(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) \
do { \
@@ -1372,6 +1499,7 @@ static const int dspp_feature_to_sub_blk_tbl[SDE_CP_CRTC_MAX_FEATURES] = {
[SDE_CP_CRTC_DSPP_LTM_QUEUE_BUF3] = SDE_DSPP_LTM,
[SDE_CP_CRTC_DSPP_LTM_VLUT] = SDE_DSPP_LTM,
[SDE_CP_CRTC_DSPP_SB] = SDE_DSPP_SB,
[SDE_CP_CRTC_DSPP_RC_MASK] = SDE_DSPP_RC,
[SDE_CP_CRTC_DSPP_MAX] = SDE_DSPP_MAX,
[SDE_CP_CRTC_LM_GC] = SDE_DSPP_MAX,
};
@@ -2391,6 +2519,36 @@ 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;
u32 version;
if (!crtc) {
DRM_ERROR("invalid arguments");
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));
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];

View File

@@ -755,6 +755,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);
@@ -5003,8 +5006,13 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
sde_crtc_install_dest_scale_properties(sde_crtc, catalog,
info);
if (catalog->dspp_count && catalog->rc_count)
sde_kms_info_add_keyint(info, "rc_mem_size",
catalog->dspp[0].sblk->rc.mem_total_size);
msm_property_install_blob(&sde_crtc->property_info, "capabilities",
DRM_MODE_PROP_IMMUTABLE, CRTC_PROP_INFO);
msm_property_set_blob(&sde_crtc->property_info, &sde_crtc->blob_info,
info->data, SDE_KMS_INFO_DATALEN(info),
CRTC_PROP_INFO);

View File

@@ -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,
@@ -699,6 +707,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},
@@ -2471,8 +2486,8 @@ static int sde_dspp_parse_dt(struct device_node *np,
struct sde_mdss_cfg *sde_cfg)
{
int rc = 0, i;
u32 off_count, ad_off_count, ltm_off_count;
struct sde_dt_props *props, *ad_props, *ltm_props;
u32 off_count, ad_off_count, ltm_off_count, rc_off_count;
struct sde_dt_props *props, *ad_props, *ltm_props, *rc_props;
struct sde_dt_props *blocks_props = NULL;
struct sde_dspp_cfg *dspp;
struct sde_dspp_sub_blks *sblk;
@@ -2506,6 +2521,14 @@ static int sde_dspp_parse_dt(struct device_node *np,
goto put_ad_props;
}
/* Parse RC dtsi entries */
rc_props = sde_get_dt_props(np, RC_PROP_MAX, rc_prop,
ARRAY_SIZE(rc_prop), &rc_off_count);
if (IS_ERR_OR_NULL(rc_props)) {
rc = PTR_ERR(rc_props);
goto put_ltm_props;
}
/* get DSPP feature dt properties if they exist */
snp = of_get_child_by_name(np, dspp_prop[DSPP_BLOCKS].prop_name);
if (snp) {
@@ -2514,7 +2537,7 @@ static int sde_dspp_parse_dt(struct device_node *np,
NULL);
if (IS_ERR_OR_NULL(blocks_props)) {
rc = PTR_ERR(blocks_props);
goto put_ltm_props;
goto put_rc_props;
}
}
@@ -2571,10 +2594,28 @@ static int sde_dspp_parse_dt(struct device_node *np,
goto end;
}
sblk->rc.id = SDE_DSPP_RC;
sde_cfg->rc_count = rc_off_count;
if (rc_props && (i < rc_off_count) &&
rc_props->exists[RC_OFF]) {
sblk->rc.base = PROP_VALUE_ACCESS(rc_props->values,
RC_OFF, i);
sblk->rc.len = PROP_VALUE_ACCESS(rc_props->values,
RC_LEN, 0);
sblk->rc.version = PROP_VALUE_ACCESS(rc_props->values,
RC_VERSION, 0);
sblk->rc.mem_total_size = PROP_VALUE_ACCESS(
rc_props->values, RC_MEM_TOTAL_SIZE,
0);
sblk->rc.idx = i;
set_bit(SDE_DSPP_RC, &dspp->features);
}
}
end:
sde_put_dt_props(blocks_props);
put_rc_props:
sde_put_dt_props(rc_props);
put_ltm_props:
sde_put_dt_props(ltm_props);
put_ad_props:

View File

@@ -755,6 +755,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;
@@ -768,6 +782,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 {
@@ -1426,6 +1441,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
* @irq_offset_list list of sde_intr_irq_offsets to initialize irq table
* @rc_count number of rounded corner hardware instances
*/
struct sde_mdss_cfg {
u32 hwversion;
@@ -1536,6 +1552,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];
@@ -1583,6 +1600,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,

View File

@@ -2,6 +2,7 @@
/*
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#include <drm/msm_drm_pp.h>
#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,
@@ -241,6 +243,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)
@@ -257,6 +290,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)
@@ -286,6 +320,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);
@@ -329,6 +364,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:

View File

@@ -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);
};
/**

View File

@@ -172,6 +172,7 @@ enum sde_stage {
SDE_STAGE_10,
SDE_STAGE_MAX
};
enum sde_dspp {
DSPP_0 = 1,
DSPP_1,
@@ -186,6 +187,12 @@ enum sde_ltm {
LTM_MAX
};
enum sde_rc {
RC_0 = DSPP_0,
RC_1,
RC_MAX
};
enum sde_ds {
DS_TOP,
DS_0,

974
msm/sde/sde_hw_rc.c Normal file
View File

@@ -0,0 +1,974 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
#include <drm/msm_drm_pp.h>
#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_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_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_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_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_r param_r,
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;
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, &param_c,
merge_mode, &rc_merge_mode);
if (rc) {
SDE_ERROR("invalid enable bits, rc:%d\n", rc);
return rc;
}
if (param_r & RC_PARAM_R1) {
val |= BIT(0);
SDE_DEBUG("enable R1\n");
}
if (param_r & RC_PARAM_R2) {
val |= BIT(4);
SDE_DEBUG("enable R2\n");
}
/*corner case for partial update*/
if (param_r == RC_PARAM_R0) {
ystart = rc_roi->y;
SDE_DEBUG("set partial update ystart:%u\n", ystart);
}
if ((rc_mask_cfg->flags & SDE_HW_RC_DISABLE_R1)
== SDE_HW_RC_DISABLE_R1) {
val &= ~BIT(0);
SDE_DEBUG("override disable R1\n");
}
if ((rc_mask_cfg->flags & SDE_HW_RC_DISABLE_R2)
== SDE_HW_RC_DISABLE_R2) {
val &= ~BIT(4);
SDE_DEBUG("override disable R2\n");
}
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, &param_r,
&param_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_r, 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;
if (!hw_dspp || !hw_cfg || !rc_mask_cfg) {
SDE_ERROR("invalid arguments\n");
return -EINVAL;
}
if (!rc_mask_cfg->cfg_param_08 ||
rc_mask_cfg->cfg_param_08 > RC_DATA_SIZE_MAX) {
SDE_ERROR("invalid cfg_param_08:%d\n",
rc_mask_cfg->cfg_param_08);
return -EINVAL;
}
if (rc_mask_cfg->cfg_param_07 + rc_mask_cfg->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",
rc_mask_cfg->cfg_param_08,
rc_mask_cfg->cfg_param_07,
hw_dspp->cap->sblk->rc.mem_total_size);
return -EINVAL;
}
if (!rc_mask_cfg->cfg_param_03 ||
(rc_mask_cfg->cfg_param_03 != RC_PARAM_A1 &&
rc_mask_cfg->cfg_param_03 != RC_PARAM_A0)) {
SDE_ERROR("invalid cfg_param_03:%d\n",
rc_mask_cfg->cfg_param_03);
return -EINVAL;
}
if ((rc_mask_cfg->cfg_param_01 < 1) ||
((hw_cfg->displayv - rc_mask_cfg->cfg_param_02) < 1)) {
SDE_ERROR("invalid min cfg_param_01:%d or cfg_param_02:%d\n",
rc_mask_cfg->cfg_param_01,
rc_mask_cfg->cfg_param_02);
return -EINVAL;
}
if (rc_mask_cfg->cfg_param_01 > rc_mask_cfg->cfg_param_02) {
SDE_ERROR("invalid cfg_param_01:%d or cfg_param_02:%d\n",
rc_mask_cfg->cfg_param_01,
rc_mask_cfg->cfg_param_02);
return -EINVAL;
}
for (i = 0; i < rc_mask_cfg->cfg_param_03; i++) {
if (rc_mask_cfg->cfg_param_04[i] < 4) {
SDE_ERROR("invalid cfg_param_04[%d]:%d\n", i,
rc_mask_cfg->cfg_param_04[i]);
return -EINVAL;
}
}
half_panel_width = hw_cfg->displayh / rc_mask_cfg->cfg_param_03 * 2;
for (i = 0; i < rc_mask_cfg->cfg_param_03; i += 2) {
if (rc_mask_cfg->cfg_param_04[i] +
rc_mask_cfg->cfg_param_04[i+1] !=
half_panel_width) {
SDE_ERROR("invalid ratio [%d]:%d, [%d]:%d, %d\n",
i,
rc_mask_cfg->cfg_param_04[i],
i+1,
rc_mask_cfg->cfg_param_04[i+1],
half_panel_width);
return -EINVAL;
}
}
for (i = 0; i < rc_mask_cfg->cfg_param_03 - 1; i++) {
if (rc_mask_cfg->cfg_param_05[i] >=
rc_mask_cfg->cfg_param_05[i+1]) {
SDE_ERROR("invalid cfg_param_05 overlap %d, %d\n",
rc_mask_cfg->cfg_param_05[i],
rc_mask_cfg->cfg_param_05[i+1]);
return -EINVAL;
}
}
for (i = 0; i < rc_mask_cfg->cfg_param_03; i++) {
if (rc_mask_cfg->cfg_param_05[i] >
RC_DATA_SIZE_MAX) {
SDE_ERROR("invalid cfg_param_05[%d]:%d\n", i,
rc_mask_cfg->cfg_param_05[i]);
return -EINVAL;
}
}
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,
&param_r, &param_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, &param_r,
&param_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_r, 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("no previously programmed partial update rois\n");
memset(&merged_roi, 0, sizeof(struct sde_rect));
} else {
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;
}

71
msm/sde/sde_hw_rc.h Normal file
View File

@@ -0,0 +1,71 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020, 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

View File

@@ -806,6 +806,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;
}

View File

@@ -2,6 +2,7 @@
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <drm/msm_drm_pp.h>
#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)
@@ -133,6 +137,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] = {
@@ -159,6 +164,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] = {
@@ -1117,6 +1123,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;

View File

@@ -2,6 +2,7 @@
/*
* Copyright (c) 2017-2020, 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.

View File

@@ -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,
};
@@ -152,6 +154,7 @@ enum sde_reg_dma_setup_ops {
};
#define REG_DMA_BLK_MAX 32
/**
* enum sde_reg_dma_blk - defines blocks for which reg dma op should be
* performed