msm: mdss: Adding support for destination scaler

Destination scaling is a new hardware feature in MSM mdss 3xx hw. When user
mode enabled the destination upscaling, framebuffer will get upscaled
and then transmitted to the primary panel.

CRs-Fixed: 988990
Change-Id: I19aa5983316bec4a87811c8aa8b54f770001c45f
Signed-off-by: Benjamin Chan <bkchan@codeaurora.org>
Signed-off-by: Naseer Ahmed <naseer@codeaurora.org>
This commit is contained in:
Naseer Ahmed 2016-04-05 15:19:25 -04:00 committed by Kyle Yan
parent e215162bb2
commit 687db9c75e
9 changed files with 713 additions and 68 deletions

View file

@ -396,6 +396,10 @@ Optional properties:
to mdp block. Xin id property is not valid for mdp
internal blocks like ctl, lm, dspp. It should set
to 0xff for such blocks.
- qcom,max-dest-scaler-input-width: This 32 bit value provides
maximum width to the input of destination scaler.
- qcom,max-dest-scaler-output-width: This 32 bit value provides
maximum width to the output of destination scaler.
Fudge Factors: Fudge factors are used to boost demand for
resources like bus bandswidth, clk rate etc. to
@ -691,6 +695,8 @@ Example:
qcom,max-mixer-width = <2048>;
qcom,max-pipe-width = <2048>;
qcom,max-dest-scaler-input-width = <2048>;
qcom,max-dest-scaler-output-width = <2560>;
qcom,max-clk-rate = <320000000>;
qcom,vbif-settings = <0x0004 0x00000001>,
<0x00D8 0x00000707>;

View file

@ -503,6 +503,10 @@ struct mdss_data_type {
u32 bcolor1;
u32 bcolor2;
struct mdss_scaler_block *scaler_off;
u32 max_dest_scaler_input_width;
u32 max_dest_scaler_output_width;
struct mdss_mdp_destination_scaler *ds;
};
extern struct mdss_data_type *mdss_res;

View file

