msm: mdss: add support dynamic resolution switch

Provide support for panel drivers to expose multiple panel timings
supported and expose this through existing fb sysfs mode nodes.
When switching between resolutions allow seamless transition between the
modes.

CRs-fixed: 873962
Change-Id: Iad660a47ea0a63ca0d2332339727b8fcd005dac6
Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
Signed-off-by: Chandan Uddaraju <chandanu@codeaurora.org>
[veeras@codeaurora.org: Resolve merge conflicts in few files]
Signed-off-by: Veera Sundaram Sankaran <veeras@codeaurora.org>
This commit is contained in:
Adrian Salido-Moreno 2015-04-23 18:33:56 -07:00 committed by David Keitel
parent 3344a14fd2
commit 649c91d1cd
7 changed files with 528 additions and 70 deletions

View file

@ -336,7 +336,7 @@ static int mdss_fb_get_panel_xres(struct mdss_panel_info *pinfo)
pdata = container_of(pinfo, struct mdss_panel_data, panel_info); pdata = container_of(pinfo, struct mdss_panel_data, panel_info);
xres = pinfo->xres; xres = pinfo->xres;
if (pdata->next) if (pdata->next && pdata->next->active)
xres += mdss_fb_get_panel_xres(&pdata->next->panel_info); xres += mdss_fb_get_panel_xres(&pdata->next->panel_info);
return xres; return xres;
@ -916,6 +916,82 @@ static void mdss_fb_unregister_input_handler(struct msm_fb_data_type *mfd)
kfree(mfd->input_handler); kfree(mfd->input_handler);
} }
static void mdss_fb_videomode_from_panel_timing(struct fb_videomode *videomode,
struct mdss_panel_timing *pt)
{
videomode->name = pt->name;
videomode->xres = pt->xres;
videomode->yres = pt->yres;
videomode->left_margin = pt->h_back_porch;
videomode->right_margin = pt->h_front_porch;
videomode->hsync_len = pt->h_pulse_width;
videomode->upper_margin = pt->v_back_porch;
videomode->lower_margin = pt->v_front_porch;
videomode->vsync_len = pt->v_pulse_width;
videomode->pixclock = pt->clk_rate;
videomode->refresh = pt->frame_rate;
videomode->flag = 0;
videomode->vmode = 0;
videomode->sync = 0;
}
static int mdss_fb_init_panel_modes(struct msm_fb_data_type *mfd,
struct mdss_panel_data *pdata)
{
struct fb_info *fbi = mfd->fbi;
struct fb_videomode *modedb;
struct mdss_panel_timing *pt;
struct list_head *pos;
int num_timings = 0;
int i = 0;
/* check if multiple modes are supported */
if (!fbi || !pdata->current_timing)
return 0;
list_for_each(pos, &pdata->timings_list)
num_timings++;
modedb = devm_kzalloc(fbi->dev, num_timings * sizeof(*modedb),
GFP_KERNEL);
if (!modedb)
return -ENOMEM;
list_for_each_entry(pt, &pdata->timings_list, list) {
struct mdss_panel_timing *spt = NULL;
mdss_fb_videomode_from_panel_timing(modedb + i, pt);
if (pdata->next) {
spt = mdss_panel_get_timing_by_name(pdata->next,
modedb[i].name);
if (!IS_ERR_OR_NULL(spt))
modedb[i].xres += spt->xres;
else
pr_debug("no matching split config for %s\n",
modedb[i].name);
/*
* if no panel timing found for current, need to
* disable it otherwise mark it as active
*/
if (pt == pdata->current_timing)
pdata->next->active = !IS_ERR_OR_NULL(spt);
}
if (pt == pdata->current_timing) {
pr_debug("found current mode: %s\n", pt->name);
fbi->mode = modedb + i;
}
i++;
}
fbi->monspecs.modedb = modedb;
fbi->monspecs.modedb_len = num_timings;
fb_videomode_to_modelist(modedb, num_timings, &fbi->modelist);
return 0;
}
static int mdss_fb_probe(struct platform_device *pdev) static int mdss_fb_probe(struct platform_device *pdev)
{ {
struct msm_fb_data_type *mfd = NULL; struct msm_fb_data_type *mfd = NULL;
@ -1033,6 +1109,8 @@ static int mdss_fb_probe(struct platform_device *pdev)
lcd_backlight_registered = 1; lcd_backlight_registered = 1;
} }
mdss_fb_init_panel_modes(mfd, pdata);
mdss_fb_create_sysfs(mfd); mdss_fb_create_sysfs(mfd);
mdss_fb_send_panel_event(mfd, MDSS_EVENT_FB_REGISTERED, fbi); mdss_fb_send_panel_event(mfd, MDSS_EVENT_FB_REGISTERED, fbi);
@ -2946,34 +3024,38 @@ u32 mdss_fb_get_mode_switch(struct msm_fb_data_type *mfd)
* and return values (such as buffer release fences) are based on the * and return values (such as buffer release fences) are based on the
* panel mode being switching into. * panel mode being switching into.
*/ */
int __ioctl_transition_dyn_mode_state(struct msm_fb_data_type *mfd, static int __ioctl_transition_dyn_mode_state(struct msm_fb_data_type *mfd,
unsigned int cmd, int validate) unsigned int cmd, int validate)
{ {
if (cmd == MDSS_MDP_NO_UPDATE_REQUESTED) if (mfd->switch_state == MDSS_MDP_NO_UPDATE_REQUESTED)
return 0; return 0;
mutex_lock(&mfd->switch_lock); mutex_lock(&mfd->switch_lock);
switch (cmd) { switch (cmd) {
case MSMFB_BUFFER_SYNC: case MSMFB_BUFFER_SYNC:
if (mfd->switch_state == MDSS_MDP_WAIT_FOR_SYNC) { if (mfd->switch_state == MDSS_MDP_WAIT_FOR_SYNC) {
mdss_fb_set_mdp_sync_pt_threshold(mfd, if (mfd->switch_new_mode != SWITCH_RESOLUTION)
mfd->switch_new_mode); mdss_fb_set_mdp_sync_pt_threshold(mfd,
mfd->switch_new_mode);
mfd->switch_state = MDSS_MDP_WAIT_FOR_COMMIT; mfd->switch_state = MDSS_MDP_WAIT_FOR_COMMIT;
} }
break; break;
case MSMFB_OVERLAY_PREPARE: case MSMFB_OVERLAY_PREPARE:
if (mfd->switch_state == MDSS_MDP_WAIT_FOR_PREP) { if (mfd->switch_state == MDSS_MDP_WAIT_FOR_PREP) {
mfd->pending_switch = true; if (mfd->switch_new_mode != SWITCH_RESOLUTION)
mfd->pending_switch = true;
mfd->switch_state = MDSS_MDP_WAIT_FOR_SYNC; mfd->switch_state = MDSS_MDP_WAIT_FOR_SYNC;
} }
break; break;
case MSMFB_ATOMIC_COMMIT: case MSMFB_ATOMIC_COMMIT:
if ((mfd->switch_state == MDSS_MDP_WAIT_FOR_PREP) && validate) { if ((mfd->switch_state == MDSS_MDP_WAIT_FOR_PREP) && validate) {
mfd->pending_switch = true; if (mfd->switch_new_mode != SWITCH_RESOLUTION)
mfd->pending_switch = true;
mfd->switch_state = MDSS_MDP_WAIT_FOR_SYNC; mfd->switch_state = MDSS_MDP_WAIT_FOR_SYNC;
} else if (mfd->switch_state == MDSS_MDP_WAIT_FOR_SYNC) { } else if (mfd->switch_state == MDSS_MDP_WAIT_FOR_SYNC) {
mdss_fb_set_mdp_sync_pt_threshold(mfd, if (mfd->switch_new_mode != SWITCH_RESOLUTION)
mfd->switch_new_mode); mdss_fb_set_mdp_sync_pt_threshold(mfd,
mfd->switch_new_mode);
mfd->switch_state = MDSS_MDP_WAIT_FOR_COMMIT; mfd->switch_state = MDSS_MDP_WAIT_FOR_COMMIT;
} }
break; break;
@ -3064,6 +3146,21 @@ static int mdss_fb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info) struct fb_info *info)
{ {
struct mdp_display_commit disp_commit; struct mdp_display_commit disp_commit;
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
/*
* during mode switch through mode sysfs node, it will trigger a
* pan_display after switch. This assumes that fb has been adjusted,
* however when using overlays we may not have the right size at this
* point, so it needs to go through PREPARE first. Abort pan_display
* operations until that happens
*/
if (mfd->switch_state != MDSS_MDP_NO_UPDATE_REQUESTED) {
pr_debug("fb%d: pan_display skipped during switch\n",
mfd->index);
return 0;
}
memset(&disp_commit, 0, sizeof(disp_commit)); memset(&disp_commit, 0, sizeof(disp_commit));
disp_commit.wait_for_finish = true; disp_commit.wait_for_finish = true;
memcpy(&disp_commit.var, var, sizeof(struct fb_var_screeninfo)); memcpy(&disp_commit.var, var, sizeof(struct fb_var_screeninfo));
@ -3363,7 +3460,13 @@ static int mdss_fb_check_var(struct fb_var_screeninfo *var,
if (var->yoffset > (var->yres_virtual - var->yres)) if (var->yoffset > (var->yres_virtual - var->yres))
return -EINVAL; return -EINVAL;
if (mfd->panel_info) { if (info->mode) {
const struct fb_videomode *mode;
mode = fb_match_mode(var, &info->modelist);
if (mode == NULL)
return -EINVAL;
} else if (mfd->panel_info && !(var->activate & FB_ACTIVATE_TEST)) {
int rc; int rc;
memcpy(&mfd->reconfig_panel_info, mfd->panel_info, memcpy(&mfd->reconfig_panel_info, mfd->panel_info,
@ -3379,6 +3482,66 @@ static int mdss_fb_check_var(struct fb_var_screeninfo *var,
return 0; return 0;
} }
static int mdss_fb_videomode_switch(struct msm_fb_data_type *mfd,
const struct fb_videomode *mode)
{
int ret = 0;
struct mdss_panel_data *pdata, *tmp;
struct mdss_panel_timing *timing;
pdata = dev_get_platdata(&mfd->pdev->dev);
if (!pdata) {
pr_err("no panel connected\n");
return -ENODEV;
}
/* make sure that we are idle while switching */
mdss_fb_wait_for_kickoff(mfd);
pr_debug("fb%d: changing display mode to %s\n", mfd->index, mode->name);
tmp = pdata;
do {
if (!tmp->event_handler) {
pr_warn("no event handler for panel\n");
continue;
}
timing = mdss_panel_get_timing_by_name(tmp, mode->name);
ret = tmp->event_handler(tmp,
MDSS_EVENT_PANEL_TIMING_SWITCH, timing);
tmp->active = timing != NULL;
tmp = tmp->next;
} while (tmp && !ret);
if (!ret && mfd->mdp.configure_panel) {
int dest_ctrl = 1;
/* todo: currently assumes no changes in video/cmd mode */
if (!mdss_fb_is_power_off(mfd)) {
mutex_lock(&mfd->switch_lock);
mfd->switch_state = MDSS_MDP_WAIT_FOR_PREP;
mfd->switch_new_mode = SWITCH_RESOLUTION;
mutex_unlock(&mfd->switch_lock);
dest_ctrl = 0;
}
ret = mfd->mdp.configure_panel(mfd,
pdata->panel_info.mipi.mode, dest_ctrl);
}
if (!ret) {
if (pdata->next && pdata->next->active)
mfd->split_mode = MDP_DUAL_LM_DUAL_DISPLAY;
else
mfd->split_mode = MDP_SPLIT_MODE_NONE;
mdss_fb_validate_split(0, 0, mfd);
}
pr_debug("fb%d: %s mode change complete\n", mfd->index, mode->name);
return ret;
}
static int mdss_fb_set_par(struct fb_info *info) static int mdss_fb_set_par(struct fb_info *info)
{ {
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
@ -3435,6 +3598,17 @@ static int mdss_fb_set_par(struct fb_info *info)
return -EINVAL; return -EINVAL;
} }
if (info->mode) {
const struct fb_videomode *mode;
mode = fb_match_mode(var, &info->modelist);
if (!mode)
return -EINVAL;
ret = mdss_fb_videomode_switch(mfd, mode);
if (ret)
return ret;
}
if (mfd->mdp.fb_stride) if (mfd->mdp.fb_stride)
mfd->fbi->fix.line_length = mfd->mdp.fb_stride(mfd->index, mfd->fbi->fix.line_length = mfd->mdp.fb_stride(mfd->index,
@ -4261,6 +4435,7 @@ int mdss_register_panel(struct platform_device *pdev,
goto mdss_notfound; goto mdss_notfound;
} }
pdata->active = true;
fb_pdev = of_find_device_by_node(node); fb_pdev = of_find_device_by_node(node);
if (fb_pdev) { if (fb_pdev) {
rc = mdss_fb_register_extra_panel(fb_pdev, pdata); rc = mdss_fb_register_extra_panel(fb_pdev, pdata);

View file

@ -237,6 +237,13 @@ struct mdss_mdp_ctl_intfs_ops {
struct mdss_mdp_ctl *sctl, int new_fps); struct mdss_mdp_ctl *sctl, int new_fps);
int (*restore_fnc)(struct mdss_mdp_ctl *ctl); int (*restore_fnc)(struct mdss_mdp_ctl *ctl);
int (*early_wake_up_fnc)(struct mdss_mdp_ctl *ctl); int (*early_wake_up_fnc)(struct mdss_mdp_ctl *ctl);
/*
* reconfigure interface for new resolution, called before (pre=1)
* and after interface has been reconfigured (pre=0)
*/
int (*reconfigure)(struct mdss_mdp_ctl *ctl,
enum dynamic_switch_modes mode, bool pre);
}; };
struct mdss_mdp_ctl { struct mdss_mdp_ctl {
@ -320,6 +327,7 @@ struct mdss_mdp_ctl {
bool force_ctl_start; bool force_ctl_start;
u64 last_input_time; u64 last_input_time;
int pending_mode_switch;
}; };
struct mdss_mdp_mixer { struct mdss_mdp_mixer {

View file

@ -2595,11 +2595,6 @@ static int mdss_mdp_ctl_fbc_enable(int enable,
fbc = &pdata->fbc; fbc = &pdata->fbc;
if (!fbc || !fbc->enabled) {
pr_err("Invalid FBC structure\n");
return -EINVAL;
}
if (mixer->num == MDSS_MDP_INTF_LAYERMIXER0 || if (mixer->num == MDSS_MDP_INTF_LAYERMIXER0 ||
mixer->num == MDSS_MDP_INTF_LAYERMIXER1) { mixer->num == MDSS_MDP_INTF_LAYERMIXER1) {
pr_debug("Mixer supports FBC.\n"); pr_debug("Mixer supports FBC.\n");
@ -2767,8 +2762,8 @@ int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl)
int mdss_mdp_ctl_reconfig(struct mdss_mdp_ctl *ctl, int mdss_mdp_ctl_reconfig(struct mdss_mdp_ctl *ctl,
struct mdss_panel_data *pdata) struct mdss_panel_data *pdata)
{ {
int (*stop_fnc)(struct mdss_mdp_ctl *ctl, int panel_power_state); void *tmp;
int ret; int ret = 0;
/* /*
* Switch first to prevent deleting important data in the case * Switch first to prevent deleting important data in the case
@ -2780,13 +2775,17 @@ int mdss_mdp_ctl_reconfig(struct mdss_mdp_ctl *ctl,
return -EINVAL; return -EINVAL;
} }
/* if only changing resolution there is no need for intf reconfig */
if (!ctl->is_video_mode == (pdata->panel_info.type == MIPI_CMD_PANEL))
goto skip_intf_reconfig;
/* /*
* Intentionally not clearing stop function, as stop will * Intentionally not clearing stop function, as stop will
* be called after panel is instructed mode switch is happening * be called after panel is instructed mode switch is happening
*/ */
stop_fnc = ctl->ops.stop_fnc; tmp = ctl->ops.stop_fnc;
memset(&ctl->ops, 0, sizeof(ctl->ops)); memset(&ctl->ops, 0, sizeof(ctl->ops));
ctl->ops.stop_fnc = stop_fnc; ctl->ops.stop_fnc = tmp;
switch (pdata->panel_info.type) { switch (pdata->panel_info.type) {
case MIPI_VIDEO_PANEL: case MIPI_VIDEO_PANEL:
@ -2810,6 +2809,16 @@ 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:
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;
}
ctl->border_x_off = pdata->panel_info.lcdc.border_left;
ctl->border_y_off = pdata->panel_info.lcdc.border_top;
return ret; return ret;
} }
@ -3098,7 +3107,7 @@ int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg,
if (pdata->event_handler) if (pdata->event_handler)
rc = pdata->event_handler(pdata, event, arg); rc = pdata->event_handler(pdata, event, arg);
pdata = pdata->next; pdata = pdata->next;
} while (rc == 0 && pdata && !skip_broadcast); } while (rc == 0 && pdata && pdata->active && !skip_broadcast);
return rc; return rc;
} }
@ -3242,7 +3251,7 @@ int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl, bool handoff)
pr_debug("ctl_num=%d, power_state=%d\n", ctl->num, ctl->power_state); pr_debug("ctl_num=%d, power_state=%d\n", ctl->num, ctl->power_state);
if (mdss_mdp_ctl_is_power_on_interactive(ctl) if (mdss_mdp_ctl_is_power_on_interactive(ctl)
&& !(ctl->force_ctl_start)) { && !(ctl->pending_mode_switch)) {
pr_debug("%d: panel already on!\n", __LINE__); pr_debug("%d: panel already on!\n", __LINE__);
return 0; return 0;
} }
@ -3262,7 +3271,7 @@ int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl, bool handoff)
* keep power_on false during handoff to avoid unexpected * keep power_on false during handoff to avoid unexpected
* operations to overlay. * operations to overlay.
*/ */
if (!handoff || ctl->force_ctl_start) if (!handoff || ctl->pending_mode_switch)
ctl->power_state = MDSS_PANEL_POWER_ON; ctl->power_state = MDSS_PANEL_POWER_ON;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
@ -3328,8 +3337,7 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl, int power_state)
if (ctl->ops.stop_fnc) { if (ctl->ops.stop_fnc) {
ret = ctl->ops.stop_fnc(ctl, power_state); ret = ctl->ops.stop_fnc(ctl, power_state);
if (ctl->panel_data->panel_info.fbc.enabled) mdss_mdp_ctl_fbc_enable(0, ctl->mixer_left,
mdss_mdp_ctl_fbc_enable(0, ctl->mixer_left,
&ctl->panel_data->panel_info); &ctl->panel_data->panel_info);
} else { } else {
pr_warn("no stop func for ctl=%d\n", ctl->num); pr_warn("no stop func for ctl=%d\n", ctl->num);
@ -3337,8 +3345,7 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl, int power_state)
if (sctl && sctl->ops.stop_fnc) { if (sctl && sctl->ops.stop_fnc) {
ret = sctl->ops.stop_fnc(sctl, power_state); ret = sctl->ops.stop_fnc(sctl, power_state);
if (ctl->panel_data->panel_info.fbc.enabled) mdss_mdp_ctl_fbc_enable(0, sctl->mixer_left,
mdss_mdp_ctl_fbc_enable(0, sctl->mixer_left,
&sctl->panel_data->panel_info); &sctl->panel_data->panel_info);
} }
if (ret) { if (ret) {
@ -3373,7 +3380,8 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl, int power_state)
end: end:
if (!ret) { if (!ret) {
ctl->power_state = power_state; ctl->power_state = power_state;
mdss_mdp_ctl_perf_update(ctl, 0, true); if (!ctl->pending_mode_switch)
mdss_mdp_ctl_perf_update(ctl, 0, true);
} }
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
@ -4593,7 +4601,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg,
ctl->valid_roi = 0; ctl->valid_roi = 0;
if (ret) if (ret)
pr_warn("error displaying frame\n"); pr_warn("ctl %d error displaying frame\n", ctl->num);
ctl->play_cnt++; ctl->play_cnt++;
ATRACE_END("flush_kickoff"); ATRACE_END("flush_kickoff");

View file

@ -24,8 +24,6 @@
#define MAX_SESSIONS 2 #define MAX_SESSIONS 2
#define SPLIT_MIXER_OFFSET 0x800 #define SPLIT_MIXER_OFFSET 0x800
/* wait for at most 2 vsync for lowest refresh rate (24hz) */
#define KOFF_TIMEOUT msecs_to_jiffies(84)
#define STOP_TIMEOUT(hz) msecs_to_jiffies((1000 / hz) * (6 + 2)) #define STOP_TIMEOUT(hz) msecs_to_jiffies((1000 / hz) * (6 + 2))
#define POWER_COLLAPSE_TIME msecs_to_jiffies(100) #define POWER_COLLAPSE_TIME msecs_to_jiffies(100)
@ -66,7 +64,6 @@ struct mdss_mdp_cmd_ctx {
struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */ struct mdss_mdp_cmd_ctx *sync_ctx; /* for partial update */
u32 pp_timeout_report_cnt; u32 pp_timeout_report_cnt;
int pingpong_split_slave; int pingpong_split_slave;
bool pending_mode_switch; /* Used to prevent powering down in stop */
}; };
struct mdss_mdp_cmd_ctx mdss_mdp_cmd_ctx_list[MAX_SESSIONS]; struct mdss_mdp_cmd_ctx mdss_mdp_cmd_ctx_list[MAX_SESSIONS];
@ -1436,21 +1433,26 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl,
sctx = (struct mdss_mdp_cmd_ctx *) sctl->intf_ctx[MASTER_CTX]; sctx = (struct mdss_mdp_cmd_ctx *) sctl->intf_ctx[MASTER_CTX];
if (!__mdss_mdp_cmd_is_panel_power_on_interactive(ctx)) { if (!__mdss_mdp_cmd_is_panel_power_on_interactive(ctx)) {
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_LINK_READY, NULL, if (ctl->pending_mode_switch != SWITCH_RESOLUTION) {
false); rc = mdss_mdp_ctl_intf_event(ctl,
WARN(rc, "intf %d link ready error (%d)\n", ctl->intf_num, rc); MDSS_EVENT_LINK_READY, NULL, false);
WARN(rc, "intf %d link ready error (%d)\n",
ctl->intf_num, rc);
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL, rc = mdss_mdp_ctl_intf_event(ctl,
false); MDSS_EVENT_UNBLANK, NULL, false);
WARN(rc, "intf %d unblank error (%d)\n", ctl->intf_num, rc); WARN(rc, "intf %d unblank error (%d)\n",
ctl->intf_num, rc);
rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_ON, NULL, rc = mdss_mdp_ctl_intf_event(ctl,
false); MDSS_EVENT_PANEL_ON, NULL, false);
WARN(rc, "intf %d panel on error (%d)\n", ctl->intf_num, rc); WARN(rc, "intf %d panel on error (%d)\n",
ctl->intf_num, rc);
rc = mdss_mdp_tearcheck_enable(ctl, true); rc = mdss_mdp_tearcheck_enable(ctl, true);
WARN(rc, "intf %d tearcheck enable error (%d)\n", WARN(rc, "intf %d tearcheck enable error (%d)\n",
ctl->intf_num, rc); ctl->intf_num, rc);
}
ctx->panel_power_state = MDSS_PANEL_POWER_ON; ctx->panel_power_state = MDSS_PANEL_POWER_ON;
if (sctx) if (sctx)
@ -1698,8 +1700,7 @@ int mdss_mdp_cmd_restore(struct mdss_mdp_ctl *ctl)
} }
int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl, int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_cmd_ctx *ctx, int panel_power_state, struct mdss_mdp_cmd_ctx *ctx, int panel_power_state)
bool pend_switch)
{ {
struct mdss_mdp_cmd_ctx *sctx = NULL; struct mdss_mdp_cmd_ctx *sctx = NULL;
struct mdss_mdp_ctl *sctl = NULL; struct mdss_mdp_ctl *sctl = NULL;
@ -1724,7 +1725,7 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
; ;
} }
if (!pend_switch) { if (!ctl->pending_mode_switch) {
mdss_mdp_ctl_intf_event(ctl, mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_REGISTER_RECOVERY_HANDLER, MDSS_EVENT_REGISTER_RECOVERY_HANDLER,
NULL, NULL,
@ -1753,7 +1754,7 @@ int mdss_mdp_cmd_ctx_stop(struct mdss_mdp_ctl *ctl,
} }
int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session,
int panel_power_state, bool pend_switch) int panel_power_state)
{ {
struct mdss_mdp_cmd_ctx *ctx; struct mdss_mdp_cmd_ctx *ctx;
@ -1766,7 +1767,7 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session,
return -ENODEV; return -ENODEV;
} }
mdss_mdp_cmd_ctx_stop(ctl, ctx, panel_power_state, pend_switch); mdss_mdp_cmd_ctx_stop(ctl, ctx, panel_power_state);
if (is_pingpong_split(ctl->mfd)) { if (is_pingpong_split(ctl->mfd)) {
session += 1; session += 1;
@ -1779,14 +1780,14 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session,
pr_err("invalid ctx session: %d\n", session); pr_err("invalid ctx session: %d\n", session);
return -ENODEV; return -ENODEV;
} }
mdss_mdp_cmd_ctx_stop(ctl, ctx, panel_power_state, pend_switch); mdss_mdp_cmd_ctx_stop(ctl, ctx, panel_power_state);
} }
pr_debug("%s:-\n", __func__); pr_debug("%s:-\n", __func__);
return 0; return 0;
} }
static int mdss_mdp_cmd_stop_sub(struct mdss_mdp_ctl *ctl, static int mdss_mdp_cmd_stop_sub(struct mdss_mdp_ctl *ctl,
int panel_power_state, bool pend_switch) int panel_power_state)
{ {
struct mdss_mdp_cmd_ctx *ctx; struct mdss_mdp_cmd_ctx *ctx;
struct mdss_mdp_vsync_handler *tmp, *handle; struct mdss_mdp_vsync_handler *tmp, *handle;
@ -1804,8 +1805,7 @@ static int mdss_mdp_cmd_stop_sub(struct mdss_mdp_ctl *ctl,
/* Command mode is supported only starting at INTF1 */ /* Command mode is supported only starting at INTF1 */
session = ctl->intf_num - MDSS_MDP_INTF1; session = ctl->intf_num - MDSS_MDP_INTF1;
return mdss_mdp_cmd_intfs_stop(ctl, session, panel_power_state, return mdss_mdp_cmd_intfs_stop(ctl, session, panel_power_state);
pend_switch);
} }
int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
@ -1815,7 +1815,6 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
bool panel_off = false; bool panel_off = false;
bool turn_off_clocks = false; bool turn_off_clocks = false;
bool send_panel_events = false; bool send_panel_events = false;
bool pend_switch = false;
int ret = 0; int ret = 0;
if (!ctx) { if (!ctx) {
@ -1901,14 +1900,11 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
if (!turn_off_clocks) if (!turn_off_clocks)
goto panel_events; goto panel_events;
if (ctx->pending_mode_switch) { if (ctl->pending_mode_switch)
pend_switch = true;
send_panel_events = false; send_panel_events = false;
ctx->pending_mode_switch = 0;
}
pr_debug("%s: turn off interface clocks\n", __func__); pr_debug("%s: turn off interface clocks\n", __func__);
ret = mdss_mdp_cmd_stop_sub(ctl, panel_power_state, pend_switch); ret = mdss_mdp_cmd_stop_sub(ctl, panel_power_state);
if (IS_ERR_VALUE(ret)) { if (IS_ERR_VALUE(ret)) {
pr_err("%s: unable to stop interface: %d\n", pr_err("%s: unable to stop interface: %d\n",
__func__, ret); __func__, ret);
@ -1916,7 +1912,7 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state)
} }
if (sctl) { if (sctl) {
mdss_mdp_cmd_stop_sub(sctl, panel_power_state, pend_switch); mdss_mdp_cmd_stop_sub(sctl, panel_power_state);
if (IS_ERR_VALUE(ret)) { if (IS_ERR_VALUE(ret)) {
pr_err("%s: unable to stop slave intf: %d\n", pr_err("%s: unable to stop slave intf: %d\n",
__func__, ret); __func__, ret);
@ -1951,6 +1947,7 @@ panel_events:
ctl->ops.wait_pingpong = NULL; ctl->ops.wait_pingpong = NULL;
ctl->ops.add_vsync_handler = NULL; ctl->ops.add_vsync_handler = NULL;
ctl->ops.remove_vsync_handler = NULL; ctl->ops.remove_vsync_handler = NULL;
ctl->ops.reconfigure = NULL;
end: end:
if (!IS_ERR_VALUE(ret)) if (!IS_ERR_VALUE(ret))
@ -2165,7 +2162,6 @@ void mdss_mdp_switch_roi_reset(struct mdss_mdp_ctl *ctl)
void mdss_mdp_switch_to_vid_mode(struct mdss_mdp_ctl *ctl, int prep) void mdss_mdp_switch_to_vid_mode(struct mdss_mdp_ctl *ctl, int prep)
{ {
struct mdss_mdp_cmd_ctx *ctx = ctl->intf_ctx[MASTER_CTX];
long int mode = MIPI_VIDEO_PANEL; long int mode = MIPI_VIDEO_PANEL;
int rc = 0; int rc = 0;
@ -2180,7 +2176,6 @@ void mdss_mdp_switch_to_vid_mode(struct mdss_mdp_ctl *ctl, int prep)
rc = mdss_mdp_ctl_intf_event rc = mdss_mdp_ctl_intf_event
(ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1, false); (ctl, MDSS_EVENT_PANEL_CLK_CTRL, (void *)1, false);
ctx->pending_mode_switch = 1;
return; return;
} }
@ -2188,6 +2183,56 @@ void mdss_mdp_switch_to_vid_mode(struct mdss_mdp_ctl *ctl, int prep)
(void *) mode, false); (void *) mode, false);
} }
static int mdss_mdp_cmd_reconfigure(struct mdss_mdp_ctl *ctl,
enum dynamic_switch_modes mode, bool prep)
{
int ret, rc = 0;
if (mdss_mdp_ctl_is_power_off(ctl))
return 0;
pr_debug("%s: ctl=%d mode=%d prep=%d\n", __func__,
ctl->num, mode, prep);
if (mode == SWITCH_TO_VIDEO_MODE) {
mdss_mdp_switch_to_vid_mode(ctl, prep);
} else if (mode == SWITCH_RESOLUTION) {
if (prep) {
/* make sure any pending transfer is finished */
ret = mdss_mdp_cmd_wait4pingpong(ctl, NULL);
if (ret)
return ret;
/*
* keep a ref count on clocks to prevent them from
* being disabled while switch happens
*/
mdss_bus_bandwidth_ctrl(true);
rc = mdss_iommu_ctrl(1);
if (IS_ERR_VALUE(rc))
pr_err("IOMMU attach failed\n");
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON);
mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_CLK_CTRL,
(void *)1, false);
mdss_mdp_ctl_stop(ctl, MDSS_PANEL_POWER_OFF);
mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_DSI_DYNAMIC_SWITCH,
(void *) mode, false);
} else {
/* release ref count after switch is complete */
mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_CLK_CTRL,
(void *)0, false);
mdss_iommu_ctrl(0);
mdss_bus_bandwidth_ctrl(false);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF);
}
}
return 0;
}
int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl) int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl)
{ {
int ret, session = 0; int ret, session = 0;
@ -2210,6 +2255,7 @@ int mdss_mdp_cmd_start(struct mdss_mdp_ctl *ctl)
ctl->ops.read_line_cnt_fnc = mdss_mdp_cmd_line_count; ctl->ops.read_line_cnt_fnc = mdss_mdp_cmd_line_count;
ctl->ops.restore_fnc = mdss_mdp_cmd_restore; ctl->ops.restore_fnc = mdss_mdp_cmd_restore;
ctl->ops.early_wake_up_fnc = mdss_mdp_cmd_early_wake_up; ctl->ops.early_wake_up_fnc = mdss_mdp_cmd_early_wake_up;
ctl->ops.reconfigure = mdss_mdp_cmd_reconfigure;
pr_debug("%s:-\n", __func__); pr_debug("%s:-\n", __func__);
return 0; return 0;

View file

@ -1652,12 +1652,32 @@ int mdss_mode_switch(struct msm_fb_data_type *mfd, u32 mode)
{ {
struct mdss_rect l_roi, r_roi; struct mdss_rect l_roi, r_roi;
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
struct mdss_mdp_ctl *sctl;
int rc; int rc;
pr_debug("%s, start\n", __func__); pr_debug("fb%d switch to mode=%x\n", mfd->index, mode);
ATRACE_FUNC();
ctl->pending_mode_switch = mode;
sctl = mdss_mdp_get_split_ctl(ctl);
if (sctl)
sctl->pending_mode_switch = mode;
/* No need for mode validation. It has been done in ioctl call */ /* No need for mode validation. It has been done in ioctl call */
if (mode == MIPI_CMD_PANEL) { if (mode == SWITCH_RESOLUTION) {
if (ctl->ops.reconfigure) {
rc = ctl->ops.reconfigure(ctl, mode, 1);
if (rc)
return rc;
/*
* For Video mode panels, reconfigure is not defined.
* So doing an explicit ctrl stop during resolution switch
* to balance the ctrl start at the end of this function.
*/
} else {
mdss_mdp_ctl_stop(ctl, MDSS_PANEL_POWER_OFF);
}
} else if (mode == MIPI_CMD_PANEL) {
/* /*
* Need to reset roi if there was partial update in previous * Need to reset roi if there was partial update in previous
* Command frame * Command frame
@ -1689,17 +1709,16 @@ int mdss_mode_switch(struct msm_fb_data_type *mfd, u32 mode)
return -EINVAL; return -EINVAL;
} }
ctl->force_ctl_start = 1;
mdss_mdp_ctl_start(ctl, true); mdss_mdp_ctl_start(ctl, true);
ctl->force_ctl_start = 0; ATRACE_END(__func__);
pr_debug("%s, end\n", __func__);
return 0; return 0;
} }
int mdss_mode_switch_post(struct msm_fb_data_type *mfd, u32 mode) int mdss_mode_switch_post(struct msm_fb_data_type *mfd, u32 mode)
{ {
struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd); struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
struct mdss_mdp_ctl *sctl;
int rc = 0; int rc = 0;
u32 frame_rate = 0; u32 frame_rate = 0;
@ -1730,7 +1749,15 @@ int mdss_mode_switch_post(struct msm_fb_data_type *mfd, u32 mode)
mdss_mdp_ctl_intf_event(ctl, mdss_mdp_ctl_intf_event(ctl,
MDSS_EVENT_PANEL_CLK_CTRL, (void *)0, MDSS_EVENT_PANEL_CLK_CTRL, (void *)0,
false); false);
} else if (mode == SWITCH_RESOLUTION) {
if (ctl->ops.reconfigure)
rc = ctl->ops.reconfigure(ctl, mode, 0);
} }
ctl->pending_mode_switch = 0;
sctl = mdss_mdp_get_split_ctl(ctl);
if (sctl)
sctl->pending_mode_switch = 0;
return rc; return rc;
} }
@ -4804,18 +4831,42 @@ static int mdss_mdp_update_panel_info(struct msm_fb_data_type *mfd,
mdss_mdp_ctl_destroy(mdp5_data->ctl); mdss_mdp_ctl_destroy(mdp5_data->ctl);
mdp5_data->ctl = NULL; mdp5_data->ctl = NULL;
} else { } else {
if (is_panel_split(mfd) && mdp5_data->mdata->has_pingpong_split)
mfd->split_mode = MDP_PINGPONG_SPLIT;
/* /*
* Dynamic change so we need to reconfig instead of * Dynamic change so we need to reconfig instead of
* destroying current ctrl sturcture. * destroying current ctrl sturcture.
*/ */
pdata = dev_get_platdata(&mfd->pdev->dev); pdata = dev_get_platdata(&mfd->pdev->dev);
mdss_mdp_ctl_reconfig(ctl, pdata); mdss_mdp_ctl_reconfig(ctl, pdata);
sctl = mdss_mdp_get_split_ctl(ctl); sctl = mdss_mdp_get_split_ctl(ctl);
if (sctl) if (sctl) {
mdss_mdp_ctl_reconfig(sctl, pdata->next); if (mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY) {
mdss_mdp_ctl_reconfig(sctl, pdata->next);
sctl->border_x_off +=
pdata->panel_info.lcdc.border_left +
pdata->panel_info.lcdc.border_right;
} else {
/*
* todo: need to revisit this and properly
* cleanup slave resources
*/
mdss_mdp_ctl_destroy(sctl);
ctl->mixer_right = NULL;
}
} else if (mfd->split_mode == MDP_DUAL_LM_DUAL_DISPLAY) {
/* enable split display for the first time */
ret = mdss_mdp_ctl_split_display_setup(ctl,
pdata->next);
if (ret) {
mdss_mdp_ctl_destroy(ctl);
mdp5_data->ctl = NULL;
}
}
} }
return 0; return ret;
} }
int mdss_mdp_input_event_handler(struct msm_fb_data_type *mfd) int mdss_mdp_input_event_handler(struct msm_fb_data_type *mfd)

View file

@ -575,3 +575,61 @@ void mdss_panel_debugfsinfo_to_panelinfo(struct mdss_panel_info *panel_info)
pdata = pdata->next; pdata = pdata->next;
} while (pdata); } while (pdata);
} }
struct mdss_panel_timing *mdss_panel_get_timing_by_name(
struct mdss_panel_data *pdata,
const char *name)
{
struct mdss_panel_timing *pt;
if (pdata && name) {
list_for_each_entry(pt, &pdata->timings_list, list)
if (pt->name && !strcmp(pt->name, name))
return pt;
}
return NULL;
}
void mdss_panel_info_from_timing(struct mdss_panel_timing *pt,
struct mdss_panel_info *pinfo)
{
if (!pt || !pinfo)
return;
pinfo->clk_rate = pt->clk_rate;
pinfo->xres = pt->xres;
pinfo->lcdc.h_front_porch = pt->h_front_porch;
pinfo->lcdc.h_back_porch = pt->h_back_porch;
pinfo->lcdc.h_pulse_width = pt->h_pulse_width;
pinfo->yres = pt->yres;
pinfo->lcdc.v_front_porch = pt->v_front_porch;
pinfo->lcdc.v_back_porch = pt->v_back_porch;
pinfo->lcdc.v_pulse_width = pt->v_pulse_width;
pinfo->lcdc.border_bottom = pt->border_bottom;
pinfo->lcdc.border_top = pt->border_top;
pinfo->lcdc.border_left = pt->border_left;
pinfo->lcdc.border_right = pt->border_right;
pinfo->lcdc.xres_pad = pt->border_left + pt->border_right;
pinfo->lcdc.yres_pad = pt->border_top + pt->border_bottom;
pinfo->lm_widths[0] = pt->lm_widths[0];
pinfo->lm_widths[1] = pt->lm_widths[1];
pinfo->mipi.frame_rate = pt->frame_rate;
pinfo->edp.frame_rate = pinfo->mipi.frame_rate;
pinfo->dsc = pt->dsc;
pinfo->dsc_enc_total = pt->dsc_enc_total;
pinfo->fbc = pt->fbc;
pinfo->compression_mode = pt->compression_mode;
pinfo->te = pt->te;
/* override te parameters if panel is in sw te mode */
if (pinfo->sim_panel_mode == SIM_SW_TE_MODE)
mdss_panel_override_te_params(pinfo);
}

