diff options
| author | Chandan Uddaraju <chandanu@codeaurora.org> | 2013-01-24 14:33:19 -0800 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:14:21 -0700 |
| commit | 0a547f9587bf86c4018ad4055592f0d9d77e56bf (patch) | |
| tree | 188aeac05b306121f2ab88c6e91b196296564ace | |
| parent | 2b0b211563d29d53c6f53831c9fe81b300f7a55b (diff) | |
msm: mdss: enable split display in mdp
Allow a second panel to be registered to a single frame buffer, the
image from frame buffer would then be displayed across the two panels.
Change-Id: I2bc78741ed8408bedfa563cd312c4e628045bfae
Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
Signed-off-by: Chandan Uddaraju <chandanu@codeaurora.org>
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_fb.c | 46 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_fb.h | 3 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.h | 2 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_ctl.c | 266 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_hwio.h | 4 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_overlay.c | 11 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_panel.h | 2 |
7 files changed, 273 insertions, 61 deletions
diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index dc983bbacb62..c18206b8ff3d 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -261,6 +261,8 @@ static int mdss_fb_probe(struct platform_device *pdev) mfd->fb_imgType = MDP_RGBA_8888; mfd->pdev = pdev; + if (pdata->next) + mfd->split_display = true; mutex_init(&mfd->lock); @@ -886,13 +888,15 @@ static int mdss_fb_register(struct msm_fb_data_type *mfd) return ret; } + var->xres = panel_info->xres; + if (mfd->split_display) + var->xres *= 2; + fix->type = panel_info->is_3d_panel; - fix->line_length = mdss_fb_line_length(mfd->index, panel_info->xres, - bpp); + fix->line_length = mdss_fb_line_length(mfd->index, var->xres, bpp); - var->xres = panel_info->xres; var->yres = panel_info->yres; - var->xres_virtual = panel_info->xres; + var->xres_virtual = var->xres; var->yres_virtual = panel_info->yres * mfd->fb_page; var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */ var->upper_margin = panel_info->lcdc.v_back_porch; @@ -1735,6 +1739,36 @@ struct fb_info *msm_fb_get_writeback_fb(void) } EXPORT_SYMBOL(msm_fb_get_writeback_fb); +static int mdss_fb_register_extra_panel(struct platform_device *pdev, + struct mdss_panel_data *pdata) +{ + struct mdss_panel_data *fb_pdata; + + fb_pdata = dev_get_platdata(&pdev->dev); + if (!fb_pdata) { + pr_err("framebuffer device %s contains invalid panel data\n", + dev_name(&pdev->dev)); + return -EINVAL; + } + + if (fb_pdata->next) { + pr_err("split panel already setup for framebuffer device %s\n", + dev_name(&pdev->dev)); + return -EEXIST; + } + + if ((fb_pdata->panel_info.type != MIPI_VIDEO_PANEL) || + (pdata->panel_info.type != MIPI_VIDEO_PANEL)) { + pr_err("Split panel not supported for panel type %d\n", + pdata->panel_info.type); + return -EINVAL; + } + + fb_pdata->next = pdata; + + return 0; +} + int mdss_register_panel(struct platform_device *pdev, struct mdss_panel_data *pdata) { @@ -1762,9 +1796,7 @@ int mdss_register_panel(struct platform_device *pdev, fb_pdev = of_find_device_by_node(node); if (fb_pdev) { - pr_warn("frame buffer device already exists for %s\n", - node->full_name); - rc = -EPERM; + rc = mdss_fb_register_extra_panel(fb_pdev, pdata); } else { pr_info("adding framebuffer device %s\n", dev_name(&pdev->dev)); fb_pdev = of_platform_device_create(node, NULL, diff --git a/drivers/video/fbdev/msm/mdss_fb.h b/drivers/video/fbdev/msm/mdss_fb.h index a3b1ac323ddf..8ad8665fdcfb 100644 --- a/drivers/video/fbdev/msm/mdss_fb.h +++ b/drivers/video/fbdev/msm/mdss_fb.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved. +/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -60,6 +60,7 @@ struct msm_fb_data_type { struct panel_id panel; struct mdss_panel_info *panel_info; + int split_display; u32 dest; struct fb_info *fbi; diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 6bf691483b1e..4a2bec98d07c 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -293,6 +293,8 @@ int mdss_mdp_overlay_kickoff(struct mdss_mdp_ctl *ctl); struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata, struct msm_fb_data_type *mfd); +int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl, + struct mdss_panel_data *pdata); int mdss_mdp_ctl_destroy(struct mdss_mdp_ctl *ctl); int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl); int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 81bec4d8bb0b..be7ee4ebd67e 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -386,20 +386,45 @@ int mdss_mdp_wb_mixer_destroy(struct mdss_mdp_mixer *mixer) return 0; } +static inline int mdss_mdp_set_split_ctl(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_ctl *split_ctl) +{ + if (!ctl || !split_ctl) + return -ENODEV; + + /* setup split ctl mixer as right mixer of original ctl so that + * original ctl can work the same way as dual pipe solution */ + ctl->mixer_right = split_ctl->mixer_left; + + return 0; +} + +static inline struct mdss_mdp_ctl *mdss_mdp_get_split_ctl( + struct mdss_mdp_ctl *ctl) +{ + if (ctl && ctl->mixer_right && (ctl->mixer_right->ctl != ctl)) + return ctl->mixer_right->ctl; + + return NULL; +} + static int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) { + struct mdss_mdp_ctl *split_ctl; u32 width, height; - int ret; if (!ctl || !ctl->panel_data) { pr_err("invalid ctl handle\n"); return -ENODEV; } + split_ctl = mdss_mdp_get_split_ctl(ctl); + width = ctl->panel_data->panel_info.xres; height = ctl->panel_data->panel_info.yres; - if (width > (2 * MAX_MIXER_WIDTH)) { + if ((split_ctl && (width > MAX_MIXER_WIDTH)) || + (width > (2 * MAX_MIXER_WIDTH))) { pr_err("Unsupported panel resolution: %dx%d\n", width, height); return -ENOTSUPP; } @@ -412,8 +437,7 @@ static int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_INTF); if (!ctl->mixer_left) { pr_err("unable to allocate layer mixer\n"); - ret = -ENOMEM; - goto setup_fail; + return -ENOMEM; } } @@ -424,14 +448,20 @@ static int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) ctl->mixer_left->height = height; ctl->mixer_left->ctl = ctl; + if (split_ctl) { + pr_debug("split display detected\n"); + return 0; + } + if (width < ctl->width) { if (ctl->mixer_right == NULL) { ctl->mixer_right = mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_INTF); if (!ctl->mixer_right) { pr_err("unable to allocate right mixer\n"); - ret = -ENOMEM; - goto setup_fail; + if (ctl->mixer_left) + mdss_mdp_mixer_free(ctl->mixer_left); + return -ENOMEM; } } ctl->mixer_right->width = width; @@ -441,7 +471,6 @@ static int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) mdss_mdp_mixer_free(ctl->mixer_right); } - if (ctl->mixer_right) { ctl->opmode |= MDSS_MDP_CTL_OP_PACK_3D_ENABLE | MDSS_MDP_CTL_OP_PACK_3D_H_ROW_INT; @@ -451,12 +480,6 @@ static int mdss_mdp_ctl_setup(struct mdss_mdp_ctl *ctl) } return 0; -setup_fail: - if (ctl->mixer_left) - mdss_mdp_mixer_free(ctl->mixer_left); - if (ctl->mixer_right) - mdss_mdp_mixer_free(ctl->mixer_right); - return ret; } struct mdss_mdp_ctl *mdss_mdp_ctl_init(struct mdss_panel_data *pdata, @@ -539,17 +562,109 @@ ctl_init_fail: return ERR_PTR(ret); } +int mdss_mdp_ctl_split_display_setup(struct mdss_mdp_ctl *ctl, + struct mdss_panel_data *pdata) +{ + struct mdss_mdp_ctl *sctl; + struct mdss_mdp_mixer *mixer; + + if (!ctl || !pdata) + return -ENODEV; + + if (pdata->panel_info.xres > MAX_MIXER_WIDTH) { + pr_err("Unsupported second panel resolution: %dx%d\n", + pdata->panel_info.xres, pdata->panel_info.yres); + return -ENOTSUPP; + } + + if (ctl->mixer_right) { + pr_err("right mixer already setup for ctl=%d\n", ctl->num); + return -EPERM; + } + + sctl = mdss_mdp_ctl_init(pdata, ctl->mfd); + if (!sctl) { + pr_err("unable to setup split display\n"); + return -ENODEV; + } + + sctl->width = pdata->panel_info.xres; + sctl->height = pdata->panel_info.yres; + + ctl->mixer_left = mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_INTF); + if (!ctl->mixer_left) { + pr_err("unable to allocate layer mixer\n"); + mdss_mdp_ctl_destroy(sctl); + return -ENOMEM; + } + ctl->mixer_left->ctl = ctl; + + mixer = mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_INTF); + if (!mixer) { + pr_err("unable to allocate layer mixer\n"); + mdss_mdp_ctl_destroy(sctl); + return -ENOMEM; + } + + mixer->width = sctl->width; + mixer->height = sctl->height; + mixer->ctl = sctl; + sctl->mixer_left = mixer; + + return mdss_mdp_set_split_ctl(ctl, sctl); +} + +static void mdss_mdp_ctl_split_display_enable(int enable, + struct mdss_mdp_ctl *main_ctl, struct mdss_mdp_ctl *slave_ctl) +{ + u32 upper = 0, lower = 0; + + pr_debug("split main ctl=%d intf=%d slave ctl=%d intf=%d\n", + main_ctl->num, main_ctl->intf_num, + slave_ctl->num, slave_ctl->intf_num); + if (enable) { + if (main_ctl->opmode & MDSS_MDP_CTL_OP_CMD_MODE) { + upper |= BIT(1); + lower |= BIT(1); + + /* interface controlling sw trigger */ + if (main_ctl->intf_num == MDSS_MDP_INTF2) + upper |= BIT(4); + else + upper |= BIT(8); + } else { /* video mode */ + if (main_ctl->intf_num == MDSS_MDP_INTF2) + lower |= BIT(4); + else + lower |= BIT(8); + } + } + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SPLIT_DISPLAY_UPPER_PIPE_CTRL, upper); + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SPLIT_DISPLAY_LOWER_PIPE_CTRL, lower); + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SPLIT_DISPLAY_EN, enable); +} + + int mdss_mdp_ctl_destroy(struct mdss_mdp_ctl *ctl) { + struct mdss_mdp_ctl *sctl; int rc; rc = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_CLOSE, NULL); WARN(rc, "unable to close panel for intf=%d\n", ctl->intf_num); + sctl = mdss_mdp_get_split_ctl(ctl); + if (sctl) { + pr_debug("destroying split display ctl=%d\n", sctl->num); + if (sctl->mixer_left) + mdss_mdp_mixer_free(sctl->mixer_left); + mdss_mdp_ctl_free(sctl); + } else if (ctl->mixer_right) { + mdss_mdp_mixer_free(ctl->mixer_right); + } + if (ctl->mixer_left) mdss_mdp_mixer_free(ctl->mixer_left); - if (ctl->mixer_right) - mdss_mdp_mixer_free(ctl->mixer_right); mdss_mdp_ctl_free(ctl); return 0; @@ -558,6 +673,8 @@ int mdss_mdp_ctl_destroy(struct mdss_mdp_ctl *ctl) int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg) { struct mdss_panel_data *pdata; + int rc = 0; + if (!ctl || !ctl->panel_data) return -ENODEV; @@ -565,41 +682,21 @@ int mdss_mdp_ctl_intf_event(struct mdss_mdp_ctl *ctl, int event, void *arg) pr_debug("sending ctl=%d event=%d\n", ctl->num, event); - if (pdata->event_handler) - return pdata->event_handler(pdata, event, arg); + do { + if (pdata->event_handler) + rc = pdata->event_handler(pdata, event, arg); + pdata = pdata->next; + } while (rc == 0 && pdata); - return 0; + return rc; } -int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl) +static int mdss_mdp_ctl_start_sub(struct mdss_mdp_ctl *ctl) { struct mdss_mdp_mixer *mixer; u32 outsize, temp, off; int ret = 0; - ret = mdss_mdp_ctl_setup(ctl); - if (ret) - return ret; - - if (ctl->power_on) { - WARN(1, "already on!\n"); - return 0; - } - - mutex_lock(&ctl->lock); - - ctl->power_on = true; - ctl->bus_ab_quota = 0; - ctl->bus_ib_quota = 0; - ctl->clk_rate = 0; - - mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); - ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_RESET, NULL); - if (ret) { - pr_err("panel power on failed ctl=%d\n", ctl->num); - goto start_fail; - } - if (ctl->start_fnc) ret = ctl->start_fnc(ctl); else @@ -608,7 +705,7 @@ int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl) if (ret) { pr_err("unable to start intf\n"); - goto start_fail; + return ret; } pr_debug("ctl_num=%d\n", ctl->num); @@ -631,17 +728,59 @@ int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl) off = MDSS_MDP_REG_LM_OFFSET(mixer->num); MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, outsize); - if (ctl->mixer_right) { - mixer = ctl->mixer_right; - mdss_mdp_pp_resume(mixer->num); - mixer->params_changed++; - outsize = (mixer->height << 16) | mixer->width; - off = MDSS_MDP_REG_LM_OFFSET(mixer->num); - MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, outsize); - mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_PACK_3D, 0); + return ret; +} + +int mdss_mdp_ctl_start(struct mdss_mdp_ctl *ctl) +{ + struct mdss_mdp_ctl *sctl; + int ret = 0; + + ret = mdss_mdp_ctl_setup(ctl); + if (ret) + return ret; + + if (ctl->power_on) { + WARN(1, "already on!\n"); + return 0; + } + + sctl = mdss_mdp_get_split_ctl(ctl); + + mutex_lock(&ctl->lock); + + ctl->power_on = true; + ctl->bus_ab_quota = 0; + ctl->bus_ib_quota = 0; + ctl->clk_rate = 0; + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + + ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_RESET, NULL); + if (ret) { + pr_err("panel power on failed ctl=%d\n", ctl->num); + return ret; + } + + ret = mdss_mdp_ctl_start_sub(ctl); + if (ret == 0) { + if (sctl) { /* split display is available */ + ret = mdss_mdp_ctl_start_sub(sctl); + if (!ret) + mdss_mdp_ctl_split_display_enable(1, ctl, sctl); + } else if (ctl->mixer_right) { + struct mdss_mdp_mixer *mixer = ctl->mixer_right; + u32 out, off; + + mdss_mdp_pp_resume(mixer->num); + mixer->params_changed++; + out = (mixer->height << 16) | mixer->width; + off = MDSS_MDP_REG_LM_OFFSET(mixer->num); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, out); + mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_PACK_3D, 0); + } } -start_fail: mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); mutex_unlock(&ctl->lock); @@ -650,6 +789,7 @@ start_fail: int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl) { + struct mdss_mdp_ctl *sctl; int ret = 0; if (!ctl->power_on) { @@ -657,6 +797,8 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl) return 0; } + sctl = mdss_mdp_get_split_ctl(ctl); + pr_debug("ctl_num=%d\n", ctl->num); mutex_lock(&ctl->lock); @@ -668,6 +810,12 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl) else pr_warn("no stop func for ctl=%d\n", ctl->num); + if (sctl && sctl->stop_fnc) { + ret = sctl->stop_fnc(sctl); + + mdss_mdp_ctl_split_display_enable(0, ctl, sctl); + } + if (ret) { pr_warn("error powering off intf ctl=%d\n", ctl->num); } else { @@ -912,6 +1060,7 @@ static int mdss_mdp_mixer_update(struct mdss_mdp_mixer *mixer) int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) { + struct mdss_mdp_ctl *sctl = NULL; int mixer1_changed, mixer2_changed; int ret = 0; int perf_update = MDSS_MDP_PERF_UPDATE_SKIP; @@ -933,6 +1082,8 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) return 0; } + sctl = mdss_mdp_get_split_ctl(ctl); + mixer1_changed = (ctl->mixer_left && ctl->mixer_left->params_changed); mixer2_changed = (ctl->mixer_right && ctl->mixer_right->params_changed); @@ -957,11 +1108,22 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_TOP, ctl->opmode); ctl->flush_bits |= BIT(17); /* CTL */ + + if (sctl) { + mdss_mdp_ctl_write(sctl, MDSS_MDP_REG_CTL_TOP, + sctl->opmode); + sctl->flush_bits |= BIT(17); + } } /* postprocessing setup, including dspp */ mdss_mdp_pp_setup(ctl); mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, ctl->flush_bits); + if (sctl) { + mdss_mdp_pp_setup(sctl); + mdss_mdp_ctl_write(sctl, MDSS_MDP_REG_CTL_FLUSH, + sctl->flush_bits); + } wmb(); ctl->flush_bits = 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp_hwio.h b/drivers/video/fbdev/msm/mdss_mdp_hwio.h index d2b2eab96d6f..dd0d998aee11 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_hwio.h +++ b/drivers/video/fbdev/msm/mdss_mdp_hwio.h @@ -34,6 +34,10 @@ #define MDSS_MDP_REG_HIST_INTR_STATUS 0x00120 #define MDSS_MDP_REG_HIST_INTR_CLEAR 0x00124 +#define MDSS_MDP_REG_SPLIT_DISPLAY_EN 0x003F4 +#define MDSS_MDP_REG_SPLIT_DISPLAY_UPPER_PIPE_CTRL 0x003F8 +#define MDSS_MDP_REG_SPLIT_DISPLAY_LOWER_PIPE_CTRL 0x004F0 + #define MDSS_INTF_DSI 0x1 #define MDSS_INTF_HDMI 0x3 #define MDSS_INTF_LCDC 0x5 diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index ad905c5da9fc..97e84d4d6525 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -863,7 +863,7 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) return; } - if (fbi->var.xres > MAX_MIXER_WIDTH) { + if (fbi->var.xres > MAX_MIXER_WIDTH || mfd->split_display) { ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, MDSS_MDP_MIXER_MUX_RIGHT); if (ret) { @@ -1266,6 +1266,15 @@ static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd) mfd->index); return PTR_ERR(ctl); } + + if (mfd->split_display && pdata->next) { + /* enable split display */ + rc = mdss_mdp_ctl_split_display_setup(ctl, pdata->next); + if (rc) { + mdss_mdp_ctl_destroy(ctl); + return rc; + } + } mfd->ctl = ctl; } diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 1682e7635085..40131eb79780 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -196,6 +196,8 @@ struct mdss_panel_data { /* function entry chain */ int (*event_handler) (struct mdss_panel_data *pdata, int e, void *arg); + + struct mdss_panel_data *next; }; int mdss_register_panel(struct platform_device *pdev, |
