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
#include "chip_support.h"
#include "host1x/host1x.h"
#include "vi.h"
+#include "vi_irq.h"
static DEFINE_MUTEX(la_lock);
{
struct nvhost_device_data *pdata;
struct vi *vi;
+ int err = 0;
pdata = container_of(inode->i_cdev,
struct nvhost_device_data, ctrl_cdev);
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) {
return -ENOMEM;
}
#endif
+
return 0;
}
#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.
}
#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;
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;
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;
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);
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__);
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;
*
* 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
--- /dev/null
+/*
+ * 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);
+
--- /dev/null
+/*
+ * 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
+