View file

@ -204,7 +204,7 @@ struct mdss_intf_recovery {
* - 1: update to command mode * - 1: update to command mode
* @MDSS_EVENT_REGISTER_RECOVERY_HANDLER: Event to recover the interface in * @MDSS_EVENT_REGISTER_RECOVERY_HANDLER: Event to recover the interface in
* case there was any errors detected. * case there was any errors detected.
* @ MDSS_EVENT_DSI_PANEL_STATUS:Event to check the panel status * @MDSS_EVENT_DSI_PANEL_STATUS: Event to check the panel status
* <= 0: panel check fail * <= 0: panel check fail
* > 0: panel check success * > 0: panel check success
* @MDSS_EVENT_DSI_DYNAMIC_SWITCH: Send DCS command to panel to initiate * @MDSS_EVENT_DSI_DYNAMIC_SWITCH: Send DCS command to panel to initiate
@ -216,6 +216,8 @@ struct mdss_intf_recovery {
* - MIPI_CMD_PANEL: switch to command mode * - MIPI_CMD_PANEL: switch to command mode
* @MDSS_EVENT_DSI_RESET_WRITE_PTR: Reset the write pointer coordinates on * @MDSS_EVENT_DSI_RESET_WRITE_PTR: Reset the write pointer coordinates on
* the panel. * the panel.
* @MDSS_EVENT_PANEL_TIMING_SWITCH: Panel timing switch is requested.
* Argument provided is new panel timing.
*/ */
enum mdss_intf_events { enum mdss_intf_events {
MDSS_EVENT_RESET = 1, MDSS_EVENT_RESET = 1,
@ -243,6 +245,7 @@ enum mdss_intf_events {
MDSS_EVENT_DSI_DYNAMIC_SWITCH, MDSS_EVENT_DSI_DYNAMIC_SWITCH,
MDSS_EVENT_DSI_RECONFIG_CMD, MDSS_EVENT_DSI_RECONFIG_CMD,
MDSS_EVENT_DSI_RESET_WRITE_PTR, MDSS_EVENT_DSI_RESET_WRITE_PTR,
MDSS_EVENT_PANEL_TIMING_SWITCH,
}; };
struct lcd_panel_info { struct lcd_panel_info {
@ -283,12 +286,57 @@ struct mdss_dsi_phy_ctrl {
char lanecfg_len; char lanecfg_len;
}; };
/**
* enum dynamic_mode_switch - Dynamic mode switch methods
* @DYNAMIC_MODE_SWITCH_DISABLED: Dynamic mode switch is not supported
* @DYNAMIC_MODE_SWITCH_SUSPEND_RESUME: Switch requires panel suspend/resume
* @DYNAMIC_MODE_SWITCH_IMMEDIATE: Supports video/cmd mode switch immediately
* @DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE: Panel supports display resolution
* switch immediately.
**/
enum dynamic_mode_switch { enum dynamic_mode_switch {
DYNAMIC_MODE_SWITCH_DISABLED = 0, DYNAMIC_MODE_SWITCH_DISABLED = 0,
DYNAMIC_MODE_SWITCH_SUSPEND_RESUME, DYNAMIC_MODE_SWITCH_SUSPEND_RESUME,
DYNAMIC_MODE_SWITCH_IMMEDIATE, DYNAMIC_MODE_SWITCH_IMMEDIATE,
DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE,
}; };
/**
* enum dynamic_switch_modes - Type of dynamic mode switch to be given as
* argument to MDSS_EVENT_DSI_DYNAMIC_SWITCH event
* @SWITCH_TO_CMD_MODE: Switch from DSI video mode to command mode
* @SWITCH_TO_VIDEO_MODE: Switch from DSI command mode to video mode
* @SWITCH_RESOLUTION: Switch only display resolution
**/
enum dynamic_switch_modes {
SWITCH_MODE_UNKNOWN = 0,
SWITCH_TO_CMD_MODE,
SWITCH_TO_VIDEO_MODE,
SWITCH_RESOLUTION,
};
/**
* struct mdss_panel_timing - structure for panel timing information
* @list: List head ptr to track within panel data timings list
* @name: A unique name of this timing that can be used to identify it
* @xres: Panel width
* @yres: Panel height
* @h_back_porch: Horizontal back porch
* @h_front_porch: Horizontal front porch
* @h_pulse_width: Horizontal pulse width
* @hsync_skew: Horizontal sync skew
* @v_back_porch: Vertical back porch
* @v_front_porch: Vertical front porch
* @v_pulse_width: Vertical pulse width
* @border_top: Border color on top
* @border_bottom: Border color on bottom
* @border_left: Border color on left
* @border_right: Border color on right
* @clk_rate: Pixel clock rate of this panel timing
* @frame_rate: Display refresh rate
* @fbc: Framebuffer compression parameters for this display timing
* @te: Tearcheck parameters for this display timing
**/
struct mipi_panel_info { struct mipi_panel_info {
char boot_mode; /* identify if mode switched from starting mode */ char boot_mode; /* identify if mode switched from starting mode */
char mode; /* video/cmd */ char mode; /* video/cmd */
@ -585,6 +633,39 @@ struct mdss_panel_info {
struct mdss_panel_debugfs_info *debugfs_info; struct mdss_panel_debugfs_info *debugfs_info;
}; };
struct mdss_panel_timing {
struct list_head list;
const char *name;
u32 xres;
u32 yres;
u32 h_back_porch;
u32 h_front_porch;
u32 h_pulse_width;
u32 hsync_skew;
u32 v_back_porch;
u32 v_front_porch;
u32 v_pulse_width;
u32 border_top;
u32 border_bottom;
u32 border_left;
u32 border_right;
u32 lm_widths[2];
u32 clk_rate;
char frame_rate;
u8 dsc_enc_total;
struct dsc_desc dsc;
struct fbc_panel_info fbc;
u32 compression_mode;
struct mdss_mdp_pp_tear_check te;
};
struct mdss_panel_data { struct mdss_panel_data {
struct mdss_panel_info panel_info; struct mdss_panel_info panel_info;
void (*set_backlight) (struct mdss_panel_data *pdata, u32 bl_level); void (*set_backlight) (struct mdss_panel_data *pdata, u32 bl_level);
@ -604,6 +685,10 @@ struct mdss_panel_data {
*/ */
int (*event_handler) (struct mdss_panel_data *pdata, int e, void *arg); int (*event_handler) (struct mdss_panel_data *pdata, int e, void *arg);
struct list_head timings_list;
struct mdss_panel_timing *current_timing;
bool active;
struct mdss_panel_data *next; struct mdss_panel_data *next;
}; };
@ -826,6 +911,28 @@ int mdss_panel_debugfs_init(struct mdss_panel_info *panel_info,
char const *panel_name); char const *panel_name);
void mdss_panel_debugfs_cleanup(struct mdss_panel_info *panel_info); void mdss_panel_debugfs_cleanup(struct mdss_panel_info *panel_info);
void mdss_panel_debugfsinfo_to_panelinfo(struct mdss_panel_info *panel_info); void mdss_panel_debugfsinfo_to_panelinfo(struct mdss_panel_info *panel_info);
/*
* mdss_panel_info_from_timing() - populate panel info from panel timing
* @pt: pointer to source panel timing
* @pinfo: pointer to destination panel info
*
* Populates relevant data from panel timing into panel info
*/
void mdss_panel_info_from_timing(struct mdss_panel_timing *pt,
struct mdss_panel_info *pinfo);
/**
* mdss_panel_get_timing_by_name() - return panel timing with matching name
* @pdata: pointer to panel data struct containing list of panel timings
* @name: name of the panel timing to be returned
*
* Looks through list of timings provided in panel data and returns pointer
* to panel timing matching it. If none is found, NULL is returned.
*/
struct mdss_panel_timing *mdss_panel_get_timing_by_name(
struct mdss_panel_data *pdata,
const char *name);
#else #else
static inline int mdss_panel_debugfs_init( static inline int mdss_panel_debugfs_init(
struct mdss_panel_info *panel_info) { return 0; }; struct mdss_panel_info *panel_info) { return 0; };
@ -833,5 +940,10 @@ static inline void mdss_panel_debugfs_cleanup(
struct mdss_panel_info *panel_info) { }; struct mdss_panel_info *panel_info) { };
static inline void mdss_panel_debugfsinfo_to_panelinfo( static inline void mdss_panel_debugfsinfo_to_panelinfo(
struct mdss_panel_info *panel_info) { }; struct mdss_panel_info *panel_info) { };
static inline void mdss_panel_info_from_timing(struct mdss_panel_timing *pt,
struct mdss_panel_info *pinfo) { };
static inline struct mdss_panel_timing *mdss_panel_get_timing_by_name(
struct mdss_panel_data *pdata,
const char *name) { return NULL; };
#endif #endif
#endif /* MDSS_PANEL_H */ #endif /* MDSS_PANEL_H */