]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/commitdiff
video: tegra: vi: Add interrupt to detect overflow
authorSudhir Vyas <svyas@nvidia.com>
Tue, 18 Feb 2014 13:37:00 +0000 (19:07 +0530)
committerTerje Bergstrom <tbergstrom@nvidia.com>
Thu, 27 Feb 2014 06:01:26 +0000 (22:01 -0800)
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>
drivers/video/tegra/host/vi/Makefile
drivers/video/tegra/host/vi/tegra_vi.c
drivers/video/tegra/host/vi/vi.c
drivers/video/tegra/host/vi/vi.h
drivers/video/tegra/host/vi/vi_irq.c [new file with mode: 0644]
drivers/video/tegra/host/vi/vi_irq.h [new file with mode: 0644]

index f52a7ca8570b300c6087c5e837f818aefb4e1abe..2a3cc38333fcfc6b9c6da3f34539e732ada5c769 100644 (file)
@@ -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
index 965b2406a7963fe721d62de019861c729db88c5e..d4f5c47ede6aaad0f22eb824f50ddb404168d292 100644 (file)
@@ -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;
 }
 
index a78260e256a0003701230a6f782e13b269af3008..9bf5d2e4a84104e8036ae9caa62d3a68e3ae59b0 100644 (file)
 #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;
 
index 5536902621074642bb2b89fedbc0578a97b12459..7ba53d3430732097b80500d3d578c8e0c9f1919f 100644 (file)
@@ -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,
 
 #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 (file)
index 0000000..d71d031
--- /dev/null
@@ -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 (file)
index 0000000..32ed1ed
--- /dev/null
@@ -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
+