summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorSudhir Vyas <svyas@nvidia.com>2014-02-18 19:07:00 +0530
committerTerje Bergstrom <tbergstrom@nvidia.com>2014-02-26 22:01:26 -0800
commitb9de88f6b2362532edbab617fd2d824b83fcf059 (patch)
tree908ca7e34e797e409589754b205138d45048ebc9 /drivers
parent5a517b07339bb1b32cd59e3403ffaa56369586a2 (diff)
video: tegra: vi: Add interrupt to detect overflow
Bug 1315446 Change-Id: I49ab493ffa2d8fc2783420f7799b3f86bba32f6d Signed-off-by: Sudhir Vyas <svyas@nvidia.com> Reviewed-on: http://git-master/r/368826 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Jihoon Bang <jbang@nvidia.com> Reviewed-by: Bryan Wu <pengw@nvidia.com> Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/video/tegra/host/vi/Makefile8
-rw-r--r--drivers/video/tegra/host/vi/tegra_vi.c20
-rw-r--r--drivers/video/tegra/host/vi/vi.c87
-rw-r--r--drivers/video/tegra/host/vi/vi.h18
-rw-r--r--drivers/video/tegra/host/vi/vi_irq.c211
-rw-r--r--drivers/video/tegra/host/vi/vi_irq.h28
6 files changed, 364 insertions, 8 deletions
diff --git a/drivers/video/tegra/host/vi/Makefile b/drivers/video/tegra/host/vi/Makefile
index f52a7ca8570b..2a3cc38333fc 100644
--- a/drivers/video/tegra/host/vi/Makefile
+++ b/drivers/video/tegra/host/vi/Makefile
@@ -3,8 +3,10 @@ ccflags-y += -Idrivers/video/tegra/host
ccflags-y += -Idrivers/video/tegra/camera
ccflags-y += -Werror
-nvhost-vi-objs = \
- vi.o
-
+obj-y += vi_irq.o
obj-y += tegra_vi.o
+
+nvhost-vi-objs = \
+ vi.o \
+
obj-$(CONFIG_TEGRA_GRHOST_VI) += nvhost-vi.o
diff --git a/drivers/video/tegra/host/vi/tegra_vi.c b/drivers/video/tegra/host/vi/tegra_vi.c
index 965b2406a796..d4f5c47ede6a 100644
--- a/drivers/video/tegra/host/vi/tegra_vi.c
+++ b/drivers/video/tegra/host/vi/tegra_vi.c
@@ -32,6 +32,7 @@
#include "chip_support.h"
#include "host1x/host1x.h"
#include "vi.h"
+#include "vi_irq.h"
static DEFINE_MUTEX(la_lock);
@@ -321,6 +322,7 @@ static int vi_open(struct inode *inode, struct file *file)
{
struct nvhost_device_data *pdata;
struct vi *vi;
+ int err = 0;
pdata = container_of(inode->i_cdev,
struct nvhost_device_data, ctrl_cdev);
@@ -332,15 +334,28 @@ static int vi_open(struct inode *inode, struct file *file)
return -ENODEV;
file->private_data = vi;
- return 0;
+
+ err = vi_enable_irq(vi);
+ if (err)
+ dev_err(&vi->ndev->dev, "%s: vi_enable_irq failed\n", __func__);
+
+ return err;
}
static int vi_release(struct inode *inode, struct file *file)
{
-#if defined(CONFIG_TEGRA_ISOMGR)
int ret = 0;
struct vi *tegra_vi = file->private_data;
+ ret = vi_disable_irq(tegra_vi);
+ if (ret) {
+ dev_err(&tegra_vi->ndev->dev,
+ "%s: vi_disable_irq failed\n",
+ __func__);
+ return ret;
+ }
+
+#if defined(CONFIG_TEGRA_ISOMGR)
/* nullify isomgr request */
ret = vi_isomgr_release(tegra_vi);
if (ret) {
@@ -350,6 +365,7 @@ static int vi_release(struct inode *inode, struct file *file)
return -ENOMEM;
}
#endif
+
return 0;
}
diff --git a/drivers/video/tegra/host/vi/vi.c b/drivers/video/tegra/host/vi/vi.c
index a78260e256a0..9bf5d2e4a841 100644
--- a/drivers/video/tegra/host/vi/vi.c
+++ b/drivers/video/tegra/host/vi/vi.c
@@ -37,8 +37,10 @@
#include "t148/t148.h"
#include "t124/t124.h"
#include "vi.h"
+#include "vi_irq.h"
#define MAX_DEVID_LENGTH 16
+#define TEGRA_VI_NAME "tegra_vi"
/*
* MAX_BW = max(VI clock) * 2BPP, in KBps.
@@ -98,6 +100,70 @@ static int vi_isomgr_unregister(struct vi *tegra_vi)
}
#endif
+static int vi_out_show(struct seq_file *s, void *unused)
+{
+ struct vi *vi = s->private;
+
+ seq_printf(s, "vi[%d] overflow: %u\n", vi->ndev->id,
+ atomic_read(&(vi->vi_out.overflow)));
+
+ return 0;
+}
+
+static int vi_out_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, vi_out_show, inode->i_private);
+}
+
+static const struct file_operations vi_out_fops = {
+ .open = vi_out_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static void vi_remove_debugfs(struct vi *vi)
+{
+ debugfs_remove_recursive(vi->debugdir);
+ vi->debugdir = NULL;
+}
+
+static void vi_create_debugfs(struct vi *vi)
+{
+ struct dentry *ret;
+ char tegra_vi_name[20];
+ char debugfs_file_name[20];
+
+
+ snprintf(tegra_vi_name, sizeof(tegra_vi_name),
+ "%s_%u", TEGRA_VI_NAME, vi->ndev->id);
+
+ vi->debugdir = debugfs_create_dir(tegra_vi_name, NULL);
+ if (!vi->debugdir) {
+ dev_err(&vi->ndev->dev,
+ "%s: failed to create %s directory",
+ __func__, tegra_vi_name);
+ goto create_debugfs_fail;
+ }
+
+ snprintf(debugfs_file_name, sizeof(debugfs_file_name),
+ "%s_%u", "vi_out", vi->ndev->id);
+
+ ret = debugfs_create_file(debugfs_file_name, S_IRUGO,
+ vi->debugdir, vi, &vi_out_fops);
+ if (!ret) {
+ dev_err(&vi->ndev->dev,
+ "%s: failed to create %s", __func__, debugfs_file_name);
+ goto create_debugfs_fail;
+ }
+
+ return;
+
+create_debugfs_fail:
+ dev_err(&vi->ndev->dev, "%s: could not create debugfs", __func__);
+ vi_remove_debugfs(vi);
+}
+
static int vi_probe(struct platform_device *dev)
{
int err = 0;
@@ -109,6 +175,11 @@ static int vi_probe(struct platform_device *dev)
match = of_match_device(tegra_vi_of_match, &dev->dev);
if (match)
pdata = (struct nvhost_device_data *)match->data;
+
+ /* DT initializes it to -1, use below WAR to set correct value.
+ * TODO: Once proper fix for dev-id goes in, remove it.
+ */
+ dev->id = dev->dev.id;
} else
pdata = (struct nvhost_device_data *)dev->dev.platform_data;
@@ -142,6 +213,15 @@ static int vi_probe(struct platform_device *dev)
goto vi_probe_fail;
#endif
+ /* call vi_intr_init and stats_work */
+ INIT_WORK(&tegra_vi->stats_work, vi_stats_worker);
+
+ err = vi_intr_init(tegra_vi);
+ if (err)
+ goto intr_init_fail;
+
+ vi_create_debugfs(tegra_vi);
+
i2c_ctrl = pdata->private_data;
pdata->private_data = tegra_vi;
@@ -182,6 +262,7 @@ camera_i2c_unregister:
if (i2c_ctrl && i2c_ctrl->remove_devices)
i2c_ctrl->remove_devices(dev);
pdata->private_data = i2c_ctrl;
+intr_init_fail:
#if defined(CONFIG_TEGRA_ISOMGR)
if (tegra_vi->isomgr_handle)
vi_isomgr_unregister(tegra_vi);
@@ -197,9 +278,7 @@ static int __exit vi_remove(struct platform_device *dev)
int err = 0;
#endif
struct nvhost_device_data *pdata = platform_get_drvdata(dev);
-#if defined(CONFIG_TEGRA_ISOMGR)
struct vi *tegra_vi = (struct vi *)pdata->private_data;
-#endif
dev_info(&dev->dev, "%s: ++\n", __func__);
@@ -208,6 +287,10 @@ static int __exit vi_remove(struct platform_device *dev)
vi_isomgr_unregister(tegra_vi);
#endif
+ vi_remove_debugfs(tegra_vi);
+
+ vi_intr_free(tegra_vi);
+
nvhost_client_device_release(dev);
pdata->aperture[0] = NULL;
diff --git a/drivers/video/tegra/host/vi/vi.h b/drivers/video/tegra/host/vi/vi.h
index 553690262107..7ba53d343073 100644
--- a/drivers/video/tegra/host/vi/vi.h
+++ b/drivers/video/tegra/host/vi/vi.h
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host VI
*
- * Copyright (c) 2012-2013, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2012-2014, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -23,11 +23,27 @@
#include "camera_priv_defs.h"
+#define CSI_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0 0x850
+#define CSI_CSI_PIXEL_PARSER_A_STATUS_0 0x854
+#define PPA_FIFO_OVRF (1 << 5)
+
+#define CSI_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0 0x884
+#define CSI_CSI_PIXEL_PARSER_B_STATUS_0 0x888
+#define PPB_FIFO_OVRF (1 << 5)
+
+struct tegra_vi_stats {
+ atomic_t overflow;
+};
+
struct vi {
struct tegra_camera *camera;
struct platform_device *ndev;
struct regulator *reg;
+ int vi_irq;
uint vi_bw;
+ struct dentry *debugdir;
+ struct tegra_vi_stats vi_out;
+ struct work_struct stats_work;
#if defined(CONFIG_TEGRA_ISOMGR)
tegra_isomgr_handle isomgr_handle;
#endif
diff --git a/drivers/video/tegra/host/vi/vi_irq.c b/drivers/video/tegra/host/vi/vi_irq.c
new file mode 100644
index 000000000000..d71d031e7042
--- /dev/null
+++ b/drivers/video/tegra/host/vi/vi_irq.c
@@ -0,0 +1,211 @@
+/*
+ * drivers/video/tegra/host/vi/vi_irq.c
+ *
+ * Copyright (c) 2014, 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
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/nvhost.h>
+
+#include "nvhost_acm.h"
+#include "vi.h"
+
+int vi_enable_irq(struct vi *tegra_vi)
+{
+ int val;
+ int err = 0;
+
+ err = nvhost_module_busy(tegra_vi->ndev);
+ if (err)
+ return err;
+
+ if (tegra_vi->ndev->id) {
+ /* Reset VI status register */
+ val = host1x_readl(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_B_STATUS_0);
+
+ host1x_writel(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_B_STATUS_0,
+ val);
+
+ /* Enable FIFO Overflow Interrupt */
+ host1x_writel(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0,
+ PPB_FIFO_OVRF);
+ } else {
+ /* Reset VI status register */
+ val = host1x_readl(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_A_STATUS_0);
+
+ host1x_writel(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_A_STATUS_0,
+ val);
+
+ /* Enable FIFO Overflow Interrupt */
+ host1x_writel(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0,
+ PPA_FIFO_OVRF);
+
+ /* interrupts are associated only with master dev vi.0 */
+ enable_irq(tegra_vi->vi_irq);
+ }
+
+ nvhost_module_idle(tegra_vi->ndev);
+
+ return 0;
+}
+EXPORT_SYMBOL(vi_enable_irq);
+
+int vi_disable_irq(struct vi *tegra_vi)
+{
+ int val;
+ int err = 0;
+
+ err = nvhost_module_busy(tegra_vi->ndev);
+ if (err)
+ return err;
+
+ if (tegra_vi->ndev->id) {
+ /* Disable FIFO Overflow Interrupt */
+ host1x_writel(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0,
+ 0);
+
+ /* Reset status register */
+ val = host1x_readl(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_B_STATUS_0);
+
+ host1x_writel(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_B_STATUS_0,
+ val);
+ } else {
+ /* interrupts are associated only with master dev vi.0 */
+ disable_irq(tegra_vi->vi_irq);
+
+ /* Disable FIFO Overflow Interrupt */
+ host1x_writel(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0,
+ 0);
+
+ /* Reset status register */
+ val = host1x_readl(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_A_STATUS_0);
+
+ host1x_writel(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_A_STATUS_0,
+ val);
+ }
+
+ nvhost_module_idle(tegra_vi->ndev);
+
+ return 0;
+}
+EXPORT_SYMBOL(vi_disable_irq);
+
+static irqreturn_t vi_isr(int irq, void *dev_id)
+{
+ struct vi *tegra_vi = (struct vi *)dev_id;
+ int val;
+
+ dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__);
+
+ if (tegra_vi->ndev->id) {
+ val = host1x_readl(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_B_STATUS_0);
+
+ /* changes required as per t124 register spec */
+ if (val & PPB_FIFO_OVRF)
+ atomic_inc(&(tegra_vi->vi_out.overflow));
+
+ /* Reset interrupt status register */
+ host1x_writel(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_B_STATUS_0,
+ val);
+ } else {
+ val = host1x_readl(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_A_STATUS_0);
+
+ /* changes required as per t124 register spec */
+ if (val & PPA_FIFO_OVRF)
+ atomic_inc(&(tegra_vi->vi_out.overflow));
+
+ /* Reset interrupt status register */
+ host1x_writel(tegra_vi->ndev,
+ CSI_CSI_PIXEL_PARSER_A_STATUS_0,
+ val);
+ }
+
+ schedule_work(&tegra_vi->stats_work);
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(vi_isr);
+
+void vi_stats_worker(struct work_struct *work)
+{
+ struct vi *tegra_vi = container_of(work, struct vi, stats_work);
+
+ dev_dbg(&tegra_vi->ndev->dev,
+ "%s: vi[%d]_out dropped data %u times", __func__,
+ tegra_vi->ndev->id,
+ atomic_read(&(tegra_vi->vi_out.overflow)));
+}
+EXPORT_SYMBOL(vi_stats_worker);
+
+int vi_intr_init(struct vi *tegra_vi)
+{
+ struct platform_device *ndev = tegra_vi->ndev;
+
+ /* Interrupt resources are only associated with
+ * master dev vi.0 so irq must be programmed
+ * with it only.
+ */
+ if (tegra_vi->ndev->id == 0) {
+ int ret;
+
+ dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__);
+
+ tegra_vi->vi_irq = platform_get_irq(ndev, 0);
+ if (IS_ERR_VALUE(tegra_vi->vi_irq)) {
+ dev_err(&tegra_vi->ndev->dev, "missing camera irq\n");
+ return -ENXIO;
+ }
+
+ ret = request_irq(tegra_vi->vi_irq,
+ vi_isr,
+ IRQF_SHARED,
+ dev_name(&tegra_vi->ndev->dev),
+ tegra_vi);
+ if (ret) {
+ dev_err(&tegra_vi->ndev->dev, "failed to get irq\n");
+ return -EBUSY;
+ }
+
+ disable_irq(tegra_vi->vi_irq);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(vi_intr_init);
+
+int vi_intr_free(struct vi *tegra_vi)
+{
+ dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__);
+
+ if (tegra_vi->ndev->id == 0)
+ free_irq(tegra_vi->vi_irq, tegra_vi);
+
+ return 0;
+}
+EXPORT_SYMBOL(vi_intr_free);
+
diff --git a/drivers/video/tegra/host/vi/vi_irq.h b/drivers/video/tegra/host/vi/vi_irq.h
new file mode 100644
index 000000000000..32ed1ed0d129
--- /dev/null
+++ b/drivers/video/tegra/host/vi/vi_irq.h
@@ -0,0 +1,28 @@
+/*
+ * drivers/video/tegra/host/vi/vi_irq.h
+ *
+ * Copyright (c) 2014, 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
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __DRIVERS_VIDEO_VI_IRQ_H
+#define __DRIVERS_VIDEO_VI_IRQ_H
+
+#include "camera_priv_defs.h"
+
+int vi_intr_init(struct vi *vi);
+int vi_intr_free(struct vi *vi);
+void vi_stats_worker(struct work_struct *work);
+int vi_enable_irq(struct vi *vi);
+int vi_disable_irq(struct vi *vi);
+#endif
+