summaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
authorAdam Jiang <chaoj@nvidia.com>2013-04-30 19:54:34 +0900
committerHarshada Kale <hkale@nvidia.com>2013-06-26 08:10:18 -0700
commit1b3633fcb9ba134cd0ae8460bba308119ac98215 (patch)
tree9453be7e89433e57b25b1b6fe7a8652d501ac8c4 /drivers/media
parentae3534ebb25604bbbf777cec9bf3e0c3ec8a2634 (diff)
video: tegra: dtv: Add PM QoS control over DTV
This is an experimental patch for controlling on cpu-dma latency and minimum cpu frequency over DTV interface. It is required for receiving ISDB-T Full-seg HDTV signals or ISDB-Tmm signals because TS signals on input will provide higher frequency up to 32MHz. CPU has to run faster to handle DMA interrupts too. Bug 1061456 Bug 1258577 Change-Id: Ic97af353f7575eff6eef977f2b204b6299c84e73 Signed-off-by: Adam Jiang <chaoj@nvidia.com> Reviewed-on: http://git-master/r/224689 Reviewed-by: Harshada Kale <hkale@nvidia.com> Tested-by: Harshada Kale <hkale@nvidia.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/tegra/tegra_dtv.c130
1 files changed, 125 insertions, 5 deletions
diff --git a/drivers/media/video/tegra/tegra_dtv.c b/drivers/media/video/tegra/tegra_dtv.c
index 215ecb439940..f523a622e09b 100644
--- a/drivers/media/video/tegra/tegra_dtv.c
+++ b/drivers/media/video/tegra/tegra_dtv.c
@@ -40,7 +40,7 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/stat.h>
-
+#include <linux/pm_qos.h>
#include <media/tegra_dtv.h>
#include <linux/uaccess.h>
@@ -57,6 +57,9 @@
#define DTV_NUM_BUFS 4
#define DTV_MAX_NUM_BUFS 8
+#define DTV_CPU_BOOST_MHZ 200
+#define DTV_TS_BITRATE (416 * 13)
+
#define DTV_FIFO_ATN_LVL_LOW_GEAR 0
#define DTV_FIFO_ATN_LVL_SECOND_GEAR 1
#define DTV_FIFO_ATN_LVL_THIRD_GEAR 2
@@ -88,6 +91,9 @@ struct dtv_stream {
struct work_struct work;
struct wake_lock wake_lock;
char wake_lock_name[16];
+
+ struct work_struct cpu_boost_work;
+ int cpu_boost_flag;
};
struct tegra_dtv_context {
@@ -108,12 +114,18 @@ struct tegra_dtv_context {
/* for refer back */
struct platform_device *pdev;
struct miscdevice miscdev;
+
+ struct pm_qos_request min_cpufreq;
+ struct pm_qos_request cpudma_lat;
};
/* assigned default values if no parameters were passed by insmod or boot
cmdline */
static unsigned int bufsize = DTV_BUF_SIZE;
static unsigned int bufnum = DTV_NUM_BUFS;
+/* PM QoS control parameters */
+static unsigned int cpuboost = DTV_CPU_BOOST_MHZ;
+static unsigned int tsbitrate = DTV_TS_BITRATE;
static inline struct tegra_dtv_context *to_ctx(struct dtv_stream *s)
{
@@ -145,6 +157,24 @@ static inline void prevent_suspend(struct dtv_stream *s)
wake_lock(&s->wake_lock);
}
+static void dtv_cpu_boost_worker(struct work_struct *work)
+{
+ struct dtv_stream *s = container_of(
+ work, struct dtv_stream, cpu_boost_work);
+ struct tegra_dtv_context *dtv_ctx = to_ctx(s);
+
+ if (s->cpu_boost_flag) {
+ pr_info("%s: Boost CPU frequency to %dMHz.",
+ __func__, cpuboost);
+ pm_qos_update_request(&dtv_ctx->min_cpufreq,
+ cpuboost * 1000);
+ } else {
+ pr_info("%s: Release CPU frequency boost.", __func__);
+ pm_qos_update_request(&dtv_ctx->min_cpufreq,
+ PM_QOS_CPU_FREQ_MIN_DEFAULT_VALUE);
+ }
+}
+
static void tegra_dtv_worker(struct work_struct *w)
{
struct dtv_stream *s = container_of(w, struct dtv_stream, work);
@@ -152,6 +182,11 @@ static void tegra_dtv_worker(struct work_struct *w)
wake_unlock(&s->wake_lock);
}
+static inline void dtv_boost_cpu(struct dtv_stream *s)
+{
+ schedule_work(&s->cpu_boost_work);
+}
+
static inline void wakeup_suspend(struct dtv_stream *s)
{
schedule_work(&s->work);
@@ -211,12 +246,14 @@ static void tegra_dtv_rx_dma_complete(struct tegra_dma_req *req)
__func__, req_num, req->bytes_transferred);
BUG_ON(req_num >= s->num_bufs);
- if (req->bytes_transferred > s->buf_size)
- pr_warn("%s: DMA transferring overlapped. bufno = %d transferred = %d bytes\n",
- __func__, req_num, req->bytes_transferred);
-
complete(&buf->comp);
+ if (req->bytes_transferred > s->buf_size
+ && are_xfers_pending(s)) {
+ dtv_boost_cpu(s);
+ pr_warn("%s: DMA buffer overlapped", __func__);
+ }
+
spin_unlock_irqrestore(&s->dma_req_lock, flags);
}
@@ -329,7 +366,17 @@ static int stop_xfer_unsafe(struct dtv_stream *s)
struct tegra_dtv_context *dtv_ctx = to_ctx(s);
pr_debug("%s called\n", __func__);
+
tegra_dma_cancel(s->dma_chan);
+
+ /* stop CPU boost */
+ s->cpu_boost_flag = 0;
+ schedule_work(&s->cpu_boost_work);
+
+ /* release restriction on CPU-DMA latency */
+ pm_qos_update_request(&dtv_ctx->cpudma_lat,
+ PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE);
+
_dtv_disable_protocol(dtv_ctx);
while ((_dtv_get_status(dtv_ctx) & DTV_STATUS_RXF_FULL) &&
spin < 100) {
@@ -462,6 +509,34 @@ static int start_xfer_unsafe(struct dtv_stream *s, size_t size)
tegra_dma_enqueue_req(s->dma_chan, &buf->dma_req);
}
+ /* set boost cpu enabled */
+ s->cpu_boost_flag = 1;
+
+ /* set the bottom line of cpu-dma latency
+ *
+ * The value of this parameter is about cpu snooping on DMAed
+ * memory. It means that CPU should keep snooping to mark
+ * corresponding entries invalid in cache if any entry is
+ * associated to DMAed memory.
+ *
+ * DTV device really doesn't like DMA snoopying takes longer
+ * than
+ *
+ * (bufsize / bitrate of signal) second
+ *
+ * . For example, if we have 4096 bytes available in each buffer
+ * and capture ISDB-T full-seg data, latency should never be
+ * longer than
+ *
+ * 4K bytes / (416 * 13)kbps ~= 6usec
+ *
+ * This will push a lot of pressure on DMA controller and CPU,
+ * especially on Tegra 3 SoC. Please set buffer size to 8192
+ * bytes at lease in this case.
+ */
+ pm_qos_update_request(&dtv_ctx->cpudma_lat,
+ s->buf_size / DTV_TS_BITRATE);
+
s->last_queued = s->num_bufs - 1;
/* too late ? */
@@ -920,6 +995,8 @@ static int setup_stream(struct dtv_stream *stream)
INIT_WORK(&stream->work, tegra_dtv_worker);
wake_lock_init(&stream->wake_lock, WAKE_LOCK_SUSPEND, "tegra_dtv");
+ INIT_WORK(&stream->cpu_boost_work, dtv_cpu_boost_worker);
+
return ret;
}
@@ -979,6 +1056,12 @@ static int tegra_dtv_probe(struct platform_device *pdev)
goto fail_no_clk;
}
+ /* add PM QoS request but leave it as default value */
+ pm_qos_add_request(&dtv_ctx->min_cpufreq, PM_QOS_CPU_FREQ_MIN,
+ PM_QOS_CPU_FREQ_MIN_DEFAULT_VALUE);
+ pm_qos_add_request(&dtv_ctx->cpudma_lat, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE);
+
/* get resource */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!res)) {
@@ -1129,6 +1212,43 @@ static void __exit tegra_dtv_exit(void)
platform_driver_unregister(&tegra_dtv_driver);
}
+/* PM QoS parameters */
+static int ts_bitrate_set(const char *arg, const struct kernel_param *kp)
+{
+ param_set_int(arg, kp);
+
+ return 0;
+}
+
+static int ts_bitrate_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_get_uint(buffer, kp);
+}
+
+static struct kernel_param_ops ts_bitrate_ops = {
+ .set = ts_bitrate_set,
+ .get = ts_bitrate_get,
+};
+module_param_cb(tsbitrate, &ts_bitrate_ops, &tsbitrate, S_IRUGO|S_IWUSR);
+
+static int cpu_boost_set(const char *arg, const struct kernel_param *kp)
+{
+ param_set_int(arg, kp);
+
+ return 0;
+}
+
+static int cpu_boost_get(char *buffer, const struct kernel_param *kp)
+{
+ return param_get_uint(buffer, kp);
+}
+
+static struct kernel_param_ops cpu_boost_ops = {
+ .set = cpu_boost_set,
+ .get = cpu_boost_get,
+};
+module_param_cb(cpuboost, &cpu_boost_ops, &cpuboost, S_IRUGO|S_IWUSR);
+
module_param(bufsize, uint, S_IRUGO);
module_param(bufnum, uint, S_IRUGO);