video: tegra: host: Add CSI-E/F settings for T210
[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         VI_CSI_4_ERROR_STATUS,
35         VI_CSI_5_ERROR_STATUS,
36         CSI1_CSI_PIXEL_PARSER_A_STATUS_0,
37         CSI1_CSI_PIXEL_PARSER_B_STATUS_0,
38         CSI2_CSI_PIXEL_PARSER_A_STATUS_0,
39         CSI2_CSI_PIXEL_PARSER_B_STATUS_0,
40
41 #endif
42 };
43
44 static const int mask_reg_table[] = {
45         VI_CFG_INTERRUPT_MASK_0,
46         CSI_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0,
47         CSI_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0,
48         VI_CSI_0_ERROR_INT_MASK_0,
49         VI_CSI_1_ERROR_INT_MASK_0,
50         VI_CSI_0_WD_CTRL,
51         VI_CSI_1_WD_CTRL,
52 #ifdef TEGRA_21X_OR_HIGHER_CONFIG
53         VI_CSI_2_WD_CTRL,
54         VI_CSI_3_WD_CTRL,
55         VI_CSI_2_ERROR_INT_MASK_0,
56         VI_CSI_3_ERROR_INT_MASK_0,
57         VI_CSI_4_WD_CTRL,
58         VI_CSI_5_WD_CTRL,
59         VI_CSI_4_ERROR_INT_MASK_0,
60         VI_CSI_5_ERROR_INT_MASK_0,
61         CSI1_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0,
62         CSI1_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0,
63         CSI2_CSI_PIXEL_PARSER_A_INTERRUPT_MASK_0,
64         CSI2_CSI_PIXEL_PARSER_B_INTERRUPT_MASK_0,
65 #endif
66 };
67
68 static inline void clear_state(struct vi *tegra_vi, int addr)
69 {
70         int val;
71         val = host1x_readl(tegra_vi->ndev, addr);
72         host1x_writel(tegra_vi->ndev, addr, val);
73         return;
74 }
75
76 int vi_enable_irq(struct vi *tegra_vi)
77 {
78         int i;
79
80         /* Mask all VI interrupt */
81         for (i = 0; i < ARRAY_SIZE(mask_reg_table); i++)
82                 host1x_writel(tegra_vi->ndev, mask_reg_table[i], 0);
83
84         /* Clear all VI interrupt state registers */
85         for (i = 0; i < ARRAY_SIZE(status_reg_table); i++)
86                 clear_state(tegra_vi, status_reg_table[i]);
87
88         enable_irq(tegra_vi->vi_irq);
89
90         return 0;
91 }
92 EXPORT_SYMBOL(vi_enable_irq);
93
94 int vi_disable_irq(struct vi *tegra_vi)
95 {
96         int i;
97
98         disable_irq(tegra_vi->vi_irq);
99
100         /* Mask all VI interrupt */
101         for (i = 0; i < ARRAY_SIZE(mask_reg_table); i++)
102                 host1x_writel(tegra_vi->ndev, mask_reg_table[i], 0);
103
104         /* Clear all VI interrupt state registers */
105         for (i = 0; i < ARRAY_SIZE(status_reg_table); i++)
106                 clear_state(tegra_vi, status_reg_table[i]);
107
108         return 0;
109 }
110 EXPORT_SYMBOL(vi_disable_irq);
111
112 static irqreturn_t vi_checkwd(struct vi *tegra_vi, int stream)
113 {
114         int err_addr, wd_addr, val;
115
116         switch (stream) {
117         case 0:
118                 err_addr = VI_CSI_0_ERROR_STATUS;
119                 wd_addr = VI_CSI_0_WD_CTRL;
120                 break;
121         case 1:
122                 err_addr = VI_CSI_1_ERROR_STATUS;
123                 wd_addr = VI_CSI_1_WD_CTRL;
124                 break;
125 #ifdef TEGRA_21X_OR_HIGHER_CONFIG
126         case 2:
127                 err_addr = VI_CSI_2_ERROR_STATUS;
128                 wd_addr = VI_CSI_2_WD_CTRL;
129                 break;
130         case 3:
131                 err_addr = VI_CSI_3_ERROR_STATUS;
132                 wd_addr = VI_CSI_3_WD_CTRL;
133                 break;
134         case 4:
135                 err_addr = VI_CSI_4_ERROR_STATUS;
136                 wd_addr = VI_CSI_4_WD_CTRL;
137                 break;
138         case 5:
139                 err_addr = VI_CSI_5_ERROR_STATUS;
140                 wd_addr = VI_CSI_5_WD_CTRL;
141                 break;
142 #endif
143         default:
144                 return IRQ_NONE;
145         }
146
147         val = host1x_readl(tegra_vi->ndev, err_addr);
148         if (val & 0x20) {
149                 host1x_writel(tegra_vi->ndev, wd_addr, 0);
150                 host1x_writel(tegra_vi->ndev, err_addr, 0x20);
151                 queue_work(tegra_vi->vi_workqueue, &tegra_vi->mfi_cb_work);
152                 return IRQ_HANDLED;
153         }
154
155         return IRQ_NONE;
156 }
157
158 static irqreturn_t vi_isr(int irq, void *dev_id)
159 {
160         struct vi *tegra_vi = (struct vi *)dev_id;
161         int i, val;
162         irqreturn_t result;
163
164         for (i = 0; i < NUM_VI_WATCHDOG; i++) {
165                 result = vi_checkwd(tegra_vi, i);
166                 if (result == IRQ_HANDLED)
167                         goto handled;
168         }
169
170         dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__);
171
172         val = host1x_readl(tegra_vi->ndev, CSI_CSI_PIXEL_PARSER_A_STATUS_0);
173
174         /* changes required as per t124 register spec */
175         if (val & PPA_FIFO_OVRF)
176                 atomic_inc(&(tegra_vi->vi_out.overflow));
177
178         /* Disable IRQ */
179         vi_disable_irq(tegra_vi);
180
181         schedule_work(&tegra_vi->stats_work);
182
183 handled:
184         return IRQ_HANDLED;
185 }
186 EXPORT_SYMBOL(vi_isr);
187
188 void vi_stats_worker(struct work_struct *work)
189 {
190         struct vi *tegra_vi = container_of(work, struct vi, stats_work);
191
192         dev_dbg(&tegra_vi->ndev->dev,
193                 "%s: vi_out dropped data %u times", __func__,
194                 atomic_read(&(tegra_vi->vi_out.overflow)));
195
196         /* Enable IRQ's */
197         vi_enable_irq(tegra_vi);
198 }
199 EXPORT_SYMBOL(vi_stats_worker);
200
201 int vi_intr_init(struct vi *tegra_vi)
202 {
203         struct platform_device *ndev = tegra_vi->ndev;
204
205         /* Interrupt resources are only associated with
206          * master dev vi.0 so irq must be programmed
207          * with it only.
208          */
209         int ret;
210
211         dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__);
212
213         tegra_vi->vi_irq = platform_get_irq(ndev, 0);
214         if (IS_ERR_VALUE(tegra_vi->vi_irq)) {
215                 dev_err(&tegra_vi->ndev->dev, "missing camera irq\n");
216                 return -ENXIO;
217         }
218
219         ret = request_irq(tegra_vi->vi_irq,
220                         vi_isr,
221                         IRQF_SHARED,
222                         dev_name(&tegra_vi->ndev->dev),
223                         tegra_vi);
224         if (ret) {
225                 dev_err(&tegra_vi->ndev->dev, "failed to get irq\n");
226                 return -EBUSY;
227         }
228
229         disable_irq(tegra_vi->vi_irq);
230
231         return 0;
232 }
233 EXPORT_SYMBOL(vi_intr_init);
234
235 int vi_intr_free(struct vi *tegra_vi)
236 {
237         dev_dbg(&tegra_vi->ndev->dev, "%s: ++", __func__);
238
239         free_irq(tegra_vi->vi_irq, tegra_vi);
240
241         return 0;
242 }
243 EXPORT_SYMBOL(vi_intr_free);
244