summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
authorJon Mayo <jmayo@nvidia.com>2012-12-18 14:57:29 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 13:04:03 -0700
commit2a3dbff56ced2ed558d6477d20fa9469b8248c84 (patch)
treee034d58cc6a2df47691a0f31062032d73e8e4700 /drivers/video
parent6c30614889433c3b0381c5b06a785e051b25e1ff (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.c142
-rw-r--r--drivers/video/tegra/dc/dc.c62
-rw-r--r--drivers/video/tegra/dc/dc_priv.h6
-rw-r--r--drivers/video/tegra/dc/dc_priv_defs.h12
-rw-r--r--drivers/video/tegra/dc/ext/control.c7
-rw-r--r--drivers/video/tegra/dc/ext/events.c20
-rw-r--r--drivers/video/tegra/dc/ext/tegra_dc_ext_priv.h4
-rw-r--r--drivers/video/tegra/dc/mode.c6
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);