diff options
| author | Adrian Salido-Moreno <adrianm@codeaurora.org> | 2012-05-29 18:36:57 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:11:41 -0700 |
| commit | f031f425cd59f1fc22aac26f1b85d07f75238fd9 (patch) | |
| tree | 54577ddf528b63534703f22d41a72ed5867bfca6 /drivers/video/fbdev | |
| parent | 4b9ab44ad93663aa5a60ea5bac821089ad5adfa8 (diff) | |
mdss: display: add clock and bus scaling logic
MDSS hardware supports different bus bandwidth and clock frequencies
depending on the load on the hardware. Add support to driver to
calculate the bus bandwidth and clock frequency requirements and
activate changes before the load on hardware changes.
Change-Id: I9bb5bbc7b3e18fb1a738f527b875506dfa0c5c81
Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org>
Diffstat (limited to 'drivers/video/fbdev')
| -rw-r--r-- | drivers/video/fbdev/msm/mdss.h | 15 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.c | 247 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp.h | 8 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_ctl.c | 138 |
4 files changed, 388 insertions, 20 deletions
diff --git a/drivers/video/fbdev/msm/mdss.h b/drivers/video/fbdev/msm/mdss.h index bc326ed545d4..8caa71e828f5 100644 --- a/drivers/video/fbdev/msm/mdss.h +++ b/drivers/video/fbdev/msm/mdss.h @@ -23,13 +23,20 @@ extern unsigned char *mdss_reg_base; +enum mdss_mdp_clk_type { + MDSS_CLK_AHB, + MDSS_CLK_AXI, + MDSS_CLK_MDP_SRC, + MDSS_CLK_MDP_CORE, + MDSS_CLK_MDP_LUT, + MDSS_CLK_MDP_VSYNC, + MDSS_MAX_CLK +}; + struct mdss_res_type { u32 rev; u32 mdp_rev; - struct clk *mdp_clk; - struct clk *mdp_pclk; - struct clk *mdp_lut_clk; - struct clk *vsync_clk; + struct clk *mdp_clk[MDSS_MAX_CLK]; struct regulator *fs; struct workqueue_struct *clk_ctrl_wq; diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index ab5919dc61c9..61b26b93c86f 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -39,14 +39,13 @@ #include <mach/board.h> #include <mach/clk.h> #include <mach/hardware.h> +#include <mach/msm_bus.h> +#include <mach/msm_bus_board.h> #include "mdss.h" #include "mdss_fb.h" #include "mdss_mdp.h" -/* 1.15 mdp clk factor */ -#define MDP_CLK_FACTOR(rate) (((rate) * 23) / 20) - unsigned char *mdss_reg_base; struct mdss_res_type *mdss_res; @@ -75,6 +74,99 @@ u32 mdss_mdp_mixer_type_map[MDSS_MDP_MAX_LAYERMIXER] = { MDSS_MDP_MIXER_TYPE_WRITEBACK, }; +#define MDP_BUS_VECTOR_ENTRY(ab_val, ib_val) \ + { \ + .src = MSM_BUS_MASTER_MDP_PORT0, \ + .dst = MSM_BUS_SLAVE_EBI_CH0, \ + .ab = (ab_val), \ + .ib = (ib_val), \ + } + +#define MDP_BUS_VECTOR_ENTRY_NDX(n) \ + MDP_BUS_VECTOR_ENTRY((n) * 100000000, (n) * 200000000) + +static struct msm_bus_vectors mdp_bus_vectors[] = { + MDP_BUS_VECTOR_ENTRY_NDX(0), + MDP_BUS_VECTOR_ENTRY_NDX(1), + MDP_BUS_VECTOR_ENTRY_NDX(2), + MDP_BUS_VECTOR_ENTRY_NDX(3), + MDP_BUS_VECTOR_ENTRY_NDX(4), + MDP_BUS_VECTOR_ENTRY_NDX(5), + MDP_BUS_VECTOR_ENTRY_NDX(6), + MDP_BUS_VECTOR_ENTRY_NDX(7), + MDP_BUS_VECTOR_ENTRY_NDX(8), + MDP_BUS_VECTOR_ENTRY_NDX(9), + MDP_BUS_VECTOR_ENTRY_NDX(10), + MDP_BUS_VECTOR_ENTRY(200000000, 200000000) +}; +static struct msm_bus_paths mdp_bus_usecases[ARRAY_SIZE(mdp_bus_vectors)]; +static struct msm_bus_scale_pdata mdp_bus_scale_table = { + .usecase = mdp_bus_usecases, + .num_usecases = ARRAY_SIZE(mdp_bus_usecases), + .name = "mdss_mdp", +}; + +static int mdss_mdp_bus_scale_register(void) +{ + if (!mdss_res->bus_hdl) { + struct msm_bus_scale_pdata *bus_pdata = &mdp_bus_scale_table; + int i; + + for (i = 0; i < bus_pdata->num_usecases; i++) { + mdp_bus_usecases[i].num_paths = 1; + mdp_bus_usecases[i].vectors = &mdp_bus_vectors[i]; + } + + mdss_res->bus_hdl = msm_bus_scale_register_client(bus_pdata); + if (!mdss_res->bus_hdl) { + pr_err("not able to get bus scale\n"); + return -ENOMEM; + } + + pr_debug("register bus_hdl=%x\n", mdss_res->bus_hdl); + } + return 0; +} + +static void mdss_mdp_bus_scale_unregister(void) +{ + pr_debug("unregister bus_hdl=%x\n", mdss_res->bus_hdl); + + if (mdss_res->bus_hdl) + msm_bus_scale_unregister_client(mdss_res->bus_hdl); +} + +int mdss_mdp_bus_scale_set_min_quota(u32 quota) +{ + struct msm_bus_scale_pdata *bus_pdata = &mdp_bus_scale_table; + struct msm_bus_vectors *vect = NULL; + int lvl; + + if (mdss_res->bus_hdl < 1) { + pr_err("invalid bus handle %d\n", mdss_res->bus_hdl); + return -EINVAL; + } + + for (lvl = 0; lvl < bus_pdata->num_usecases; lvl++) { + if (bus_pdata->usecase[lvl].num_paths) { + vect = &bus_pdata->usecase[lvl].vectors[0]; + if (vect->ab >= quota) { + pr_debug("lvl=%d quota=%u ab=%u\n", lvl, quota, + vect->ab); + break; + } + } + } + + if (lvl == bus_pdata->num_usecases) { + pr_warn("cannot match quota=%u try with max level\n", quota); + lvl--; + } + + return msm_bus_scale_client_update_request(mdss_res->bus_hdl, lvl); +} + + irqreturn_t mdss_irq_handler(int mdss_irq, void *ptr) { u32 intr = MDSS_MDP_REG_READ(MDSS_REG_HW_INTR_STATUS); @@ -172,13 +264,99 @@ void mdss_mdp_irq_disable_nosync(u32 intr_type, u32 intf_num) spin_unlock(&mdp_lock); } +static inline struct clk *mdss_mdp_get_clk(u32 clk_idx) +{ + if (clk_idx < MDSS_MAX_CLK) + return mdss_res->mdp_clk[clk_idx]; + return NULL; +} + +static int mdss_mdp_clk_update(u32 clk_idx, u32 enable) +{ + int ret = -ENODEV; + struct clk *clk = mdss_mdp_get_clk(clk_idx); + + if (clk) { + pr_debug("clk=%d en=%d\n", clk_idx, enable); + if (enable) { + ret = clk_prepare_enable(clk); + } else { + clk_disable_unprepare(clk); + ret = 0; + } + } + return ret; +} + +int mdss_mdp_vsync_clk_enable(int enable) +{ + int ret = 0; + pr_debug("clk enable=%d\n", enable); + mutex_lock(&mdp_clk_lock); + if (mdss_res->vsync_ena != enable) { + mdss_res->vsync_ena = enable; + ret = mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, enable); + } + mutex_unlock(&mdp_clk_lock); + return ret; +} + +void mdss_mdp_set_clk_rate(unsigned long min_clk_rate) +{ + unsigned long clk_rate; + struct clk *clk = mdss_mdp_get_clk(MDSS_CLK_MDP_SRC); + if (clk) { + mutex_lock(&mdp_clk_lock); + clk_rate = clk_round_rate(clk, min_clk_rate); + if (IS_ERR_VALUE(clk_rate)) { + pr_err("unable to round rate err=%ld\n", clk_rate); + } else if (clk_rate != clk_get_rate(clk)) { + if (IS_ERR_VALUE(clk_set_rate(clk, clk_rate))) + pr_err("clk_set_rate failed\n"); + else + pr_debug("mdp clk rate=%lu\n", clk_rate); + } + mutex_unlock(&mdp_clk_lock); + } +} + +unsigned long mdss_mdp_get_clk_rate(u32 clk_idx) +{ + unsigned long clk_rate = 0; + struct clk *clk = mdss_mdp_get_clk(clk_idx); + mutex_lock(&mdp_clk_lock); + if (clk) + clk_rate = clk_get_rate(clk); + mutex_unlock(&mdp_clk_lock); + + return clk_rate; +} + static void mdss_mdp_clk_ctrl_update(int enable) { if (mdss_res->clk_ena == enable) return; pr_debug("MDP CLKS %s\n", (enable ? "Enable" : "Disable")); + + mutex_lock(&mdp_clk_lock); mdss_res->clk_ena = enable; + mb(); + + mdss_mdp_clk_update(MDSS_CLK_AHB, enable); + mdss_mdp_clk_update(MDSS_CLK_AXI, enable); + + mdss_mdp_clk_update(MDSS_CLK_MDP_CORE, enable); + mdss_mdp_clk_update(MDSS_CLK_MDP_LUT, enable); + if (mdss_res->vsync_ena) + mdss_mdp_clk_update(MDSS_CLK_MDP_VSYNC, enable); + + mutex_unlock(&mdp_clk_lock); +} + +static void mdss_mdp_clk_ctrl_workqueue_handler(struct work_struct *work) +{ + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); } void mdss_mdp_clk_ctrl(int enable, int isr) @@ -233,14 +411,30 @@ void mdss_mdp_clk_ctrl(int enable, int isr) } } -static void mdss_mdp_clk_ctrl_workqueue_handler(struct work_struct *work) +static inline int mdss_mdp_irq_clk_register(struct platform_device *pdev, + char *clk_name, int clk_idx) { - mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + struct clk *tmp; + if (clk_idx >= MDSS_MAX_CLK) { + pr_err("invalid clk index %d\n", clk_idx); + return -EINVAL; + } + + + tmp = clk_get(&pdev->dev, clk_name); + if (IS_ERR(tmp)) { + pr_err("unable to get clk: %s\n", clk_name); + return PTR_ERR(tmp); + } + + mdss_res->mdp_clk[clk_idx] = tmp; + return 0; } -static int mdss_mdp_irq_clk_setup(void) +static int mdss_mdp_irq_clk_setup(struct platform_device *pdev) { int ret; + int i; ret = request_irq(mdss_res->irq, mdss_irq_handler, IRQF_DISABLED, "MDSS", 0); @@ -250,16 +444,38 @@ static int mdss_mdp_irq_clk_setup(void) } disable_irq(mdss_res->irq); - mdss_res->fs = regulator_get(NULL, "fs_mdp"); - if (IS_ERR(mdss_res->fs)) + mdss_res->fs = regulator_get(NULL, "gdsc_mdss"); + if (IS_ERR_OR_NULL(mdss_res->fs)) { mdss_res->fs = NULL; - else { - regulator_enable(mdss_res->fs); - mdss_res->fs_ena = true; + pr_err("unable to get gdsc_mdss regulator\n"); + goto error; } regulator_enable(mdss_res->fs); + if (mdss_mdp_irq_clk_register(pdev, "bus_clk", MDSS_CLK_AXI) || + mdss_mdp_irq_clk_register(pdev, "iface_clk", MDSS_CLK_AHB) || + mdss_mdp_irq_clk_register(pdev, "core_clk_src", MDSS_CLK_MDP_SRC) || + mdss_mdp_irq_clk_register(pdev, "core_clk", MDSS_CLK_MDP_CORE) || + mdss_mdp_irq_clk_register(pdev, "lut_clk", MDSS_CLK_MDP_LUT) || + mdss_mdp_irq_clk_register(pdev, "vsync_clk", MDSS_CLK_MDP_VSYNC)) + goto error; + + mdss_mdp_set_clk_rate(MDP_CLK_DEFAULT_RATE); + pr_debug("mdp clk rate=%ld\n", mdss_mdp_get_clk_rate(MDSS_CLK_MDP_SRC)); + return 0; +error: + for (i = 0; i < MDSS_MAX_CLK; i++) { + if (mdss_res->mdp_clk[i]) + clk_put(mdss_res->mdp_clk[i]); + } + if (mdss_res->fs) + regulator_put(mdss_res->fs); + if (mdss_res->irq) + free_irq(mdss_res->irq, 0); + + return -EINVAL; + } static struct msm_panel_common_pdata *mdss_mdp_populate_pdata( @@ -273,11 +489,11 @@ static struct msm_panel_common_pdata *mdss_mdp_populate_pdata( return pdata; } -static u32 mdss_mdp_res_init(void) +static u32 mdss_mdp_res_init(struct platform_device *pdev) { u32 rc; - rc = mdss_mdp_irq_clk_setup(); + rc = mdss_mdp_irq_clk_setup(pdev); if (rc) return rc; @@ -366,12 +582,12 @@ static int mdss_mdp_probe(struct platform_device *pdev) goto probe_done; } - rc = mdss_mdp_res_init(); + rc = mdss_mdp_res_init(pdev); if (rc) { pr_err("unable to initialize mdss mdp resources\n"); goto probe_done; } - + rc = mdss_mdp_bus_scale_register(); probe_done: if (IS_ERR_VALUE(rc)) { if (mdss_res) { @@ -453,6 +669,7 @@ static int mdss_mdp_remove(struct platform_device *pdev) regulator_put(mdss_res->fs); iounmap(mdss_reg_base); pm_runtime_disable(&pdev->dev); + mdss_mdp_bus_scale_unregister(); return 0; } diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 05b7ea55023d..aeb8af8711c6 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -96,6 +96,7 @@ enum mdss_mdp_block_type { struct mdss_mdp_ctl { u32 num; u32 ref_cnt; + int power_on; u32 intf_num; u32 intf_type; @@ -109,6 +110,8 @@ struct mdss_mdp_ctl { u16 height; u32 dst_format; + u32 bus_quota; + struct msm_fb_data_type *mfd; struct mdss_mdp_mixer *mixer_left; struct mdss_mdp_mixer *mixer_right; @@ -133,6 +136,8 @@ struct mdss_mdp_mixer { u8 cursor_enabled; u8 rotator_mode; + u32 bus_quota; + struct mdss_mdp_ctl *ctl; struct mdss_mdp_pipe *stage_pipe[MDSS_MDP_MAX_STAGE]; }; @@ -218,6 +223,7 @@ struct mdss_mdp_pipe { struct mdss_mdp_format_params *src_fmt; struct mdss_mdp_plane_sizes src_planes; + u32 bus_quota; u8 mixer_stage; u8 is_fg; u8 alpha; @@ -253,6 +259,8 @@ void mdss_mdp_irq_disable_nosync(u32 intr_type, u32 intf_num); int mdss_mdp_set_intr_callback(u32 intr_type, u32 intf_num, void (*fnc_ptr)(void *), void *arg); +int mdss_mdp_bus_scale_set_min_quota(u32 quota); +void mdss_mdp_set_clk_rate(unsigned long min_clk_rate); unsigned long mdss_mdp_get_clk_rate(u32 clk_idx); int mdss_mdp_vsync_clk_enable(int enable); void mdss_mdp_clk_ctrl(int enable, int isr); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 8974595bd170..d9c5de435acf 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -20,10 +20,127 @@ #include "mdss_fb.h" #include "mdss_mdp.h" +enum { + MDSS_MDP_BUS_UPDATE_SKIP, + MDSS_MDP_BUS_UPDATE_EARLY, + MDSS_MDP_BUS_UPDATE_LATE, +}; + static DEFINE_MUTEX(mdss_mdp_ctl_lock); static struct mdss_mdp_ctl mdss_mdp_ctl_list[MDSS_MDP_MAX_CTL]; static struct mdss_mdp_mixer mdss_mdp_mixer_list[MDSS_MDP_MAX_LAYERMIXER]; +static int mdss_mdp_ctl_update_clk_rate(void) +{ + struct mdss_mdp_ctl *ctl; + int cnum; + unsigned long clk_rate = MDP_CLK_DEFAULT_RATE; + + mutex_lock(&mdss_mdp_ctl_lock); + for (cnum = 0; cnum < MDSS_MDP_MAX_CTL; cnum++) { + ctl = &mdss_mdp_ctl_list[cnum]; + if (ctl->power_on && ctl->mfd) { + unsigned long tmp; + pr_debug("ctl=%d pclk_rate=%u\n", ctl->num, + ctl->mfd->panel_info.clk_rate); + tmp = (ctl->mfd->panel_info.clk_rate * 23) / 20; + if (tmp > clk_rate) + clk_rate = tmp; + } + } + mdss_mdp_set_clk_rate(clk_rate); + mutex_unlock(&mdss_mdp_ctl_lock); + + return 0; +} + +static int mdss_mdp_ctl_update_bus_scale(void) +{ + struct mdss_mdp_ctl *ctl; + int cnum; + u32 bus_quota = 0; + + mutex_lock(&mdss_mdp_ctl_lock); + for (cnum = 0; cnum < MDSS_MDP_MAX_CTL; cnum++) { + ctl = &mdss_mdp_ctl_list[cnum]; + if (ctl->power_on) + bus_quota += ctl->bus_quota; + } + mdss_mdp_bus_scale_set_min_quota(bus_quota); + mutex_unlock(&mdss_mdp_ctl_lock); + + return 0; +} + +static void mdss_mdp_bus_update_pipe_quota(struct mdss_mdp_pipe *pipe) +{ + u32 quota; + + quota = pipe->img_width * pipe->img_height * 60 * pipe->src_fmt->bpp; + quota *= 5 / 4; /* 1.25 factor */ + + pr_debug("pipe=%d quota old=%u new=%u\n", pipe->num, + pipe->bus_quota, quota); + pipe->bus_quota = quota; +} + +static int mdss_mdp_bus_update_mixer_quota(struct mdss_mdp_mixer *mixer) +{ + struct mdss_mdp_pipe *pipe; + u32 quota, stage; + + if (!mixer) + return 0; + + quota = 0; + for (stage = 0; stage < MDSS_MDP_MAX_STAGE; stage++) { + pipe = mixer->stage_pipe[stage]; + if (pipe == NULL) + continue; + + quota += pipe->bus_quota; + } + + pr_debug("mixer=%d quota old=%u new=%u\n", mixer->num, + mixer->bus_quota, quota); + + if (quota != mixer->bus_quota) { + mixer->bus_quota = quota; + return 1; + } + + return 0; +} + +static int mdss_mdp_bus_update_ctl_quota(struct mdss_mdp_ctl *ctl) +{ + int ret = MDSS_MDP_BUS_UPDATE_SKIP; + + if (mdss_mdp_bus_update_mixer_quota(ctl->mixer_left) || + mdss_mdp_bus_update_mixer_quota(ctl->mixer_right)) { + u32 quota = 0; + + if (ctl->mixer_left) + quota += ctl->mixer_left->bus_quota; + if (ctl->mixer_right) + quota += ctl->mixer_right->bus_quota; + + pr_debug("ctl=%d quota old=%u new=%u\n", + ctl->num, ctl->bus_quota, quota); + + if (quota != ctl->bus_quota) { + if (quota > ctl->bus_quota) + ret = MDSS_MDP_BUS_UPDATE_EARLY; + else + ret = MDSS_MDP_BUS_UPDATE_LATE; + + ctl->bus_quota = quota; + } + } + + return ret; +} + static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(void) { struct mdss_mdp_ctl *ctl = NULL; @@ -237,6 +354,10 @@ int mdss_mdp_ctl_on(struct msm_fb_data_type *mfd) ctl = mfd->ctl; mutex_lock(&ctl->lock); + + ctl->power_on = true; + mdss_mdp_ctl_update_clk_rate(); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); if (ctl->start_fnc) ret = ctl->start_fnc(ctl); @@ -308,6 +429,8 @@ int mdss_mdp_ctl_off(struct msm_fb_data_type *mfd) pr_debug("ctl_num=%d\n", mfd->ctl->num); mutex_lock(&ctl->lock); + ctl->power_on = false; + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); if (ctl->stop_fnc) ret = ctl->stop_fnc(ctl); @@ -321,6 +444,10 @@ int mdss_mdp_ctl_off(struct msm_fb_data_type *mfd) mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); ctl->play_cnt = 0; + + mdss_mdp_ctl_update_bus_scale(); + mdss_mdp_ctl_update_clk_rate(); + mutex_unlock(&ctl->lock); mdss_mdp_pipe_release_all(mfd); @@ -328,7 +455,6 @@ int mdss_mdp_ctl_off(struct msm_fb_data_type *mfd) if (!mfd->ref_cnt) mdss_mdp_ctl_destroy(mfd); - return ret; } @@ -493,6 +619,7 @@ int mdss_mdp_mixer_pipe_update(struct mdss_mdp_pipe *pipe, int params_changed) if (params_changed) { mixer->params_changed++; mixer->stage_pipe[pipe->mixer_stage] = pipe; + mdss_mdp_bus_update_pipe_quota(pipe); } if (pipe->type == MDSS_MDP_PIPE_TYPE_DMA) @@ -548,6 +675,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) { int mixer1_changed, mixer2_changed; int ret = 0; + int bus_update = MDSS_MDP_BUS_UPDATE_SKIP; if (!ctl) { pr_err("display function not set\n"); @@ -564,6 +692,8 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); if (mixer1_changed || mixer2_changed) { + bus_update = mdss_mdp_bus_update_ctl_quota(ctl); + if (ctl->prepare_fnc) ret = ctl->prepare_fnc(ctl, arg); if (ret) { @@ -571,6 +701,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) goto done; } + if (bus_update == MDSS_MDP_BUS_UPDATE_EARLY) + mdss_mdp_ctl_update_bus_scale(); + if (mixer1_changed) mdss_mdp_mixer_update(ctl->mixer_left); if (mixer2_changed) @@ -591,6 +724,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) ctl->play_cnt++; + if (bus_update == MDSS_MDP_BUS_UPDATE_LATE) + mdss_mdp_ctl_update_bus_scale(); + done: mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); |
