diff options
author | Jon Mayo <jmayo@nvidia.com> | 2012-12-18 14:57:29 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2013-09-14 13:04:03 -0700 |
commit | 2a3dbff56ced2ed558d6477d20fa9469b8248c84 (patch) | |
tree | e034d58cc6a2df47691a0f31062032d73e8e4700 /drivers/video | |
parent | 6c30614889433c3b0381c5b06a785e051b25e1ff (diff) |
video: tegra: dc: request bandwidth from isomgr
Report event TEGRA_DC_EXT_EVENT_BANDWIDTH through tegra_dc_ctrl device
when there is not enough bandwidth.
Bug 1169265
Change-Id: Ia95b1ecc7eb16db6f3a709e7c9efc59ab2490ead
Signed-off-by: Jon Mayo <jmayo@nvidia.com>
Reviewed-on: http://git-master/r/207766
Reviewed-by: Automatic_Commit_Validation_User
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/tegra/dc/bandwidth.c | 142 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc.c | 62 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_priv.h | 6 | ||||
-rw-r--r-- | drivers/video/tegra/dc/dc_priv_defs.h | 12 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/control.c | 7 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/events.c | 20 | ||||
-rw-r--r-- | drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h | 4 | ||||
-rw-r--r-- | drivers/video/tegra/dc/mode.c | 6 |
8 files changed, 210 insertions, 49 deletions
diff --git a/drivers/video/tegra/dc/bandwidth.c b/drivers/video/tegra/dc/bandwidth.c index ec0ba2d73d4b..9f2b85308ec3 100644 --- a/drivers/video/tegra/dc/bandwidth.c +++ b/drivers/video/tegra/dc/bandwidth.c @@ -3,6 +3,8 @@ * * Copyright (c) 2010-2013, NVIDIA CORPORATION, All rights reserved. * + * Author: Jon Mayo <jmayo@nvidia.com> + * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. @@ -86,7 +88,8 @@ static void tegra_dc_set_latency_allowance(struct tegra_dc *dc, /* our bandwidth is in kbytes/sec, but LA takes MBps. * round up bandwidth to next 1MBps */ - bw = bw / 1000 + 1; + if (bw != ULONG_MAX) + bw = bw / 1000 + 1; tegra_set_latency_allowance(la_id_tab[dc->ndev->id][w->idx], bw); #if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC) @@ -234,37 +237,92 @@ static unsigned long tegra_dc_get_bandwidth( return tegra_dc_find_max_bandwidth(windows, n); } +#ifdef CONFIG_TEGRA_ISOMGR +/* to save power, call when display memory clients would be idle */ +void tegra_dc_clear_bandwidth(struct tegra_dc *dc) +{ + int latency; + + trace_clear_bandwidth(dc); + latency = tegra_isomgr_reserve(dc->isomgr_handle, 0, 1000); + WARN_ONCE(!latency, "tegra_isomgr_reserve failed\n"); + if (latency) { + latency = tegra_isomgr_realize(dc->isomgr_handle); + WARN_ONCE(!latency, "tegra_isomgr_realize failed\n"); + } else { + tegra_dc_ext_process_bandwidth_renegotiate(dc->ndev->id); + } + dc->bw_kbps = 0; +} +#else /* to save power, call when display memory clients would be idle */ void tegra_dc_clear_bandwidth(struct tegra_dc *dc) { trace_clear_bandwidth(dc); if (tegra_is_clk_enabled(dc->emc_clk)) clk_disable_unprepare(dc->emc_clk); - dc->emc_clk_rate = 0; + dc->bw_kbps = 0; +} + +/* bw in kByte/second. returns Hz for EMC frequency */ +static inline unsigned long tegra_dc_kbps_to_emc(unsigned long bw) +{ + unsigned long freq; + + if (bw == ULONG_MAX) + return ULONG_MAX; + + freq = tegra_emc_bw_to_freq_req(bw); + if (freq >= (ULONG_MAX / 1000)) + return ULONG_MAX; /* freq too big - clamp at max */ + + if (WARN_ONCE((freq * 1000) < freq, "Bandwidth Overflow")) + return ULONG_MAX; /* should never occur because of above. */ + return freq * 1000; } +#endif -/* use the larger of dc->emc_clk_rate or dc->new_emc_clk_rate, and copies - * dc->new_emc_clk_rate into dc->emc_clk_rate. +/* use the larger of dc->bw_kbps or dc->new_bw_kbps, and copies + * dc->new_bw_kbps into dc->bw_kbps. * calling this function both before and after a flip is sufficient to select * the best possible frequency and latency allowance. - * set use_new to true to force dc->new_emc_clk_rate programming. + * set use_new to true to force dc->new_bw_kbps programming. */ void tegra_dc_program_bandwidth(struct tegra_dc *dc, bool use_new) { unsigned i; - if (use_new || dc->emc_clk_rate != dc->new_emc_clk_rate) { + if (use_new || dc->bw_kbps != dc->new_bw_kbps) { + long bw = max(dc->bw_kbps, dc->new_bw_kbps); + +#ifdef CONFIG_TEGRA_ISOMGR + int latency; + + latency = tegra_isomgr_reserve(dc->isomgr_handle, bw, 1000); + if (latency) { + latency = tegra_isomgr_realize(dc->isomgr_handle); + WARN_ONCE(!latency, "tegra_isomgr_realize failed\n"); + } else { + tegra_dc_ext_process_bandwidth_renegotiate( + dc->ndev->id); + } +#else /* EMC version */ + int emc_freq; + /* going from 0 to non-zero */ - if (!dc->emc_clk_rate && !tegra_is_clk_enabled(dc->emc_clk)) + if (!dc->bw_kbps && dc->new_bw_kbps && + !tegra_is_clk_enabled(dc->emc_clk)) clk_prepare_enable(dc->emc_clk); - clk_set_rate(dc->emc_clk, - max(dc->emc_clk_rate, dc->new_emc_clk_rate)); - dc->emc_clk_rate = dc->new_emc_clk_rate; + emc_freq = tegra_dc_kbps_to_emc(bw); + clk_set_rate(dc->emc_clk, emc_freq); /* going from non-zero to 0 */ - if (!dc->new_emc_clk_rate && tegra_is_clk_enabled(dc->emc_clk)) + if (dc->bw_kbps && !dc->new_bw_kbps && + tegra_is_clk_enabled(dc->emc_clk)) clk_disable_unprepare(dc->emc_clk); +#endif + dc->bw_kbps = dc->new_bw_kbps; } for (i = 0; i < DC_N_WINDOWS; i++) { @@ -278,16 +336,6 @@ void tegra_dc_program_bandwidth(struct tegra_dc *dc, bool use_new) } } -/* bw in kByte/second. returns Hz for EMC frequency */ -static inline unsigned long tegra_dc_kbps_to_emc(unsigned long bw) -{ - if (bw >= (ULONG_MAX / 1000)) - return ULONG_MAX; - if (WARN_ONCE((bw * 1000) < bw, "Bandwidth Overflow")) - return ULONG_MAX; - return tegra_emc_bw_to_freq_req(bw) * 1000; -} - int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n) { unsigned long new_rate; @@ -298,14 +346,60 @@ int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n) dc = windows[0]->dc; +#ifdef CONFIG_TEGRA_ISOMGR + new_rate = tegra_dc_get_bandwidth(windows, n); +#else if (tegra_dc_has_multiple_dc()) new_rate = ULONG_MAX; else - new_rate = tegra_dc_kbps_to_emc( - tegra_dc_get_bandwidth(windows, n)); + new_rate = tegra_dc_get_bandwidth(windows, n); +#endif - dc->new_emc_clk_rate = new_rate; + dc->new_bw_kbps = new_rate; trace_set_dynamic_emc(dc); return 0; } + +/* return the minimum bandwidth in kbps for display to function */ +long tegra_dc_calc_min_bandwidth(struct tegra_dc *dc) +{ + unsigned pclk = tegra_dc_get_out_max_pixclock(dc); + + if (WARN_ONCE(!dc, "dc is NULL") || + WARN_ONCE(!dc->out, "dc->out is NULL!")) + return 0; + + if (!pclk && dc->out->type == TEGRA_DC_OUT_HDMI) { + pclk = tegra_dc_get_out_max_pixclock(dc); + if (!pclk) { +#if defined(CONFIG_ARCH_TEGRA_11x_SOC) + pclk = 300000000; /* 300MHz max */ +#else + pclk = 150000000; /* 150MHz max */ +#endif + } + } else { + pclk = dc->mode.pclk; + } + return pclk / 1000 * 4; /* support a single 32bpp window */ +} + +#ifdef CONFIG_TEGRA_ISOMGR +void tegra_dc_bandwidth_renegotiate(void *p) +{ + struct tegra_dc *dc = p; + unsigned long bw; + + if (WARN_ONCE(!dc, "dc is NULL!")) + return; + tegra_dc_ext_process_bandwidth_renegotiate(dc->ndev->id); + + /* a bit of a hack, report the change in bandwidth before it + * really happens. + */ + bw = tegra_dc_calc_min_bandwidth(dc); + if (tegra_isomgr_reserve(dc->isomgr_handle, 0, 1000)) + tegra_isomgr_realize(dc->isomgr_handle); +} +#endif diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 6a9579e5e39c..64e0a5824aaa 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -2301,7 +2301,10 @@ static int tegra_dc_probe(struct platform_device *ndev) struct tegra_dc *dc; struct tegra_dc_mode *mode; struct clk *clk; +#ifndef CONFIG_TEGRA_ISOMGR struct clk *emc_clk; +#endif + int isomgr_client_id = -1; struct resource *res; struct resource *base_res; struct resource *fb_mem = NULL; @@ -2359,6 +2362,7 @@ static int tegra_dc_probe(struct platform_device *ndev) dc->win_syncpt[4] = NVSYNCPT_DISP0_H; #endif dc->powergate_id = TEGRA_POWERGATE_DISA; + isomgr_client_id = TEGRA_ISO_CLIENT_DISP_0; } else if (TEGRA_DISPLAY2_BASE == res->start) { dc->vblank_syncpt = NVSYNCPT_VBLANK1; dc->win_syncpt[0] = NVSYNCPT_DISP1_A; @@ -2368,6 +2372,7 @@ static int tegra_dc_probe(struct platform_device *ndev) dc->win_syncpt[4] = NVSYNCPT_DISP1_H; #endif dc->powergate_id = TEGRA_POWERGATE_DISB; + isomgr_client_id = TEGRA_ISO_CLIENT_DISP_1; } else { dev_err(&ndev->dev, "Unknown base address %#08x: unable to assign syncpt\n", @@ -2384,15 +2389,7 @@ static int tegra_dc_probe(struct platform_device *ndev) goto err_iounmap_reg; } - emc_clk = clk_get(&ndev->dev, "emc"); - if (IS_ERR_OR_NULL(emc_clk)) { - dev_err(&ndev->dev, "can't get emc clock\n"); - ret = -ENOENT; - goto err_put_clk; - } - dc->clk = clk; - dc->emc_clk = emc_clk; dc->shift_clk_div.mul = dc->shift_clk_div.div = 1; /* Initialize one shot work delay, it will be assigned by dsi * according to refresh rate later. */ @@ -2404,11 +2401,7 @@ static int tegra_dc_probe(struct platform_device *ndev) dc->ndev = ndev; dc->pdata = ndev->dev.platform_data; - /* - * The emc is a shared clock, it will be set based on - * the requirements for each user on the bus. - */ - dc->emc_clk_rate = 0; + dc->bw_kbps = 0; mutex_init(&dc->lock); mutex_init(&dc->one_shot_lock); @@ -2441,7 +2434,7 @@ static int tegra_dc_probe(struct platform_device *ndev) ret = tegra_dc_set(dc, ndev->id); if (ret < 0) { dev_err(&ndev->dev, "can't add dc\n"); - goto err_put_emc_clk; + goto err_put_clk; } platform_set_drvdata(ndev, dc); @@ -2463,6 +2456,35 @@ static int tegra_dc_probe(struct platform_device *ndev) dev_err(&ndev->dev, "No default output specified. Leaving output disabled.\n"); dc->mode_dirty = false; /* ignore changes tegra_dc_set_out has done */ +#ifdef CONFIG_TEGRA_ISOMGR + if (isomgr_client_id == -1) { + dc->isomgr_handle = NULL; + } else { + dc->isomgr_handle = tegra_isomgr_register(isomgr_client_id, + tegra_dc_calc_min_bandwidth(dc), + tegra_dc_bandwidth_renegotiate, dc); + if (IS_ERR(dc->isomgr_handle)) { + dev_err(&dc->ndev->dev, + "could not register isomgr. err=%ld\n", + PTR_ERR(dc->isomgr_handle)); + ret = -ENOENT; + goto err_put_clk; + } + } +#else + /* + * The emc is a shared clock, it will be set based on + * the requirements for each user on the bus. + */ + emc_clk = clk_get(&ndev->dev, "emc"); + if (IS_ERR_OR_NULL(emc_clk)) { + dev_err(&ndev->dev, "can't get emc clock\n"); + ret = -ENOENT; + goto err_put_clk; + } + dc->emc_clk = emc_clk; +#endif + dc->ext = tegra_dc_ext_register(ndev, dc); if (IS_ERR_OR_NULL(dc->ext)) { dev_warn(&ndev->dev, "Failed to enable Tegra DC extensions.\n"); @@ -2555,8 +2577,11 @@ err_disable_dc: #ifdef CONFIG_SWITCH switch_dev_unregister(&dc->modeset_switch); #endif -err_put_emc_clk: +#ifdef CONFIG_TEGRA_ISOMGR + tegra_isomgr_unregister(dc->isomgr_handle); +#else clk_put(emc_clk); +#endif err_put_clk: clk_put(clk); err_iounmap_reg: @@ -2600,7 +2625,14 @@ static int tegra_dc_remove(struct platform_device *ndev) switch_dev_unregister(&dc->modeset_switch); #endif free_irq(dc->irq, dc); +#ifdef CONFIG_TEGRA_ISOMGR + if (dc->isomgr_handle) { + tegra_isomgr_unregister(dc->isomgr_handle); + dc->isomgr_handle = NULL; + } +#else clk_put(dc->emc_clk); +#endif clk_put(dc->clk); iounmap(dc->base); if (dc->fb_mem) diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index 229f7554cbd7..d1802f917299 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -4,7 +4,7 @@ * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * - * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * Copyright (c) 2010-2013, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -291,6 +291,10 @@ void tegra_dc_release_dc_out(struct tegra_dc *dc); void tegra_dc_clear_bandwidth(struct tegra_dc *dc); void tegra_dc_program_bandwidth(struct tegra_dc *dc, bool use_new); int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n); +#ifdef CONFIG_TEGRA_ISOMGR +void tegra_dc_bandwidth_renegotiate(void *p); +#endif +long tegra_dc_calc_min_bandwidth(struct tegra_dc *dc); /* defined in mode.c, used in dc.c and window.c */ int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode); diff --git a/drivers/video/tegra/dc/dc_priv_defs.h b/drivers/video/tegra/dc/dc_priv_defs.h index 197e073f8943..a5215b5a900a 100644 --- a/drivers/video/tegra/dc/dc_priv_defs.h +++ b/drivers/video/tegra/dc/dc_priv_defs.h @@ -1,11 +1,10 @@ - /* * drivers/video/tegra/dc/dc_priv.h * * Copyright (C) 2010 Google, Inc. * Author: Erik Gilling <konkers@android.com> * - * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * Copyright (c) 2010-2013, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -35,6 +34,7 @@ #include <mach/tegra_dc_ext.h> #include <mach/hardware.h> #include <mach/clk.h> +#include <mach/isomgr.h> #include "dc_reg.h" @@ -114,9 +114,13 @@ struct tegra_dc { int irq; struct clk *clk; +#ifdef CONFIG_TEGRA_ISOMGR + tegra_isomgr_handle isomgr_handle; +#else struct clk *emc_clk; - int emc_clk_rate; - int new_emc_clk_rate; +#endif + long bw_kbps; /* bandwidth in KBps */ + long new_bw_kbps; struct tegra_dc_shift_clk_div shift_clk_div; u32 powergate_id; diff --git a/drivers/video/tegra/dc/ext/control.c b/drivers/video/tegra/dc/ext/control.c index 3e30f2e3b6cf..9f7b59fa1f1f 100644 --- a/drivers/video/tegra/dc/ext/control.c +++ b/drivers/video/tegra/dc/ext/control.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/ext/control.c * - * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. + * Copyright (c) 2011-2013, NVIDIA CORPORATION, All rights reserved. * * Author: Robert Morell <rmorell@nvidia.com> * @@ -276,3 +276,8 @@ int tegra_dc_ext_control_init(void) return ret; } + +int tegra_dc_ext_process_bandwidth_renegotiate(int output) +{ + return tegra_dc_ext_queue_bandwidth_renegotiate(&g_control, output); +} diff --git a/drivers/video/tegra/dc/ext/events.c b/drivers/video/tegra/dc/ext/events.c index 577d056e2436..a6c58f35954c 100644 --- a/drivers/video/tegra/dc/ext/events.c +++ b/drivers/video/tegra/dc/ext/events.c @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/ext/events.c * - * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. + * Copyright (c) 2011-2013, NVIDIA CORPORATION, All rights reserved. * * Author: Robert Morell <rmorell@nvidia.com> * @@ -195,3 +195,21 @@ int tegra_dc_ext_queue_hotplug(struct tegra_dc_ext_control *control, int output) return 0; } + +int tegra_dc_ext_queue_bandwidth_renegotiate( + struct tegra_dc_ext_control *control, int output) +{ + struct { + struct tegra_dc_ext_event event; + struct tegra_dc_ext_control_event_bandwidth bandwidth; + } __packed pack; + + pack.event.type = TEGRA_DC_EXT_EVENT_BANDWIDTH; + pack.event.data_size = sizeof(pack.bandwidth); + + pack.bandwidth.handle = output; + + tegra_dc_ext_queue_event(control, &pack.event); + + return 0; +} diff --git a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h index 459181e62ef3..1329544b21d7 100644 --- a/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h +++ b/drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h @@ -1,7 +1,7 @@ /* * drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h * - * Copyright (c) 2011-2012, NVIDIA CORPORATION, All rights reserved. + * Copyright (c) 2011-2013, NVIDIA CORPORATION, All rights reserved. * * Author: Robert Morell <rmorell@nvidia.com> * @@ -144,6 +144,8 @@ extern int tegra_dc_ext_control_init(void); extern int tegra_dc_ext_queue_hotplug(struct tegra_dc_ext_control *, int output); +extern int tegra_dc_ext_queue_bandwidth_renegotiate( + struct tegra_dc_ext_control *, int output); extern ssize_t tegra_dc_ext_event_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos); extern unsigned int tegra_dc_ext_event_poll(struct file *, poll_table *); diff --git a/drivers/video/tegra/dc/mode.c b/drivers/video/tegra/dc/mode.c index 45c0746e9966..c860f34f52ed 100644 --- a/drivers/video/tegra/dc/mode.c +++ b/drivers/video/tegra/dc/mode.c @@ -3,7 +3,7 @@ * * Copyright (C) 2010 Google, Inc. * - * Copyright (c) 2010-2012, NVIDIA CORPORATION, All rights reserved. + * Copyright (c) 2010-2013, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -23,6 +23,7 @@ #include <mach/clk.h> #include <mach/dc.h> +#include <mach/mc.h> #include <trace/events/display.h> #include "dc_reg.h" @@ -201,7 +202,8 @@ int tegra_dc_program_mode(struct tegra_dc *dc, struct tegra_dc_mode *mode) print_mode(dc, mode, __func__); /* use default EMC rate when switching modes */ - dc->new_emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc); + dc->new_bw_kbps = tegra_emc_freq_req_to_bw( + tegra_dc_get_default_emc_clk_rate(dc) / 1000); tegra_dc_program_bandwidth(dc, true); tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS); |