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 to mdp block. Xin id property is not valid for mdp
internal blocks like ctl, lm, dspp. It should set internal blocks like ctl, lm, dspp. It should set
to 0xff for such blocks. 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 Fudge Factors: Fudge factors are used to boost demand for
resources like bus bandswidth, clk rate etc. to resources like bus bandswidth, clk rate etc. to
@ -691,6 +695,8 @@ Example:
qcom,max-mixer-width = <2048>; qcom,max-mixer-width = <2048>;
qcom,max-pipe-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,max-clk-rate = <320000000>;
qcom,vbif-settings = <0x0004 0x00000001>, qcom,vbif-settings = <0x0004 0x00000001>,
<0x00D8 0x00000707>; <0x00D8 0x00000707>;

View file

@ -503,6 +503,10 @@ struct mdss_data_type {
u32 bcolor1; u32 bcolor1;
u32 bcolor2; u32 bcolor2;
struct mdss_scaler_block *scaler_off; 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; 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); int event, void *arg);
static void mdss_fb_set_mdp_sync_pt_threshold(struct msm_fb_data_type *mfd, static void mdss_fb_set_mdp_sync_pt_threshold(struct msm_fb_data_type *mfd,
int type); 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) void mdss_fb_no_update_notify_timer_cb(unsigned long data)
{ {
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
@ -4269,6 +4280,101 @@ err:
return ret; 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, static int mdss_fb_atomic_commit_ioctl(struct fb_info *info,
unsigned long *argp, struct file *file) 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_input_layer __user *input_layer_list;
struct mdp_output_layer *output_layer = NULL; struct mdp_output_layer *output_layer = NULL;
struct mdp_output_layer __user *output_layer_user; 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)); ret = copy_from_user(&commit, argp, sizeof(struct mdp_layer_commit));
if (ret) { 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"); ATRACE_BEGIN("ATOMIC_COMMIT");
ret = mdss_fb_atomic_commit(info, &commit, file); ret = mdss_fb_atomic_commit(info, &commit, file);
if (ret) 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.input_layers = input_layer_list;
commit.commit_v1.output_layer = output_layer_user; commit.commit_v1.output_layer = output_layer_user;
commit.commit_v1.dest_scaler = ds_data_user;
rc = copy_to_user(argp, &commit, rc = copy_to_user(argp, &commit,
sizeof(struct mdp_layer_commit)); sizeof(struct mdp_layer_commit));
if (rc) { if (rc) {
@ -4397,6 +4516,11 @@ err:
} }
kfree(layer_list); kfree(layer_list);
kfree(output_layer); 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; 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, static u32 mdss_mdp_scaler_init(struct mdss_data_type *mdata,
struct device *dev) struct device *dev)
{ {
int ret; int ret = -EINVAL;
struct device_node *node; struct device_node *node;
u32 prop_val; u32 prop_val;
int len = 0;
if (!dev) if (!dev)
return -EPERM; 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->vig_scaler_lut_off = prop_val;
mdata->scaler_off->has_dest_scaler = mdata->scaler_off->has_dest_scaler =
of_property_read_bool(mdata->pdev->dev.of_node, of_property_read_bool(node, "qcom,mdss-has-dest-scaler");
"qcom,mdss-has-dest-scaler");
if (mdata->scaler_off->has_dest_scaler) { if (mdata->scaler_off->has_dest_scaler) {
ret = of_property_read_u32(node, ret = of_property_read_u32(node,
"qcom,mdss-dest-block-off", "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 + mdata->scaler_off->dest_base = mdata->mdss_io.base +
prop_val; prop_val;
mdata->scaler_off->ndest_scalers =
mdss_mdp_parse_dt_prop_len(mdata->pdev, if (!of_find_property(node, "qcom,mdss-dest-scaler-off", &len)
"qcom,mdss-dest-scalers-off"); || (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 = mdata->scaler_off->dest_scaler_off =
devm_kzalloc(&mdata->pdev->dev, sizeof(u32) * devm_kzalloc(dev, sizeof(u32) *
mdata->scaler_off->ndest_scalers, mdata->scaler_off->ndest_scalers,
GFP_KERNEL); GFP_KERNEL);
if (!mdata->scaler_off->dest_scaler_off) { if (!mdata->scaler_off->dest_scaler_off) {
kfree(mdata->scaler_off->dest_scaler_off);
return -ENOMEM; return -ENOMEM;
} }
ret = mdss_mdp_parse_dt_handler(mdata->pdev, ret = of_property_read_u32_array(node,
"qcom,mdss-dest-scaler-off", "qcom,mdss-dest-scaler-off",
mdata->scaler_off->dest_scaler_off, mdata->scaler_off->dest_scaler_off,
mdata->scaler_off->ndest_scalers); mdata->scaler_off->ndest_scalers);
if (ret) if (ret)
return -EINVAL; return ret;
mdata->scaler_off->dest_scaler_lut_off = mdata->scaler_off->dest_scaler_lut_off =
devm_kzalloc(&mdata->pdev->dev, sizeof(u32) * devm_kzalloc(dev, sizeof(u32) *
mdata->scaler_off->ndest_scalers, mdata->scaler_off->ndest_scalers,
GFP_KERNEL); GFP_KERNEL);
if (!mdata->scaler_off->dest_scaler_lut_off) { if (!mdata->scaler_off->dest_scaler_lut_off) {
kfree(mdata->scaler_off->dest_scaler_lut_off);
return -ENOMEM; return -ENOMEM;
} }
ret = mdss_mdp_parse_dt_handler(mdata->pdev, ret = of_property_read_u32_array(node,
"qcom,mdss-dest-scalers-lut-off", "qcom,mdss-dest-scaler-lut-off",
mdata->scaler_off->dest_scaler_lut_off, mdata->scaler_off->dest_scaler_lut_off,
mdata->scaler_off->ndest_scalers); mdata->scaler_off->ndest_scalers);
if (ret) 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);
}
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 0; return ret;
} }
/** /**
@ -2322,6 +2347,16 @@ ssize_t mdss_mdp_show_capabilities(struct device *dev,
if (mdata->clk_factor.numer) if (mdata->clk_factor.numer)
SPRINT("clk_fudge_factor=%u,%u\n", mdata->clk_factor.numer, SPRINT("clk_fudge_factor=%u,%u\n", mdata->clk_factor.numer,
mdata->clk_factor.denom); 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="); SPRINT("features=");
if (mdata->has_bwc) if (mdata->has_bwc)
SPRINT(" bwc"); SPRINT(" bwc");

View file

@ -86,6 +86,36 @@
#define MAX_LAYER_COUNT 0xC #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 */ /* hw cursor can only be setup in highest mixer stage */
#define HW_CURSOR_STAGE(mdata) \ #define HW_CURSOR_STAGE(mdata) \
(((mdata)->max_target_zorder + MDSS_MDP_STAGE_0) - 1) (((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)]; 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 { struct mdss_mdp_ctl_intfs_ops {
int (*start_fnc)(struct mdss_mdp_ctl *ctl); int (*start_fnc)(struct mdss_mdp_ctl *ctl);
int (*stop_fnc)(struct mdss_mdp_ctl *ctl, int panel_power_state); 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 *base;
char __iomem *dspp_base; char __iomem *dspp_base;
char __iomem *pingpong_base; char __iomem *pingpong_base;
/* Destination Scaler is hard wired to each mixer */
struct mdss_mdp_destination_scaler *ds;
u8 type; u8 type;
u8 params_changed; u8 params_changed;
u16 width; u16 width;
@ -1049,12 +1100,63 @@ static inline int mdss_mdp_line_buffer_width(void)
return MAX_LINE_BUFFER_WIDTH; 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) static inline u32 get_panel_yres(struct mdss_panel_info *pinfo)
{ {
u32 yres; u32 yres;
yres = pinfo->yres + pinfo->lcdc.border_top + yres = pinfo->yres + pinfo->lcdc.border_top +
pinfo->lcdc.border_bottom; pinfo->lcdc.border_bottom;
return yres; return yres;
} }
@ -1064,6 +1166,7 @@ static inline u32 get_panel_xres(struct mdss_panel_info *pinfo)
xres = pinfo->xres + pinfo->lcdc.border_left + xres = pinfo->xres + pinfo->lcdc.border_left +
pinfo->lcdc.border_right; pinfo->lcdc.border_right;
return xres; return xres;
} }
@ -1653,6 +1756,7 @@ int mdss_mdp_ctl_addr_setup(struct mdss_data_type *mdata, u32 *ctl_offsets,
u32 len); u32 len);
int mdss_mdp_wb_addr_setup(struct mdss_data_type *mdata, int mdss_mdp_wb_addr_setup(struct mdss_data_type *mdata,
u32 num_wb, u32 num_intf_wb); 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); 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); 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; struct mdss_panel_info *pinfo = NULL;
int fps = DEFAULT_FRAME_RATE; int fps = DEFAULT_FRAME_RATE;
u32 v_total = 0, bpp = MDSS_MDP_WB_OUTPUT_BPP; u32 v_total = 0, bpp = MDSS_MDP_WB_OUTPUT_BPP;
u32 h_total = 0;
int i; int i;
u32 max_clk_rate = 0; u32 max_clk_rate = 0;
u64 bw_overlap_max = 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); fps = mdss_panel_get_framerate(pinfo);
v_total = mdss_panel_get_vtotal(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 { } else {
v_total = mixer->height; v_total = mixer->height;
} }
@ -1248,7 +1253,11 @@ static void mdss_mdp_perf_calc_mixer(struct mdss_mdp_mixer *mixer,
pinfo = NULL; 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 = perf->mdp_clk_rate =
mdss_mdp_clk_fudge_factor(mixer, 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); split_ctl = mdss_mdp_get_split_ctl(ctl);
width = get_panel_width(ctl); if (is_dest_scaling_enable(ctl->mixer_left)) {
height = get_panel_yres(pinfo); 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; 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); ctl->opmode |= (ctl->intf_num << 4);
skip_intf_reconfig: skip_intf_reconfig:
ctl->width = get_panel_xres(&pdata->panel_info); if (is_dest_scaling_enable(ctl->mixer_left)) {
ctl->height = get_panel_yres(&pdata->panel_info); 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) { if (ctl->mixer_left) {
ctl->mixer_left->width = ctl->width; ctl->mixer_left->width = ctl->width;
ctl->mixer_left->height = ctl->height; ctl->mixer_left->height = ctl->height;
@ -3741,11 +3762,6 @@ int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl,
return -ENODEV; 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) { if (!ctl->mixer_left) {
ctl->mixer_left = mdss_mdp_mixer_alloc(ctl, ctl->mixer_left = mdss_mdp_mixer_alloc(ctl,
MDSS_MDP_MIXER_TYPE_INTF, MDSS_MDP_MIXER_TYPE_INTF,
@ -3764,6 +3780,16 @@ int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl,
return -ENOMEM; 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->is_right_mixer = true;
mixer->width = sctl->width; mixer->width = sctl->width;
mixer->height = sctl->height; mixer->height = sctl->height;
@ -4942,6 +4968,43 @@ int mdss_mdp_wb_addr_setup(struct mdss_data_type *mdata,
return 0; 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 *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux)
{ {
struct mdss_mdp_mixer *mixer = NULL; 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_0 0x74
#define MDSS_MDP_REG_SCALER_MISR_SIGNATURE_1 0x78 #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_EN BIT(0)
#define SCALER_DIR_EN BIT(4) #define SCALER_DIR_EN BIT(4)
#define SCALER_DE_EN BIT(8) #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; 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_needs_src_split() - check needs source split configuration
* @layer: input layer * @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 left_lm_w = left_lm_w_from_mfd(mfd);
u32 mixer_mux, dst_x; u32 mixer_mux, dst_x;
int layer_count = commit->input_layer_cnt; int layer_count = commit->input_layer_cnt;
u32 ds_mode = 0;
struct mdss_mdp_pipe *pipe, *tmp, *left_blend_pipe; struct mdss_mdp_pipe *pipe, *tmp, *left_blend_pipe;
struct mdss_mdp_pipe *right_plist[MAX_PIPES_PER_LM] = {0}; struct mdss_mdp_pipe *right_plist[MAX_PIPES_PER_LM] = {0};
struct mdss_mdp_pipe *left_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_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 mdss_mdp_mixer *mixer = NULL;
struct mdp_input_layer *layer, *prev_layer, *layer_list; 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; 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, ret = mdss_mdp_perf_bw_check(mdp5_data->ctl, left_plist, left_cnt,
right_plist, right_cnt); right_plist, right_cnt);
if (ret) { 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); 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_MV_OFF 0x0
#define CSC_BV_OFF 0x2C #define CSC_BV_OFF 0x2C
#define CSC_LV_OFF 0x14 #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 mdss_mdp_qseed3_setup(struct mdp_scale_data_v2 *scaler,
int location, int id) char __iomem *offset,
char __iomem *lut_offset,
struct mdss_mdp_format_params *fmt)
{ {
int rc = 0; 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 op_mode = 0;
uint32_t phase_init, preload, src_y_rgb, src_uv, dst; 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); pr_debug("scaler->enable=%d", scaler->enable);
if (scaler->enable) { if (scaler->enable) {
@ -1650,8 +1637,6 @@ int mdss_mdp_qseed3_setup(struct mdss_mdp_pipe *pipe,
ALPHA_FILTER_CFG; ALPHA_FILTER_CFG;
} }
/* TODO:if src_fmt is 10 bits program the bitwidth
* accordingly */
if (!fmt->unpack_dx_format) if (!fmt->unpack_dx_format)
op_mode |= 0x1 << SCALER_BIT_WIDTH; 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; struct mdss_data_type *mdata;
int rc = 0; int rc = 0;
char __iomem *offset, *lut_offset;
mdata = mdss_mdp_get_mdata(); mdata = mdss_mdp_get_mdata();
if (test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map))
rc = mdss_mdp_qseed3_setup(pipe, pp_blk, 0); if (test_bit(MDSS_CAPS_QSEED3, mdata->mdss_caps_map)) {
else 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); rc = mdss_mdp_qseed2_setup(pipe);
}
if (rc) if (rc)
pr_err("scale setup on pipe %d type %d failed ret %d\n", pr_err("scale setup on pipe %d type %d failed ret %d\n",
@ -2432,6 +2429,71 @@ dspp_exit:
return ret; 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 mdss_mdp_pp_setup(struct mdss_mdp_ctl *ctl)
{ {
int ret = 0; int ret = 0;
@ -2521,11 +2583,13 @@ int mdss_mdp_pp_setup_locked(struct mdss_mdp_ctl *ctl)
} }
if (ctl->mixer_left) { if (ctl->mixer_left) {
pp_dest_scaler_setup(ctl->mixer_left);
pp_mixer_setup(ctl->mixer_left); pp_mixer_setup(ctl->mixer_left);
pp_dspp_setup(disp_num, ctl->mixer_left); pp_dspp_setup(disp_num, ctl->mixer_left);
pp_ppb_setup(ctl->mixer_left); pp_ppb_setup(ctl->mixer_left);
} }
if (ctl->mixer_right) { if (ctl->mixer_right) {
pp_dest_scaler_setup(ctl->mixer_right);
pp_mixer_setup(ctl->mixer_right); pp_mixer_setup(ctl->mixer_right);
pp_dspp_setup(disp_num, ctl->mixer_right); pp_dspp_setup(disp_num, ctl->mixer_right);
pp_ppb_setup(ctl->mixer_right); pp_ppb_setup(ctl->mixer_right);