2b656f1a3b804b8bfe4f75348c781bec87d53750
[linux-3.10.git] / drivers / video / tegra / host / vi / vi_irq.c
1 /*
2  * drivers/video/tegra/host/vi/vi_irq.c
3  *
4  * Copyright (c) 2014-2015, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <linux/interrupt.h>
18 #include <linux/module.h>
19 #include <linux/nvhost.h>
20
21 #include "nvhost_acm.h"
22 #include "vi.h"
23 #include "vi_irq.h"
24
25 static const int status_reg_table[] = {
26         VI_CFG_INTERRUPT_STATUS_0,
27         CSI_CSI_PIXEL_PARSER_A_STATUS_0,
28         CSI_CSI_PIXEL_PARSER_B_STATUS_0,
29         VI_CSI_0_ERROR_STATUS,
30         VI_CSI_1_ERROR_STATUS,
31 #ifdef TEGRA_21X_OR_HIGHER_CONFIG
32         VI_CSI_2_ERROR_STATUS,
33         VI_CSI_3_ERROR_STATUS,
34         CSI1_CSI_PIXEL_PARSER_A_STATUS_0,
35         CSI1_CSI_PIXEL_PARSER_B_STATUS_0,
36         CSI2_CSI_PIXEL_PARSER_A_STATUS_0,
37         CSI2_CSI_PIXEL_PARSER_B_STATUS_0,
38
39 #endif
40 };
41
42 static const int mask_reg_table[] = {
43         VI_CFG_INTERRUPT_MASK_0,
44         CSI_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0,
45         CSI_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0,
46         VI_CSI_0_ERROR_INT_MASK_0,
47         VI_CSI_1_ERROR_INT_MASK_0,
48         VI_CSI_0_WD_CTRL,
49         VI_CSI_1_WD_CTRL,
50 #ifdef TEGRA_21X_OR_HIGHER_CONFIG
51         VI_CSI_2_WD_CTRL,
52         VI_CSI_3_WD_CTRL,
53         VI_CSI_2_ERROR_INT_MASK_0,
54         VI_CSI_3_ERROR_INT_MASK_0,
55         CSI1_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0,
56         CSI1_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0,
57         CSI2_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0,
58         CSI2_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0,
59 #endif
60 };
61
62 static inline void clear_state(struct vi *tegra_vi, int addr)
63 {
64         int val;
65         val = host1x_readl(tegra_vi->ndev, addr);
66         host1x_writel(tegra_vi->ndev, addr, val);
67         return;
68 }
69
70 int vi_enable_irq(struct vi *tegra_vi)
71 {
72         int i;
73
74         /* Mask all VI interrupt */
75         for (i = 0; i < ARRAY_SIZE(mask_reg_table); i++)
76                 host1x_writel(tegra_vi->ndev, mask_reg_table[i], 0);
77
78         /* Clear all VI interrupt state registers */
79         for (i = 0; i < ARRAY_SIZE(status_reg_table); i++)
80                 clear_state(tegra_vi, status_reg_table[i]);
81
82         enable_irq(tegra_vi->vi_irq);
83
84         return 0;
85 }
86 EXPORT_SYMBOL(vi_enable_irq);
87
88 int vi_disable_irq(struct vi *tegra_vi)
89 {
90         int i;
91
92         disable_irq(tegra_vi->vi_irq);
93
94         /* Mask all VI interrupt */
95         for (i = 0; i < ARRAY_SIZE(mask_reg_table); i++)
96                 host1x_writel(tegra_vi->ndev, mask_reg_table[i], 0);
97
98         /* Clear all VI interrupt state registers */
99         for (i = 0; i < ARRAY_SIZE(status_reg_table); i++)
100                 clear_state(tegra_vi, status_reg_table[i]);
101
102         return 0;
103 }
104 EXPORT_SYMBOL(vi_disable_irq);
105
106 static irqreturn_t vi_checkwd(struct vi *tegra_vi, int stream)
107 {
108         int err_addr, wd_addr, val;
109
110         switch (stream) {
111         case 0:
112                 err_addr = VI_CSI_0_ERROR_STATUS;
113                 wd_addr = VI_CSI_0_WD_CTRL;
114                 break;
115         case 1:
116                 err_addr = VI_CSI_1_ERROR_STATUS;
117                 wd_addr = VI_CSI_1_WD_CTRL;
118                 break;
119 #ifdef TEGRA_21X_OR_HIGHER_CONFIG
120         case 2:
121                 err_addr = VI_CSI_2_ERROR_STATUS;
122                 wd_addr = VI_CSI_2_WD_CTRL;
123                 break;
124         case 3:
125                 err_addr = VI_CSI_3_ERROR_STATUS;
126                 wd_addr = VI_CSI_3_WD_CTRL;
127                 break;
128 #endif
129         default:
130                 return IRQ_NONE;
131         }
132
133         val = host1x_readl(tegra_vi->ndev, err_addr);
134         if (val & 0x20) {
135                 host1x_writel(tegra_vi->ndev, wd_addr, 0);
136                 host1x_writel(tegra_vi->ndev, err_addr, 0x20);
137                 queue_work(tegra_vi->vi_workqueue, &tegra_vi->mfi_cb_work);
138                 return IRQ_HANDLED;
139         }
140
141         return IRQ_NONE;
142 }
143
144 static irqreturn_t vi_isr(int irq, void *dev_id)
145 {
146         struct vi *tegra_vi = (struct vi *)dev_id;
147         int i, val;
148         irqreturn_t result;
149
150         for (i = 0; i < NUM_VI_WATCHDOG; i++) {
151                 result = vi_checkwd(tegra_vi, i);
152                 if (result == IRQ_HANDLED)
153                         goto handled;
154         }
155
156         dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__);
157
158         val = host1x_readl(tegra_vi->ndev, CSI_CSI_PIXEL_PARSER_A_STATUS_0);
159
160         /* changes required as per t124 register spec */
161         if (val & PPA_FIFO_OVRF)
162                 atomic_inc(&(tegra_vi->vi_out.overflow));
163
164         /* Disable IRQ */
165         vi_disable_irq(tegra_vi);
166
167         schedule_work(&tegra_vi->stats_work);
168
169 handled:
170         return IRQ_HANDLED;
171 }
172 EXPORT_SYMBOL(vi_isr);
173
174 void vi_stats_worker(struct work_struct *work)
175 {
176         struct vi *tegra_vi = container_of(work, struct vi, stats_work);
177
178         dev_dbg(&tegra_vi->ndev->dev,
179                 "%s: vi_out dropped data %u times", __func__,
180                 atomic_read(&(tegra_vi->vi_out.overflow)));
181
182         /* Enable IRQ's */
183         vi_enable_irq(tegra_vi);
184 }
185 EXPORT_SYMBOL(vi_stats_worker);
186
187 int vi_intr_init(struct vi *tegra_vi)
188 {
189         struct platform_device *ndev = tegra_vi->ndev;
190
191         /* Interrupt resources are only associated with
192          * master dev vi.0 so irq must be programmed
193          * with it only.
194          */
195         int ret;
196
197         dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__);
198
199         tegra_vi->vi_irq = platform_get_irq(ndev, 0);
200         if (IS_ERR_VALUE(tegra_vi->vi_irq)) {
201                 dev_err(&tegra_vi->ndev->dev, "missing camera irq\n");
202                 return -ENXIO;
203         }
204
205         ret = request_irq(tegra_vi->vi_irq,
206                         vi_isr,
207                         IRQF_SHARED,
208                         dev_name(&tegra_vi->ndev->dev),
209                         tegra_vi);
210         if (ret) {
211                 dev_err(&tegra_vi->ndev->dev, "failed to get irq\n");
212                 return -EBUSY;
213         }
214
215         disable_irq(tegra_vi->vi_irq);
216
217         return 0;
218 }
219 EXPORT_SYMBOL(vi_intr_init);
220
221 int vi_intr_free(struct vi *tegra_vi)
222 {
223         dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__);
224
225         free_irq(tegra_vi->vi_irq, tegra_vi);
226
227         return 0;
228 }
229 EXPORT_SYMBOL(vi_intr_free);
230