@ -116,6 +116,17 @@ static int mdss_fb_send_panel_event(struct msm_fb_data_type *mfd,
int event, void *arg);
static void mdss_fb_set_mdp_sync_pt_threshold(struct msm_fb_data_type *mfd,
int type);
static inline void __user *to_user_ptr(uint64_t address)
{
return (void __user *)(uintptr_t)address;
}
static inline uint64_t __user to_user_u64(void *ptr)
{
return (uint64_t)((uintptr_t)ptr);
}
void mdss_fb_no_update_notify_timer_cb(unsigned long data)
{
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
@ -4269,6 +4280,101 @@ err:
return ret;
}
static int __mdss_fb_copy_destscaler_data(struct fb_info *info,
struct mdp_layer_commit *commit)
{
int i;
int ret = 0;
u32 data_size;
struct mdp_destination_scaler_data __user *ds_data_user;
struct mdp_destination_scaler_data *ds_data = NULL;
void __user *scale_data_user;
struct mdp_scale_data_v2 *scale_data = NULL;
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
struct mdss_data_type *mdata;
if (!mfd || !mfd->mdp.private1) {
pr_err("mfd is NULL or operation not permitted\n");
ret = -EINVAL;
goto err;
}
mdata = mfd_to_mdata(mfd);
if (!mdata) {
pr_err("mdata is NULL or not initialized\n");
ret = -EINVAL;
goto err;
}
if (commit->commit_v1.dest_scaler_cnt >
mdata->scaler_off->ndest_scalers) {
pr_err("Commit destination scaler cnt larger than HW setting, commit cnt=%d\n",
commit->commit_v1.dest_scaler_cnt);
ret = -EINVAL;
goto err;
}
ds_data_user = (struct mdp_destination_scaler_data *)
commit->commit_v1.dest_scaler;
data_size = commit->commit_v1.dest_scaler_cnt *
sizeof(struct mdp_destination_scaler_data);
ds_data = kzalloc(data_size, GFP_KERNEL);
if (!ds_data) {
ret = -ENOMEM;
goto err;
}
ret = copy_from_user(ds_data, ds_data_user, data_size);
if (ret) {
pr_err("dest scaler data copy from user failed\n");
goto err;
}
commit->commit_v1.dest_scaler = ds_data;
for (i = 0; i < commit->commit_v1.dest_scaler_cnt; i++) {
scale_data = NULL;
if (ds_data[i].scale) {
scale_data_user = to_user_ptr(ds_data[i].scale);
data_size = sizeof(struct mdp_scale_data_v2);
scale_data = kzalloc(data_size, GFP_KERNEL);
if (!scale_data) {
ds_data[i].scale = 0;
ret = -ENOMEM;
goto err;
}
ds_data[i].scale = to_user_u64(scale_data);
}
if (scale_data && (ds_data[i].flags &
(MDP_DESTSCALER_SCALE_UPDATE |
MDP_DESTSCALER_ENHANCER_UPDATE))) {
ret = copy_from_user(scale_data, scale_data_user,
data_size);
if (ret) {
pr_err("scale data copy from user failed\n");
goto err;
}
}
}
return ret;
err:
if (ds_data) {
for (i = 0; i < commit->commit_v1.dest_scaler_cnt; i++) {
scale_data = to_user_ptr(ds_data[i].scale);
kfree(scale_data);
}
kfree(ds_data);
}
return ret;
}
static int mdss_fb_atomic_commit_ioctl(struct fb_info *info,
unsigned long *argp, struct file *file)
{
@ -4279,6 +4385,8 @@ static int mdss_fb_atomic_commit_ioctl(struct fb_info *info,
struct mdp_input_layer __user *input_layer_list;
struct mdp_output_layer *output_layer = NULL;
struct mdp_output_layer __user *output_layer_user;
struct mdp_destination_scaler_data *ds_data = NULL;
struct mdp_destination_scaler_data __user *ds_data_user;
ret = copy_from_user(&commit, argp, sizeof(struct mdp_layer_commit));
if (ret) {
@ -4356,6 +4464,16 @@ static int mdss_fb_atomic_commit_ioctl(struct fb_info *info,
}
}
ds_data_user = commit.commit_v1.dest_scaler;
if (ds_data_user) {
ret = __mdss_fb_copy_destscaler_data(info, &commit);
if (ret) {
pr_err("copy dest scaler failed\n");
goto err;
}
ds_data = commit.commit_v1.dest_scaler;
}
ATRACE_BEGIN("ATOMIC_COMMIT");
ret = mdss_fb_atomic_commit(info, &commit, file);
if (ret)
@ -4372,6 +4490,7 @@ static int mdss_fb_atomic_commit_ioctl(struct fb_info *info,
commit.commit_v1.input_layers = input_layer_list;
commit.commit_v1.output_layer = output_layer_user;
commit.commit_v1.dest_scaler = ds_data_user;
rc = copy_to_user(argp, &commit,
sizeof(struct mdp_layer_commit));
if (rc) {
@ -4397,6 +4516,11 @@ err:
}
kfree(layer_list);
kfree(output_layer);
if (ds_data) {
for (i = 0; i < commit.commit_v1.dest_scaler_cnt; i++)
kfree(to_user_ptr(ds_data[i].scale));
kfree(ds_data);
}
return ret;
}

View file

@ -1940,9 +1940,10 @@ static u32 mdss_mdp_res_init(struct mdss_data_type *mdata)
static u32 mdss_mdp_scaler_init(struct mdss_data_type *mdata,
struct device *dev)
{
int ret;
int ret = -EINVAL;
struct device_node *node;
u32 prop_val;
int len = 0;
if (!dev)
return -EPERM;
@ -1978,8 +1979,7 @@ static u32 mdss_mdp_scaler_init(struct mdss_data_type *mdata,
}
mdata->scaler_off->vig_scaler_lut_off = prop_val;
mdata->scaler_off->has_dest_scaler =
of_property_read_bool(mdata->pdev->dev.of_node,
"qcom,mdss-has-dest-scaler");
of_property_read_bool(node, "qcom,mdss-has-dest-scaler");
if (mdata->scaler_off->has_dest_scaler) {
ret = of_property_read_u32(node,
"qcom,mdss-dest-block-off",
@ -1991,40 +1991,65 @@ static u32 mdss_mdp_scaler_init(struct mdss_data_type *mdata,
}
mdata->scaler_off->dest_base = mdata->mdss_io.base +
prop_val;
mdata->scaler_off->ndest_scalers =
mdss_mdp_parse_dt_prop_len(mdata->pdev,
"qcom,mdss-dest-scalers-off");
if (!of_find_property(node, "qcom,mdss-dest-scaler-off", &len)
|| (len < 1)) {
pr_err("find property %s failed ret %d\n",
"qcom,mdss-dest-scaler-off", ret);
return -EINVAL;
}
mdata->scaler_off->ndest_scalers = len/sizeof(u32);
mdata->scaler_off->dest_scaler_off =
devm_kzalloc(&mdata->pdev->dev, sizeof(u32) *
devm_kzalloc(dev, sizeof(u32) *
mdata->scaler_off->ndest_scalers,
GFP_KERNEL);
if (!mdata->scaler_off->dest_scaler_off) {
kfree(mdata->scaler_off->dest_scaler_off);
return -ENOMEM;
}
ret = mdss_mdp_parse_dt_handler(mdata->pdev,
ret = of_property_read_u32_array(node,
"qcom,mdss-dest-scaler-off",
mdata->scaler_off->dest_scaler_off,
mdata->scaler_off->ndest_scalers);
if (ret)
return -EINVAL;
return ret;
mdata->scaler_off->dest_scaler_lut_off =
devm_kzalloc(&mdata->pdev->dev, sizeof(u32) *
devm_kzalloc(dev, sizeof(u32) *
mdata->scaler_off->ndest_scalers,
GFP_KERNEL);
if (!mdata->scaler_off->dest_scaler_lut_off) {
kfree(mdata->scaler_off->dest_scaler_lut_off);
return -ENOMEM;
}
ret = mdss_mdp_parse_dt_handler(mdata->pdev,
"qcom,mdss-dest-scalers-lut-off",
ret = of_property_read_u32_array(node,
"qcom,mdss-dest-scaler-lut-off",
mdata->scaler_off->dest_scaler_lut_off,
mdata->scaler_off->ndest_scalers);
if (ret)
return -EINVAL;
return ret;
ret = of_property_read_u32(dev->of_node,
"qcom,max-dest-scaler-input-width",
&mdata->max_dest_scaler_input_width);
if (ret) {
pr_debug("read property %s failed ret %d\n",
"qcom,max-dest-scaler-input-width",
ret);
}
return 0;
ret = of_property_read_u32(dev->of_node,
"qcom,max-dest-scaler-output-width",
&mdata->max_dest_scaler_output_width);
if (ret) {
pr_debug("read property %s failed ret %d\n",
"qcom,max-dest-scaler-output-width",
ret);
}
ret = mdss_mdp_ds_addr_setup(mdata);
}
return ret;
}
/**
@ -2322,6 +2347,16 @@ ssize_t mdss_mdp_show_capabilities(struct device *dev,
if (mdata->clk_factor.numer)
SPRINT("clk_fudge_factor=%u,%u\n", mdata->clk_factor.numer,
mdata->clk_factor.denom);
if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map)) {
SPRINT("max_dest_scaler_input_width=%u\n",
mdata->max_dest_scaler_input_width);
SPRINT("max_dest_scaler_output_width=%u\n",
mdata->max_dest_scaler_output_width);
SPRINT("dest_scaler_count=%u\n",
mdata->scaler_off->ndest_scalers);
SPRINT("max_dest_scale_up=%u\n", MAX_UPSCALE_RATIO);
}
SPRINT("features=");
if (mdata->has_bwc)
SPRINT(" bwc");

View file

@ -86,6 +86,36 @@
#define MAX_LAYER_COUNT 0xC
/**
* Destination Scaler control flags setting
*
* @DS_ENABLE: Setting the bit indicates Destination Scaler is enabled. Unset
* the bit indicates Destination Scaler is disable.
* @DS_DUAL_MODE: Setting the bit indicates Left and Right Destination Scaler
* are operated in Dual mode.
* @DS_LEFT: Setting the bit indicates current Destination Scaler is assigned
* with the Left LM. DS_LEFT and DS_DUAL_MODE can be used
* together.
* @DS_RIGHT: Setting the bit indicates current Destination Scaler is assigned
* with the Right LM. DS_RIGHT and DS_DUAL_MODE can be used
* together.
* @DS_SCALE_UPDATE: Setting the bit indicates current Destination Scaler
* QSEED3 parameters needs to be updated.
* @DS_ENHANCER_UPDATE: Setting this bit indicates current Desitnation Scaler
* QSEED3 Detial enhancer parameters need to be updated.
*/
#define DS_ENABLE BIT(0)
#define DS_DUAL_MODE BIT(1)
#define DS_LEFT BIT(2)
#define DS_RIGHT BIT(3)
#define DS_SCALE_UPDATE BIT(4)
#define DS_ENHANCER_UPDATE BIT(5)
/**
* Destination Scaler DUAL mode overfetch pixel count
*/
#define MDSS_MDP_DS_OVERFETCH_SIZE 5
/* hw cursor can only be setup in highest mixer stage */
#define HW_CURSOR_STAGE(mdata) \
(((mdata)->max_target_zorder + MDSS_MDP_STAGE_0) - 1)
@ -310,6 +340,25 @@ struct mdss_mdp_writeback {
u8 supported_output_formats[BITS_TO_BYTES(MDP_IMGTYPE_LIMIT1)];
};
/*
* Destination scaler info
* destination scaler is hard wired to DSPP0/1 and LM0/1
* Input dimension is always matching to LM output dimension
* Output dimension is the Panel/WB dimension
* In bypass mode (off), input and output dimension is the same
*/
struct mdss_mdp_destination_scaler {
u32 num;
char __iomem *ds_base;
char __iomem *scaler_base;
char __iomem *lut_base;
u16 src_width;
u16 src_height;
u32 flags;
struct mdp_scale_data_v2 scaler;
};
struct mdss_mdp_ctl_intfs_ops {
int (*start_fnc)(struct mdss_mdp_ctl *ctl);
int (*stop_fnc)(struct mdss_mdp_ctl *ctl, int panel_power_state);
@ -462,6 +511,8 @@ struct mdss_mdp_mixer {
char __iomem *base;
char __iomem *dspp_base;
char __iomem *pingpong_base;
/* Destination Scaler is hard wired to each mixer */
struct mdss_mdp_destination_scaler *ds;
u8 type;
u8 params_changed;
u16 width;
@ -1049,12 +1100,63 @@ static inline int mdss_mdp_line_buffer_width(void)
return MAX_LINE_BUFFER_WIDTH;
}
static inline int is_dest_scaling_enable(struct mdss_mdp_mixer *mixer)
{
return (test_bit(MDSS_CAPS_DEST_SCALER, mdss_res->mdss_caps_map) &&
mixer && mixer->ds && (mixer->ds->flags & DS_ENABLE));
}
static inline u32 get_ds_input_width(struct mdss_mdp_mixer *mixer)
{
struct mdss_mdp_destination_scaler *ds;
ds = mixer->ds;
if (ds)
return ds->src_width;
return 0;
}
static inline u32 get_ds_input_height(struct mdss_mdp_mixer *mixer)
{
struct mdss_mdp_destination_scaler *ds;
ds = mixer->ds;
if (ds)
return ds->src_height;
return 0;
}
static inline u32 get_ds_output_width(struct mdss_mdp_mixer *mixer)
{
struct mdss_mdp_destination_scaler *ds;
ds = mixer->ds;
if (ds)
return ds->scaler.dst_width;
return 0;
}
static inline u32 get_ds_output_height(struct mdss_mdp_mixer *mixer)
{
struct mdss_mdp_destination_scaler *ds;
ds = mixer->ds;
if (ds)
return ds->scaler.dst_height;
return 0;
}
static inline u32 get_panel_yres(struct mdss_panel_info *pinfo)
{
u32 yres;
yres = pinfo->yres + pinfo->lcdc.border_top +
pinfo->lcdc.border_bottom;
return yres;
}
@ -1064,6 +1166,7 @@ static inline u32 get_panel_xres(struct mdss_panel_info *pinfo)
xres = pinfo->xres + pinfo->lcdc.border_left +
pinfo->lcdc.border_right;
return xres;
}
@ -1653,6 +1756,7 @@ int mdss_mdp_ctl_addr_setup(struct mdss_data_type *mdata, u32 *ctl_offsets,
u32 len);
int mdss_mdp_wb_addr_setup(struct mdss_data_type *mdata,
u32 num_wb, u32 num_intf_wb);
int mdss_mdp_ds_addr_setup(struct mdss_data_type *mdata);
void mdss_mdp_pipe_clk_force_off(struct mdss_mdp_pipe *pipe);
int mdss_mdp_pipe_fetch_halt(struct mdss_mdp_pipe *pipe, bool is_recovery);

View file

@ -1205,6 +1205,7 @@ static void mdss_mdp_perf_calc_mixer(struct mdss_mdp_mixer *mixer,
struct mdss_panel_info *pinfo = NULL;
int fps = DEFAULT_FRAME_RATE;
u32 v_total = 0, bpp = MDSS_MDP_WB_OUTPUT_BPP;
u32 h_total = 0;
int i;
u32 max_clk_rate = 0;
u64 bw_overlap_max = 0;
@ -1235,6 +1236,10 @@ static void mdss_mdp_perf_calc_mixer(struct mdss_mdp_mixer *mixer,
fps = mdss_panel_get_framerate(pinfo);
v_total = mdss_panel_get_vtotal(pinfo);
}
if (is_dest_scaling_enable(mixer))
h_total = get_ds_output_width(mixer);
else
h_total = mixer->width;
} else {
v_total = mixer->height;
}
@ -1248,7 +1253,11 @@ static void mdss_mdp_perf_calc_mixer(struct mdss_mdp_mixer *mixer,
pinfo = NULL;
}
perf->mdp_clk_rate = mixer->width * v_total * fps;
/*
* with destination scaling, the increase of clock
* calculation should depends on output of size of DS setting.
*/
perf->mdp_clk_rate = h_total * v_total * fps;
perf->mdp_clk_rate =
mdss_mdp_clk_fudge_factor(mixer, perf->mdp_clk_rate);
@ -3429,8 +3438,15 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl)
split_ctl = mdss_mdp_get_split_ctl(ctl);
if (is_dest_scaling_enable(ctl->mixer_left)) {
width = get_ds_input_width(ctl->mixer_left);
height = get_ds_input_height(ctl->mixer_left);
if (ctl->panel_data->next && is_pingpong_split(ctl->mfd))
width *= 2;
} else {
width = get_panel_width(ctl);
height = get_panel_yres(pinfo);
}
max_mixer_width = ctl->mdata->max_mixer_width;
@ -3593,8 +3609,13 @@ int mdss_mdp_ctl_reconfig(struct mdss_mdp_ctl *ctl,
ctl->opmode |= (ctl->intf_num << 4);
skip_intf_reconfig:
if (is_dest_scaling_enable(ctl->mixer_left)) {
ctl->width = get_ds_input_width(ctl->mixer_left);
ctl->height = get_ds_input_height(ctl->mixer_left);
} else {
ctl->width = get_panel_xres(&pdata->panel_info);
ctl->height = get_panel_yres(&pdata->panel_info);
}
if (ctl->mixer_left) {
ctl->mixer_left->width = ctl->width;
ctl->mixer_left->height = ctl->height;
@ -3741,11 +3762,6 @@ int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl,
return -ENODEV;
}
sctl->width = get_panel_xres(&pdata->panel_info);
sctl->height = get_panel_yres(&pdata->panel_info);
sctl->roi = (struct mdss_rect){0, 0, sctl->width, sctl->height};
if (!ctl->mixer_left) {
ctl->mixer_left = mdss_mdp_mixer_alloc(ctl,
MDSS_MDP_MIXER_TYPE_INTF,
@ -3764,6 +3780,16 @@ int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl,
return -ENOMEM;
}
if (is_dest_scaling_enable(mixer)) {
sctl->width = get_ds_input_width(mixer);
sctl->height = get_ds_input_height(mixer);
} else {
sctl->width = get_panel_xres(&pdata->panel_info);
sctl->height = get_panel_yres(&pdata->panel_info);
}
sctl->roi = (struct mdss_rect){0, 0, sctl->width, sctl->height};
mixer->is_right_mixer = true;
mixer->width = sctl->width;
mixer->height = sctl->height;
@ -4942,6 +4968,43 @@ int mdss_mdp_wb_addr_setup(struct mdss_data_type *mdata,
return 0;
}
int mdss_mdp_ds_addr_setup(struct mdss_data_type *mdata)
{
struct mdss_mdp_destination_scaler *ds;
struct mdss_mdp_mixer *mixer = mdata->mixer_intf;
u32 num_ds_block;
int i;
num_ds_block = mdata->scaler_off->ndest_scalers;
ds = devm_kcalloc(&mdata->pdev->dev, num_ds_block,
sizeof(struct mdss_mdp_destination_scaler),
GFP_KERNEL);
if (!ds) {
pr_err("unable to setup ds: kzalloc failed\n");
return -ENOMEM;
}
for (i = 0; i < num_ds_block; i++) {
ds[i].num = i;
ds[i].ds_base = mdata->scaler_off->dest_base;
ds[i].scaler_base = mdata->scaler_off->dest_base +
mdata->scaler_off->dest_scaler_off[i];
ds[i].lut_base = mdata->scaler_off->dest_base +
mdata->scaler_off->dest_scaler_lut_off[i];
/*
* Assigning destination scaler to each LM. There is no dynamic
* assignment because destination scaler and LM are hard wired.
*/
if (i < mdata->nmixers_intf)
mixer[i].ds = &ds[i];
}
mdata->ds = ds;
return 0;
}
struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux)
{
struct mdss_mdp_mixer *mixer = NULL;

View file

@ -346,6 +346,10 @@ enum mdss_mdp_sspp_chroma_samp_type {
#define MDSS_MDP_REG_SCALER_MISR_SIGNATURE_0 0x74
#define MDSS_MDP_REG_SCALER_MISR_SIGNATURE_1 0x78
/* Destination scaler TOP registers */
#define MDSS_MDP_REG_DEST_SCALER_OP_MODE 0x00
#define MDSS_MDP_REG_DEST_SCALER_HW_VERSION 0x10
#define SCALER_EN BIT(0)
#define SCALER_DIR_EN BIT(4)
#define SCALER_DE_EN BIT(8)

View file

@ -62,6 +62,208 @@ struct mdss_mdp_validate_info_t {
struct mdss_mdp_pipe_multirect_params multirect;
};
static inline void *u64_to_ptr(uint64_t address)
{
return (void *)(uintptr_t)address;
}
static int __dest_scaler_data_setup(struct mdp_destination_scaler_data *ds_data,
struct mdss_mdp_destination_scaler *ds,
u32 max_input_width, u32 max_output_width)
{
struct mdp_scale_data_v2 *scale;
ds->flags = (ds_data->flags & MDP_DESTSCALER_ENABLE) ? DS_ENABLE : 0;
if (ds_data->flags & (MDP_DESTSCALER_SCALE_UPDATE |
MDP_DESTSCALER_ENHANCER_UPDATE)) {
if (!ds_data->scale) {
pr_err("NULL scale data\n");
return -EFAULT;
}
scale = u64_to_ptr(ds_data->scale);
if (scale->src_width[0] > max_input_width) {
pr_err("Exceed max input width for dest scaler-%d: %d\n",
ds_data->dest_scaler_ndx,
scale->src_width[0]);
return -EINVAL;
}
if (scale->dst_width > max_output_width) {
pr_err("Exceed max output width for dest scaler-%d: %d\n",
ds_data->dest_scaler_ndx,
scale->dst_width);
return -EINVAL;
}
memcpy(&ds->scaler, scale, sizeof(*scale));
if (ds_data->flags & MDP_DESTSCALER_SCALE_UPDATE)
ds->flags |= DS_SCALE_UPDATE;
if (ds_data->flags & MDP_DESTSCALER_ENHANCER_UPDATE)
ds->flags |= DS_ENHANCER_UPDATE;
ds->src_width = scale->src_width[0];
ds->src_height = scale->src_height[0];
}
if (ds_data->flags == 0) {
pr_debug("Disabling destination scaler-%d\n",
ds_data->dest_scaler_ndx);
}
return 0;
}
static int mdss_mdp_destination_scaler_pre_validate(struct mdss_mdp_ctl *ctl,
struct mdp_destination_scaler_data *ds_data)
{
struct mdss_data_type *mdata;
struct mdss_panel_info *pinfo;
mdata = ctl->mdata;
/*
* we need to quickly check for any scale update, and adjust the mixer
* width and height accordingly. Otherwise, layer validate will fail
* when we switch between scaling factor or disabling scaling.
*/
if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) && ds_data) {
if (ctl->mixer_left) {
/*
* Any scale update from usermode, we will update the
* mixer width and height with the given LM width and
* height.
*/
pinfo = &ctl->panel_data->panel_info;
if ((ds_data->lm_width > get_panel_xres(pinfo)) ||
(ds_data->lm_height > get_panel_yres(pinfo)) ||
(ds_data->lm_width == 0) ||
(ds_data->lm_height == 0)) {
pr_err("Invalid LM width / height setting\n");
return -EINVAL;
}
ctl->width = ds_data->lm_width;
ctl->height = ds_data->lm_height;
ctl->mixer_left->width = ds_data->lm_width;
ctl->mixer_left->height = ds_data->lm_height;
pr_debug("Update mixer-left width/height: %dx%d\n",
ds_data->lm_width, ds_data->lm_width);
}
if (ctl->mixer_right) {
/*
* Split display both left and right should have the
* same width and height
*/
ctl->mixer_right->width = ds_data->lm_width;
ctl->mixer_right->height = ds_data->lm_height;
pr_info("Update mixer-right width/height: %dx%d\n",
ds_data->lm_width, ds_data->lm_height);
/*
* For split display, CTL width should be equal to
* whole panel size
*/
ctl->width += ds_data->lm_width;
}
pr_debug("Updated CTL width:%d, height:%d\n",
ctl->width, ctl->height);
}
return 0;
}
static int mdss_mdp_validate_destination_scaler(struct msm_fb_data_type *mfd,
struct mdp_destination_scaler_data *ds_data,
u32 ds_mode)
{
int ret = 0;
struct mdss_data_type *mdata;
struct mdss_mdp_ctl *ctl;
struct mdss_mdp_destination_scaler *ds_left = NULL;
struct mdss_mdp_destination_scaler *ds_right = NULL;
if (ds_data) {
mdata = mfd_to_mdata(mfd);
ctl = mfd_to_ctl(mfd);
if (ctl->mixer_left)
ds_left = ctl->mixer_left->ds;
if (ctl->mixer_right)
ds_right = ctl->mixer_right->ds;
switch (ds_mode) {
case DS_DUAL_MODE:
if (!ds_left || !ds_right) {
pr_err("Cannot support DUAL mode dest scaling\n");
return -EINVAL;
}
ret = __dest_scaler_data_setup(&ds_data[0], ds_left,
mdata->max_dest_scaler_input_width -
MDSS_MDP_DS_OVERFETCH_SIZE,
mdata->max_dest_scaler_output_width);
if (ret)
return ret;
ret = __dest_scaler_data_setup(&ds_data[1], ds_right,
mdata->max_dest_scaler_input_width -
MDSS_MDP_DS_OVERFETCH_SIZE,
mdata->max_dest_scaler_output_width);
if (ret)
return ret;
ds_left->flags &= ~(DS_LEFT|DS_RIGHT);
ds_left->flags |= DS_DUAL_MODE;
ds_right->flags &= ~(DS_LEFT|DS_RIGHT);
ds_right->flags |= DS_DUAL_MODE;
break;
case DS_LEFT:
if (!ds_left) {
pr_err("LM in ctl does not support Destination Scaler\n");
return -EINVAL;
}
ds_left->flags &= ~(DS_DUAL_MODE|DS_RIGHT);
ds_left->flags |= DS_LEFT;
ret = __dest_scaler_data_setup(&ds_data[0], ds_left,
mdata->max_dest_scaler_input_width,
mdata->max_dest_scaler_output_width);
break;
case DS_RIGHT:
if (!ds_right) {
pr_err("Cannot setup DS_RIGHT because only single DS assigned to ctl\n");
return -EINVAL;
}
ds_right->flags &= ~(DS_DUAL_MODE|DS_LEFT);
ds_right->flags |= DS_RIGHT;
ret = __dest_scaler_data_setup(&ds_data[0], ds_right,
mdata->max_dest_scaler_input_width,
mdata->max_dest_scaler_output_width);
break;
}
} else {
pr_err("NULL destionation scaler data\n");
return -EFAULT;
}
if (ds_left)
pr_debug("DS_LEFT: flags=0x%X\n", ds_left->flags);
if (ds_right)
pr_debug("DS_RIGHT: flags=0x%X\n", ds_right->flags);
return ret;
}
/*
* __layer_needs_src_split() - check needs source split configuration
* @layer: input layer
@ -1605,11 +1807,13 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
u32 left_lm_w = left_lm_w_from_mfd(mfd);
u32 mixer_mux, dst_x;
int layer_count = commit->input_layer_cnt;
u32 ds_mode = 0;
struct mdss_mdp_pipe *pipe, *tmp, *left_blend_pipe;
struct mdss_mdp_pipe *right_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_mdp_pipe *left_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_data_type *mdata = mfd_to_mdata(mfd);
struct mdss_mdp_mixer *mixer = NULL;
struct mdp_input_layer *layer, *prev_layer, *layer_list;
@ -1863,6 +2067,37 @@ static int __validate_layers(struct msm_fb_data_type *mfd,
layer->z_order -= MDSS_MDP_STAGE_0;
}
if (test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) &&
commit->dest_scaler) {
/*
* Find out which DS block to use based on LM assignment
*/
if ((left_cnt > 0) && (right_cnt > 0) &&
(commit->dest_scaler_cnt == 2))
ds_mode = DS_DUAL_MODE;
else if ((left_cnt > 0) && (right_cnt == 0) &&
(commit->dest_scaler_cnt == 1))
ds_mode = DS_LEFT;
else if ((left_cnt == 0) && (right_cnt > 0) &&
(commit->dest_scaler_cnt == 1))
ds_mode = DS_RIGHT;
else {
pr_err("Commit destination scaler count not matching with LM assignment, DS-cnt:%d\n",
commit->dest_scaler_cnt);
ret = -EINVAL;
goto validate_exit;
}
ret = mdss_mdp_validate_destination_scaler(mfd,
commit->dest_scaler,
ds_mode);
if (ret) {
pr_err("fail to validate destination scaler\n");
layer->error_code = ret;
goto validate_exit;
}
}
ret = mdss_mdp_perf_bw_check(mdp5_data->ctl, left_plist, left_cnt,
right_plist, right_cnt);
if (ret) {
@ -2130,6 +2365,12 @@ int mdss_mdp_layer_atomic_validate(struct msm_fb_data_type *mfd,
}
}
if (mdss_mdp_destination_scaler_pre_validate(mdp5_data->ctl,
commit->dest_scaler)) {
pr_err("Destination scaler pre-validate failed\n");
return -EINVAL;
}
return __validate_layers(mfd, file, commit);
}

View file

@ -221,6 +221,26 @@ struct mdp_csc_cfg mdp_csc_10bit_convert[MDSS_MDP_MAX_CSC] = {
},
};
static struct mdss_mdp_format_params dest_scaler_fmt = {
.format = MDP_XBGR_2101010,
.flag = 0,
.fetch_planes = MDSS_MDP_PLANE_INTERLEAVED,
.unpack_tight = 1,
.unpack_align_msb = 0,
.alpha_enable = 0,
.unpack_count = 4,
.bpp = 4,
.fetch_mode = MDSS_MDP_FETCH_LINEAR,
.element = { C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr },
.bits = {
[C3_ALPHA] = 3,
[C2_R_Cr] = 3,
[C0_G_Y] = 3,
[C1_B_Cb] = 3,
},
.unpack_dx_format = 1,
};
#define CSC_MV_OFF 0x0
#define CSC_BV_OFF 0x2C
#define CSC_LV_OFF 0x14
@ -1589,48 +1609,15 @@ static void mdss_mdp_scaler_detail_enhance_cfg(
}
}
int mdss_mdp_qseed3_setup(struct mdss_mdp_pipe *pipe,
int location, int id)
int mdss_mdp_qseed3_setup(struct mdp_scale_data_v2 *scaler,
char __iomem *offset,
char __iomem *lut_offset,
struct mdss_mdp_format_params *fmt)
{
int rc = 0;
struct mdp_scale_data_v2 *scaler;
struct mdss_data_type *mdata;
char __iomem *offset, *lut_offset;
struct mdss_mdp_format_params *fmt;
uint32_t op_mode = 0;
uint32_t phase_init, preload, src_y_rgb, src_uv, dst;
mdata = mdss_mdp_get_mdata();
/* SRC pipe QSEED3 Configuration */
if (location == SSPP_VIG) {
scaler = &pipe->scaler;
offset = pipe->base + mdata->scaler_off->vig_scaler_off;
lut_offset = pipe->base + mdata->scaler_off->vig_scaler_lut_off;
fmt = pipe->src_fmt;
} else if (location == DSPP) {
/* Destination scaler QSEED3 Configuration */
if ((mdata->scaler_off->has_dest_scaler) &&
(id < mdata->scaler_off->ndest_scalers)) {
/* TODO :point to the destination params */
scaler = NULL;
offset = mdata->scaler_off->dest_base +
mdata->scaler_off->dest_scaler_off[id];
lut_offset = mdata->scaler_off->dest_base +
mdata->scaler_off->dest_scaler_lut_off[id];
/*TODO : set pixel fmt to RGB101010 */
return -ENOSYS;
} else {
return -EINVAL;
}
} else {
return -EINVAL;
}
if (!scaler) {
pr_debug("scaler pointer is NULL\n");
return 0;
}
pr_debug("scaler->enable=%d", scaler->enable);
if (scaler->enable) {
@ -1650,8 +1637,6 @@ int mdss_mdp_qseed3_setup(struct mdss_mdp_pipe *pipe,
ALPHA_FILTER_CFG;
}
/* TODO:if src_fmt is 10 bits program the bitwidth
* accordingly */
if (!fmt->unpack_dx_format)
op_mode |= 0x1 << SCALER_BIT_WIDTH;
@ -1750,12 +1735,24 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe,
{
struct mdss_data_type *mdata;
int rc = 0;
char __iomem *offset, *lut_offset;
mdata = mdss_mdp_get_mdata();
if (test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map))
rc = mdss_mdp_qseed3_setup(pipe, pp_blk, 0);
else
if (test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map)) {
if (pp_blk == SSPP_VIG) {
offset = pipe->base + mdata->scaler_off->vig_scaler_off;
lut_offset = pipe->base +
mdata->scaler_off->vig_scaler_lut_off;
rc = mdss_mdp_qseed3_setup(&pipe->scaler, offset,
lut_offset, pipe->src_fmt);
} else {
rc = -EINVAL;
}
} else {
rc = mdss_mdp_qseed2_setup(pipe);
}
if (rc)
pr_err("scale setup on pipe %d type %d failed ret %d\n",
@ -2432,6 +2429,71 @@ dspp_exit:
return ret;
}
static int pp_dest_scaler_setup(struct mdss_mdp_mixer *mixer)
{
struct mdss_mdp_ctl *ctl;
struct mdss_data_type *mdata;
struct mdss_mdp_destination_scaler *ds;
int ret = 0;
u32 op_mode;
u32 mask;
char *ds_offset;
if (!mixer || !mixer->ctl || !mixer->ctl->mdata)
return -EINVAL;
ctl = mixer->ctl;
mdata = ctl->mdata;
ds = mixer->ds;
if (!test_bit(MDSS_CAPS_DEST_SCALER, mdata->mdss_caps_map) || !ds)
return 0;
ds_offset = ds->ds_base;
op_mode = readl_relaxed(MDSS_MDP_REG_DEST_SCALER_OP_MODE +
ds_offset);
mask = BIT(ds->num);
if (ds->flags & DS_ENABLE)
op_mode |= mask;
else
op_mode &= ~mask;
if (ds->flags & DS_DUAL_MODE)
op_mode |= BIT(16);
else
op_mode &= ~BIT(16);
writel_relaxed(op_mode, MDSS_MDP_REG_DEST_SCALER_OP_MODE + ds_offset);
if (ds->flags & DS_SCALE_UPDATE) {
ret = mdss_mdp_qseed3_setup(&ds->scaler,
ds->scaler_base, ds->lut_base,
&dest_scaler_fmt);
if (ret) {
pr_err("Failed setup destination scaler\n");
return ret;
}
/*
* Clearing the flag because we don't need to program the block
* for each commit if there is no change.
*/
ds->flags &= ~DS_SCALE_UPDATE;
}
if (ds->flags & DS_ENHANCER_UPDATE) {
mdss_mdp_scaler_detail_enhance_cfg(&ds->scaler.detail_enhance,
ds->scaler_base);
ds->flags &= ~DS_ENHANCER_UPDATE;
}
/* Destinations scaler shared the flush with DSPP in control */
if (ds->flags & DS_ENABLE)
ctl->flush_bits |= BIT(13 + ds->num);
return 0;
}
int mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl)
{
int ret = 0;
@ -2521,11 +2583,13 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
}
if (ctl->mixer_left) {
pp_dest_scaler_setup(ctl->mixer_left);
pp_mixer_setup(ctl->mixer_left);
pp_dspp_setup(disp_num, ctl->mixer_left);
pp_ppb_setup(ctl->mixer_left);
}
if (ctl->mixer_right) {
pp_dest_scaler_setup(ctl->mixer_right);
pp_mixer_setup(ctl->mixer_right);
pp_dspp_setup(disp_num, ctl->mixer_right);
pp_ppb_setup(ctl->mixer_right);