diff options
author | Sudhir Vyas <svyas@nvidia.com> | 2014-02-18 19:07:00 +0530 |
---|---|---|
committer | Terje Bergstrom <tbergstrom@nvidia.com> | 2014-02-26 22:01:26 -0800 |
commit | b9de88f6b2362532edbab617fd2d824b83fcf059 (patch) | |
tree | 908ca7e34e797e409589754b205138d45048ebc9 /drivers | |
parent | 5a517b07339bb1b32cd59e3403ffaa56369586a2 (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/Makefile | 8 | ||||
-rw-r--r-- | drivers/video/tegra/host/vi/tegra_vi.c | 20 | ||||
-rw-r--r-- | drivers/video/tegra/host/vi/vi.c | 87 | ||||
-rw-r--r-- | drivers/video/tegra/host/vi/vi.h | 18 | ||||
-rw-r--r-- | drivers/video/tegra/host/vi/vi_irq.c | 211 | ||||
-rw-r--r-- | drivers/video/tegra/host/vi/vi_irq.h | 28 |
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 + |