]>
Commit | Line | Data |
---|---|---|
443c1228 ST |
1 | /* |
2 | * Driver for the NXP SAA7164 PCIe bridge | |
3 | * | |
63a412ec | 4 | * Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com> |
443c1228 ST |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | */ | |
21 | ||
22 | #include <linux/init.h> | |
23 | #include <linux/list.h> | |
24 | #include <linux/module.h> | |
25 | #include <linux/moduleparam.h> | |
26 | #include <linux/kmod.h> | |
27 | #include <linux/kernel.h> | |
28 | #include <linux/slab.h> | |
29 | #include <linux/interrupt.h> | |
30 | #include <linux/delay.h> | |
31 | #include <asm/div64.h> | |
32 | ||
e48836b8 ST |
33 | #ifdef CONFIG_PROC_FS |
34 | #include <linux/proc_fs.h> | |
35 | #endif | |
443c1228 ST |
36 | #include "saa7164.h" |
37 | ||
38 | MODULE_DESCRIPTION("Driver for NXP SAA7164 based TV cards"); | |
9d119c33 | 39 | MODULE_AUTHOR("Steven Toth <stoth@kernellabs.com>"); |
443c1228 ST |
40 | MODULE_LICENSE("GPL"); |
41 | ||
42 | /* | |
bc250684 ST |
43 | * 1 Basic |
44 | * 2 | |
45 | * 4 i2c | |
46 | * 8 api | |
47 | * 16 cmd | |
48 | * 32 bus | |
443c1228 ST |
49 | */ |
50 | ||
b1912a85 IM |
51 | unsigned int saa_debug; |
52 | module_param_named(debug, saa_debug, int, 0644); | |
443c1228 ST |
53 | MODULE_PARM_DESC(debug, "enable debug messages"); |
54 | ||
5a9ff85d | 55 | static unsigned int fw_debug; |
e48836b8 | 56 | module_param(fw_debug, int, 0644); |
a895d57d | 57 | MODULE_PARM_DESC(fw_debug, "Firmware debug level def:2"); |
e48836b8 | 58 | |
66e1d378 ST |
59 | unsigned int encoder_buffers = SAA7164_MAX_ENCODER_BUFFERS; |
60 | module_param(encoder_buffers, int, 0644); | |
61 | MODULE_PARM_DESC(encoder_buffers, "Total buffers in read queue 16-512 def:64"); | |
62 | ||
e8ce2f21 ST |
63 | unsigned int vbi_buffers = SAA7164_MAX_VBI_BUFFERS; |
64 | module_param(vbi_buffers, int, 0644); | |
65 | MODULE_PARM_DESC(vbi_buffers, "Total buffers in read queue 16-512 def:64"); | |
66 | ||
bbf504c3 | 67 | unsigned int waitsecs = 10; |
dd1ee444 | 68 | module_param(waitsecs, int, 0644); |
66e1d378 | 69 | MODULE_PARM_DESC(waitsecs, "timeout on firmware messages"); |
dd1ee444 | 70 | |
443c1228 ST |
71 | static unsigned int card[] = {[0 ... (SAA7164_MAXBOARDS - 1)] = UNSET }; |
72 | module_param_array(card, int, NULL, 0444); | |
73 | MODULE_PARM_DESC(card, "card type"); | |
74 | ||
5a9ff85d | 75 | static unsigned int print_histogram = 64; |
91d80189 | 76 | module_param(print_histogram, int, 0644); |
66e1d378 | 77 | MODULE_PARM_DESC(print_histogram, "print histogram values once"); |
91d80189 | 78 | |
1b0e8e46 ST |
79 | unsigned int crc_checking = 1; |
80 | module_param(crc_checking, int, 0644); | |
81 | MODULE_PARM_DESC(crc_checking, "enable crc sanity checking on buffers"); | |
82 | ||
5a9ff85d | 83 | static unsigned int guard_checking = 1; |
1b0e8e46 | 84 | module_param(guard_checking, int, 0644); |
bc250684 ST |
85 | MODULE_PARM_DESC(guard_checking, |
86 | "enable dma sanity checking for buffer overruns"); | |
1b0e8e46 | 87 | |
77978089 BM |
88 | static bool enable_msi = true; |
89 | module_param(enable_msi, bool, 0444); | |
90 | MODULE_PARM_DESC(enable_msi, | |
91 | "enable the use of an msi interrupt if available"); | |
92 | ||
443c1228 ST |
93 | static unsigned int saa7164_devcount; |
94 | ||
95 | static DEFINE_MUTEX(devlist); | |
96 | LIST_HEAD(saa7164_devlist); | |
97 | ||
98 | #define INT_SIZE 16 | |
99 | ||
a97781ac ST |
100 | static void saa7164_pack_verifier(struct saa7164_buffer *buf) |
101 | { | |
102 | u8 *p = (u8 *)buf->cpu; | |
103 | int i; | |
104 | ||
105 | for (i = 0; i < buf->actual_size; i += 2048) { | |
106 | ||
c7e242ba MCC |
107 | if ((*(p + i + 0) != 0x00) || (*(p + i + 1) != 0x00) || |
108 | (*(p + i + 2) != 0x01) || (*(p + i + 3) != 0xBA)) { | |
a97781ac | 109 | printk(KERN_ERR "No pack at 0x%x\n", i); |
bc250684 | 110 | #if 0 |
518c267f AS |
111 | print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, |
112 | p + 1, 32, false); | |
bc250684 | 113 | #endif |
1b0e8e46 | 114 | } |
a97781ac ST |
115 | } |
116 | } | |
117 | ||
1b0e8e46 ST |
118 | #define FIXED_VIDEO_PID 0xf1 |
119 | #define FIXED_AUDIO_PID 0xf2 | |
120 | ||
9230acaa ST |
121 | static void saa7164_ts_verifier(struct saa7164_buffer *buf) |
122 | { | |
123 | struct saa7164_port *port = buf->port; | |
9230acaa | 124 | u32 i; |
1b0e8e46 ST |
125 | u8 cc, a; |
126 | u16 pid; | |
065e1477 | 127 | u8 *bufcpu = (u8 *)buf->cpu; |
9230acaa ST |
128 | |
129 | port->sync_errors = 0; | |
130 | port->v_cc_errors = 0; | |
131 | port->a_cc_errors = 0; | |
132 | ||
133 | for (i = 0; i < buf->actual_size; i += 188) { | |
134 | if (*(bufcpu + i) != 0x47) | |
135 | port->sync_errors++; | |
136 | ||
1b0e8e46 ST |
137 | /* TODO: Query pid lower 8 bits, ignoring upper bits intensionally */ |
138 | pid = ((*(bufcpu + i + 1) & 0x1f) << 8) | *(bufcpu + i + 2); | |
9230acaa ST |
139 | cc = *(bufcpu + i + 3) & 0x0f; |
140 | ||
1b0e8e46 | 141 | if (pid == FIXED_VIDEO_PID) { |
9230acaa ST |
142 | a = ((port->last_v_cc + 1) & 0x0f); |
143 | if (a != cc) { | |
1b0e8e46 ST |
144 | printk(KERN_ERR "video cc last = %x current = %x i = %d\n", |
145 | port->last_v_cc, cc, i); | |
9230acaa ST |
146 | port->v_cc_errors++; |
147 | } | |
148 | ||
149 | port->last_v_cc = cc; | |
150 | } else | |
1b0e8e46 | 151 | if (pid == FIXED_AUDIO_PID) { |
9230acaa ST |
152 | a = ((port->last_a_cc + 1) & 0x0f); |
153 | if (a != cc) { | |
1b0e8e46 ST |
154 | printk(KERN_ERR "audio cc last = %x current = %x i = %d\n", |
155 | port->last_a_cc, cc, i); | |
9230acaa ST |
156 | port->a_cc_errors++; |
157 | } | |
158 | ||
159 | port->last_a_cc = cc; | |
160 | } | |
161 | ||
162 | } | |
163 | ||
32299a14 ST |
164 | /* Only report errors if we've been through this function atleast |
165 | * once already and the cached cc values are primed. First time through | |
166 | * always generates errors. | |
167 | */ | |
168 | if (port->v_cc_errors && (port->done_first_interrupt > 1)) | |
9230acaa ST |
169 | printk(KERN_ERR "video pid cc, %d errors\n", port->v_cc_errors); |
170 | ||
32299a14 | 171 | if (port->a_cc_errors && (port->done_first_interrupt > 1)) |
9230acaa ST |
172 | printk(KERN_ERR "audio pid cc, %d errors\n", port->a_cc_errors); |
173 | ||
32299a14 | 174 | if (port->sync_errors && (port->done_first_interrupt > 1)) |
9230acaa | 175 | printk(KERN_ERR "sync_errors = %d\n", port->sync_errors); |
32299a14 ST |
176 | |
177 | if (port->done_first_interrupt == 1) | |
178 | port->done_first_interrupt++; | |
9230acaa ST |
179 | } |
180 | ||
91d80189 | 181 | static void saa7164_histogram_reset(struct saa7164_histogram *hg, char *name) |
443c1228 | 182 | { |
91d80189 | 183 | int i; |
443c1228 | 184 | |
91d80189 ST |
185 | memset(hg, 0, sizeof(struct saa7164_histogram)); |
186 | strcpy(hg->name, name); | |
187 | ||
188 | /* First 30ms x 1ms */ | |
bc250684 | 189 | for (i = 0; i < 30; i++) |
91d80189 | 190 | hg->counter1[0 + i].val = i; |
91d80189 ST |
191 | |
192 | /* 30 - 200ms x 10ms */ | |
bc250684 | 193 | for (i = 0; i < 18; i++) |
91d80189 | 194 | hg->counter1[30 + i].val = 30 + (i * 10); |
91d80189 ST |
195 | |
196 | /* 200 - 2000ms x 100ms */ | |
bc250684 | 197 | for (i = 0; i < 15; i++) |
58acca10 | 198 | hg->counter1[48 + i].val = 200 + (i * 200); |
91d80189 | 199 | |
58acca10 ST |
200 | /* Catch all massive value (2secs) */ |
201 | hg->counter1[55].val = 2000; | |
202 | ||
203 | /* Catch all massive value (4secs) */ | |
204 | hg->counter1[56].val = 4000; | |
205 | ||
206 | /* Catch all massive value (8secs) */ | |
207 | hg->counter1[57].val = 8000; | |
208 | ||
209 | /* Catch all massive value (15secs) */ | |
210 | hg->counter1[58].val = 15000; | |
211 | ||
212 | /* Catch all massive value (30secs) */ | |
213 | hg->counter1[59].val = 30000; | |
214 | ||
215 | /* Catch all massive value (60secs) */ | |
216 | hg->counter1[60].val = 60000; | |
217 | ||
218 | /* Catch all massive value (5mins) */ | |
219 | hg->counter1[61].val = 300000; | |
220 | ||
221 | /* Catch all massive value (15mins) */ | |
222 | hg->counter1[62].val = 900000; | |
223 | ||
224 | /* Catch all massive values (1hr) */ | |
91d80189 | 225 | hg->counter1[63].val = 3600000; |
443c1228 ST |
226 | } |
227 | ||
58acca10 | 228 | void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val) |
443c1228 | 229 | { |
91d80189 | 230 | int i; |
c7e242ba | 231 | for (i = 0; i < 64; i++) { |
91d80189 ST |
232 | if (val <= hg->counter1[i].val) { |
233 | hg->counter1[i].count++; | |
234 | hg->counter1[i].update_time = jiffies; | |
235 | break; | |
236 | } | |
237 | } | |
238 | } | |
443c1228 | 239 | |
91d80189 ST |
240 | static void saa7164_histogram_print(struct saa7164_port *port, |
241 | struct saa7164_histogram *hg) | |
242 | { | |
91d80189 ST |
243 | u32 entries = 0; |
244 | int i; | |
245 | ||
58acca10 | 246 | printk(KERN_ERR "Histogram named %s (ms, count, last_update_jiffy)\n", hg->name); |
c7e242ba | 247 | for (i = 0; i < 64; i++) { |
91d80189 ST |
248 | if (hg->counter1[i].count == 0) |
249 | continue; | |
443c1228 | 250 | |
91d80189 ST |
251 | printk(KERN_ERR " %4d %12d %Ld\n", |
252 | hg->counter1[i].val, | |
253 | hg->counter1[i].count, | |
254 | hg->counter1[i].update_time); | |
255 | ||
256 | entries++; | |
257 | } | |
258 | printk(KERN_ERR "Total: %d\n", entries); | |
443c1228 ST |
259 | } |
260 | ||
cfbaf337 | 261 | static void saa7164_work_enchandler_helper(struct saa7164_port *port, int bufnr) |
7615e434 ST |
262 | { |
263 | struct saa7164_dev *dev = port->dev; | |
61ca1500 PH |
264 | struct saa7164_buffer *buf = NULL; |
265 | struct saa7164_user_buffer *ubuf = NULL; | |
7615e434 | 266 | struct list_head *c, *n; |
cfbaf337 | 267 | int i = 0; |
065e1477 | 268 | u8 *p; |
91d80189 ST |
269 | |
270 | mutex_lock(&port->dmaqueue_lock); | |
7615e434 | 271 | list_for_each_safe(c, n, &port->dmaqueue.list) { |
91d80189 | 272 | |
7615e434 | 273 | buf = list_entry(c, struct saa7164_buffer, list); |
91d80189 ST |
274 | if (i++ > port->hwcfg.buffercount) { |
275 | printk(KERN_ERR "%s() illegal i count %d\n", | |
276 | __func__, i); | |
277 | break; | |
278 | } | |
7615e434 | 279 | |
cfbaf337 | 280 | if (buf->idx == bufnr) { |
12d3203e | 281 | |
7615e434 | 282 | /* Found the buffer, deal with it */ |
1b0e8e46 ST |
283 | dprintk(DBGLVL_IRQ, "%s() bufnr: %d\n", __func__, bufnr); |
284 | ||
285 | if (crc_checking) { | |
286 | /* Throw a new checksum on the dma buffer */ | |
287 | buf->crc = crc32(0, buf->cpu, buf->actual_size); | |
288 | } | |
289 | ||
290 | if (guard_checking) { | |
291 | p = (u8 *)buf->cpu; | |
c7e242ba | 292 | if ((*(p + buf->actual_size + 0) != 0xff) || |
1b0e8e46 ST |
293 | (*(p + buf->actual_size + 1) != 0xff) || |
294 | (*(p + buf->actual_size + 2) != 0xff) || | |
295 | (*(p + buf->actual_size + 3) != 0xff) || | |
296 | (*(p + buf->actual_size + 0x10) != 0xff) || | |
297 | (*(p + buf->actual_size + 0x11) != 0xff) || | |
298 | (*(p + buf->actual_size + 0x12) != 0xff) || | |
c7e242ba | 299 | (*(p + buf->actual_size + 0x13) != 0xff)) { |
1b0e8e46 ST |
300 | printk(KERN_ERR "%s() buf %p guard buffer breach\n", |
301 | __func__, buf); | |
bc250684 | 302 | #if 0 |
518c267f AS |
303 | print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, |
304 | p + buf->actual_size - 32, 64, false); | |
bc250684 | 305 | #endif |
1b0e8e46 ST |
306 | } |
307 | } | |
7615e434 | 308 | |
1107237e ST |
309 | if ((port->nr != SAA7164_PORT_VBI1) && (port->nr != SAA7164_PORT_VBI2)) { |
310 | /* Validate the incoming buffer content */ | |
311 | if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) | |
312 | saa7164_ts_verifier(buf); | |
313 | else if (port->encoder_params.stream_type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS) | |
314 | saa7164_pack_verifier(buf); | |
315 | } | |
9230acaa | 316 | |
7615e434 ST |
317 | /* find a free user buffer and clone to it */ |
318 | if (!list_empty(&port->list_buf_free.list)) { | |
319 | ||
320 | /* Pull the first buffer from the used list */ | |
321 | ubuf = list_first_entry(&port->list_buf_free.list, | |
322 | struct saa7164_user_buffer, list); | |
323 | ||
a97781ac ST |
324 | if (buf->actual_size <= ubuf->actual_size) { |
325 | ||
065e1477 | 326 | memcpy(ubuf->data, buf->cpu, ubuf->actual_size); |
12d3203e | 327 | |
1b0e8e46 ST |
328 | if (crc_checking) { |
329 | /* Throw a new checksum on the read buffer */ | |
330 | ubuf->crc = crc32(0, ubuf->data, ubuf->actual_size); | |
331 | } | |
12d3203e | 332 | |
a97781ac ST |
333 | /* Requeue the buffer on the free list */ |
334 | ubuf->pos = 0; | |
7615e434 | 335 | |
a97781ac ST |
336 | list_move_tail(&ubuf->list, |
337 | &port->list_buf_used.list); | |
7615e434 | 338 | |
a97781ac ST |
339 | /* Flag any userland waiters */ |
340 | wake_up_interruptible(&port->wait_read); | |
7615e434 | 341 | |
a97781ac ST |
342 | } else { |
343 | printk(KERN_ERR "buf %p bufsize fails match\n", buf); | |
344 | } | |
7615e434 ST |
345 | |
346 | } else | |
66e1d378 | 347 | printk(KERN_ERR "encirq no free buffers, increase param encoder_buffers\n"); |
7615e434 | 348 | |
9230acaa | 349 | /* Ensure offset into buffer remains 0, fill buffer |
a97781ac ST |
350 | * with known bad data. We check for this data at a later point |
351 | * in time. */ | |
cfbaf337 | 352 | saa7164_buffer_zero_offsets(port, bufnr); |
065e1477 | 353 | memset(buf->cpu, 0xff, buf->pci_size); |
1b0e8e46 ST |
354 | if (crc_checking) { |
355 | /* Throw yet aanother new checksum on the dma buffer */ | |
356 | buf->crc = crc32(0, buf->cpu, buf->actual_size); | |
357 | } | |
12d3203e | 358 | |
a97781ac | 359 | break; |
cfbaf337 ST |
360 | } |
361 | } | |
362 | mutex_unlock(&port->dmaqueue_lock); | |
363 | } | |
364 | ||
365 | static void saa7164_work_enchandler(struct work_struct *w) | |
366 | { | |
367 | struct saa7164_port *port = | |
368 | container_of(w, struct saa7164_port, workenc); | |
369 | struct saa7164_dev *dev = port->dev; | |
370 | ||
371 | u32 wp, mcb, rp, cnt = 0; | |
372 | ||
373 | port->last_svc_msecs_diff = port->last_svc_msecs; | |
374 | port->last_svc_msecs = jiffies_to_msecs(jiffies); | |
375 | ||
376 | port->last_svc_msecs_diff = port->last_svc_msecs - | |
377 | port->last_svc_msecs_diff; | |
378 | ||
379 | saa7164_histogram_update(&port->svc_interval, | |
380 | port->last_svc_msecs_diff); | |
381 | ||
382 | port->last_irq_svc_msecs_diff = port->last_svc_msecs - | |
383 | port->last_irq_msecs; | |
384 | ||
385 | saa7164_histogram_update(&port->irq_svc_interval, | |
386 | port->last_irq_svc_msecs_diff); | |
9230acaa | 387 | |
cfbaf337 ST |
388 | dprintk(DBGLVL_IRQ, |
389 | "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n", | |
390 | __func__, | |
391 | port->last_svc_msecs_diff, | |
392 | port->last_irq_svc_msecs_diff, | |
393 | port->last_svc_wp, | |
394 | port->last_svc_rp | |
395 | ); | |
396 | ||
397 | /* Current write position */ | |
398 | wp = saa7164_readl(port->bufcounter); | |
399 | if (wp > (port->hwcfg.buffercount - 1)) { | |
400 | printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp); | |
401 | return; | |
402 | } | |
403 | ||
404 | /* Most current complete buffer */ | |
405 | if (wp == 0) | |
1b0e8e46 | 406 | mcb = (port->hwcfg.buffercount - 1); |
cfbaf337 ST |
407 | else |
408 | mcb = wp - 1; | |
409 | ||
410 | while (1) { | |
1b0e8e46 ST |
411 | if (port->done_first_interrupt == 0) { |
412 | port->done_first_interrupt++; | |
413 | rp = mcb; | |
414 | } else | |
415 | rp = (port->last_svc_rp + 1) % 8; | |
cfbaf337 | 416 | |
3eeba4a7 | 417 | if (rp > (port->hwcfg.buffercount - 1)) { |
cfbaf337 ST |
418 | printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp); |
419 | break; | |
7615e434 | 420 | } |
1b0e8e46 | 421 | |
cfbaf337 ST |
422 | saa7164_work_enchandler_helper(port, rp); |
423 | port->last_svc_rp = rp; | |
424 | cnt++; | |
425 | ||
426 | if (rp == mcb) | |
427 | break; | |
7615e434 | 428 | } |
cfbaf337 | 429 | |
1b0e8e46 | 430 | /* TODO: Convert this into a /proc/saa7164 style readable file */ |
91d80189 ST |
431 | if (print_histogram == port->nr) { |
432 | saa7164_histogram_print(port, &port->irq_interval); | |
433 | saa7164_histogram_print(port, &port->svc_interval); | |
434 | saa7164_histogram_print(port, &port->irq_svc_interval); | |
58acca10 ST |
435 | saa7164_histogram_print(port, &port->read_interval); |
436 | saa7164_histogram_print(port, &port->poll_interval); | |
7c161822 | 437 | /* TODO: fix this to preserve any previous state */ |
91d80189 ST |
438 | print_histogram = 64 + port->nr; |
439 | } | |
440 | } | |
cfbaf337 | 441 | |
e8ce2f21 ST |
442 | static void saa7164_work_vbihandler(struct work_struct *w) |
443 | { | |
444 | struct saa7164_port *port = | |
445 | container_of(w, struct saa7164_port, workenc); | |
446 | struct saa7164_dev *dev = port->dev; | |
447 | ||
448 | u32 wp, mcb, rp, cnt = 0; | |
449 | ||
450 | port->last_svc_msecs_diff = port->last_svc_msecs; | |
451 | port->last_svc_msecs = jiffies_to_msecs(jiffies); | |
452 | port->last_svc_msecs_diff = port->last_svc_msecs - | |
453 | port->last_svc_msecs_diff; | |
454 | ||
455 | saa7164_histogram_update(&port->svc_interval, | |
456 | port->last_svc_msecs_diff); | |
457 | ||
458 | port->last_irq_svc_msecs_diff = port->last_svc_msecs - | |
459 | port->last_irq_msecs; | |
460 | ||
461 | saa7164_histogram_update(&port->irq_svc_interval, | |
462 | port->last_irq_svc_msecs_diff); | |
463 | ||
464 | dprintk(DBGLVL_IRQ, | |
465 | "%s() %Ldms elapsed irq->deferred %Ldms wp: %d rp: %d\n", | |
466 | __func__, | |
467 | port->last_svc_msecs_diff, | |
468 | port->last_irq_svc_msecs_diff, | |
469 | port->last_svc_wp, | |
470 | port->last_svc_rp | |
471 | ); | |
472 | ||
473 | /* Current write position */ | |
474 | wp = saa7164_readl(port->bufcounter); | |
475 | if (wp > (port->hwcfg.buffercount - 1)) { | |
476 | printk(KERN_ERR "%s() illegal buf count %d\n", __func__, wp); | |
477 | return; | |
478 | } | |
479 | ||
480 | /* Most current complete buffer */ | |
481 | if (wp == 0) | |
482 | mcb = (port->hwcfg.buffercount - 1); | |
483 | else | |
484 | mcb = wp - 1; | |
1107237e ST |
485 | |
486 | while (1) { | |
487 | if (port->done_first_interrupt == 0) { | |
488 | port->done_first_interrupt++; | |
489 | rp = mcb; | |
490 | } else | |
491 | rp = (port->last_svc_rp + 1) % 8; | |
492 | ||
3eeba4a7 | 493 | if (rp > (port->hwcfg.buffercount - 1)) { |
1107237e ST |
494 | printk(KERN_ERR "%s() illegal rp count %d\n", __func__, rp); |
495 | break; | |
496 | } | |
497 | ||
498 | saa7164_work_enchandler_helper(port, rp); | |
499 | port->last_svc_rp = rp; | |
500 | cnt++; | |
501 | ||
502 | if (rp == mcb) | |
503 | break; | |
504 | } | |
505 | ||
e8ce2f21 ST |
506 | /* TODO: Convert this into a /proc/saa7164 style readable file */ |
507 | if (print_histogram == port->nr) { | |
508 | saa7164_histogram_print(port, &port->irq_interval); | |
509 | saa7164_histogram_print(port, &port->svc_interval); | |
510 | saa7164_histogram_print(port, &port->irq_svc_interval); | |
511 | saa7164_histogram_print(port, &port->read_interval); | |
512 | saa7164_histogram_print(port, &port->poll_interval); | |
513 | /* TODO: fix this to preserve any previous state */ | |
514 | print_histogram = 64 + port->nr; | |
515 | } | |
516 | } | |
517 | ||
91d80189 ST |
518 | static void saa7164_work_cmdhandler(struct work_struct *w) |
519 | { | |
520 | struct saa7164_dev *dev = container_of(w, struct saa7164_dev, workcmd); | |
521 | ||
522 | /* Wake up any complete commands */ | |
523 | saa7164_irq_dequeue(dev); | |
524 | } | |
525 | ||
526 | static void saa7164_buffer_deliver(struct saa7164_buffer *buf) | |
527 | { | |
528 | struct saa7164_port *port = buf->port; | |
529 | ||
530 | /* Feed the transport payload into the kernel demux */ | |
531 | dvb_dmx_swfilter_packets(&port->dvb.demux, (u8 *)buf->cpu, | |
532 | SAA7164_TS_NUMBER_OF_LINES); | |
533 | ||
534 | } | |
535 | ||
e8ce2f21 ST |
536 | static irqreturn_t saa7164_irq_vbi(struct saa7164_port *port) |
537 | { | |
538 | struct saa7164_dev *dev = port->dev; | |
539 | ||
540 | /* Store old time */ | |
541 | port->last_irq_msecs_diff = port->last_irq_msecs; | |
542 | ||
543 | /* Collect new stats */ | |
544 | port->last_irq_msecs = jiffies_to_msecs(jiffies); | |
545 | ||
546 | /* Calculate stats */ | |
547 | port->last_irq_msecs_diff = port->last_irq_msecs - | |
548 | port->last_irq_msecs_diff; | |
549 | ||
550 | saa7164_histogram_update(&port->irq_interval, | |
551 | port->last_irq_msecs_diff); | |
552 | ||
553 | dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__, | |
554 | port->last_irq_msecs_diff); | |
555 | ||
556 | /* Tis calls the vbi irq handler */ | |
557 | schedule_work(&port->workenc); | |
558 | return 0; | |
559 | } | |
560 | ||
91d80189 ST |
561 | static irqreturn_t saa7164_irq_encoder(struct saa7164_port *port) |
562 | { | |
563 | struct saa7164_dev *dev = port->dev; | |
07603131 ST |
564 | |
565 | /* Store old time */ | |
91d80189 ST |
566 | port->last_irq_msecs_diff = port->last_irq_msecs; |
567 | ||
568 | /* Collect new stats */ | |
569 | port->last_irq_msecs = jiffies_to_msecs(jiffies); | |
91d80189 ST |
570 | |
571 | /* Calculate stats */ | |
572 | port->last_irq_msecs_diff = port->last_irq_msecs - | |
573 | port->last_irq_msecs_diff; | |
574 | ||
575 | saa7164_histogram_update(&port->irq_interval, | |
576 | port->last_irq_msecs_diff); | |
577 | ||
cfbaf337 ST |
578 | dprintk(DBGLVL_IRQ, "%s() %Ldms elapsed\n", __func__, |
579 | port->last_irq_msecs_diff); | |
12d3203e | 580 | |
91d80189 | 581 | schedule_work(&port->workenc); |
7615e434 ST |
582 | return 0; |
583 | } | |
584 | ||
add3f580 | 585 | static irqreturn_t saa7164_irq_ts(struct saa7164_port *port) |
443c1228 ST |
586 | { |
587 | struct saa7164_dev *dev = port->dev; | |
588 | struct saa7164_buffer *buf; | |
589 | struct list_head *c, *n; | |
590 | int wp, i = 0, rp; | |
591 | ||
592 | /* Find the current write point from the hardware */ | |
593 | wp = saa7164_readl(port->bufcounter); | |
594 | if (wp > (port->hwcfg.buffercount - 1)) | |
595 | BUG(); | |
596 | ||
597 | /* Find the previous buffer to the current write point */ | |
598 | if (wp == 0) | |
1b0e8e46 | 599 | rp = (port->hwcfg.buffercount - 1); |
443c1228 ST |
600 | else |
601 | rp = wp - 1; | |
602 | ||
603 | /* Lookup the WP in the buffer list */ | |
604 | /* TODO: turn this into a worker thread */ | |
605 | list_for_each_safe(c, n, &port->dmaqueue.list) { | |
606 | buf = list_entry(c, struct saa7164_buffer, list); | |
607 | if (i++ > port->hwcfg.buffercount) | |
608 | BUG(); | |
609 | ||
add3f580 | 610 | if (buf->idx == rp) { |
443c1228 ST |
611 | /* Found the buffer, deal with it */ |
612 | dprintk(DBGLVL_IRQ, "%s() wp: %d processing: %d\n", | |
613 | __func__, wp, rp); | |
614 | saa7164_buffer_deliver(buf); | |
615 | break; | |
616 | } | |
617 | ||
618 | } | |
619 | return 0; | |
620 | } | |
621 | ||
622 | /* Primary IRQ handler and dispatch mechanism */ | |
623 | static irqreturn_t saa7164_irq(int irq, void *dev_id) | |
624 | { | |
625 | struct saa7164_dev *dev = dev_id; | |
3c71d978 | 626 | struct saa7164_port *porta, *portb, *portc, *portd, *porte, *portf; |
7615e434 | 627 | |
50bcb4ae | 628 | u32 intid, intstat[INT_SIZE/4]; |
443c1228 ST |
629 | int i, handled = 0, bit; |
630 | ||
61ca1500 | 631 | if (dev == NULL) { |
d888ea03 ST |
632 | printk(KERN_ERR "%s() No device specified\n", __func__); |
633 | handled = 0; | |
634 | goto out; | |
635 | } | |
636 | ||
3c71d978 MCC |
637 | porta = &dev->ports[SAA7164_PORT_TS1]; |
638 | portb = &dev->ports[SAA7164_PORT_TS2]; | |
639 | portc = &dev->ports[SAA7164_PORT_ENC1]; | |
640 | portd = &dev->ports[SAA7164_PORT_ENC2]; | |
641 | porte = &dev->ports[SAA7164_PORT_VBI1]; | |
642 | portf = &dev->ports[SAA7164_PORT_VBI2]; | |
643 | ||
b595076a UKK |
644 | /* Check that the hardware is accessible. If the status bytes are |
645 | * 0xFF then the device is not accessible, the the IRQ belongs | |
443c1228 | 646 | * to another driver. |
1a6450d4 | 647 | * 4 x u32 interrupt registers. |
443c1228 ST |
648 | */ |
649 | for (i = 0; i < INT_SIZE/4; i++) { | |
650 | ||
651 | /* TODO: Convert into saa7164_readl() */ | |
652 | /* Read the 4 hardware interrupt registers */ | |
1a6450d4 | 653 | intstat[i] = saa7164_readl(dev->int_status + (i * 4)); |
443c1228 | 654 | |
50bcb4ae ST |
655 | if (intstat[i]) |
656 | handled = 1; | |
443c1228 | 657 | } |
50bcb4ae | 658 | if (handled == 0) |
443c1228 | 659 | goto out; |
443c1228 ST |
660 | |
661 | /* For each of the HW interrupt registers */ | |
662 | for (i = 0; i < INT_SIZE/4; i++) { | |
663 | ||
664 | if (intstat[i]) { | |
665 | /* Each function of the board has it's own interruptid. | |
666 | * Find the function that triggered then call | |
667 | * it's handler. | |
668 | */ | |
669 | for (bit = 0; bit < 32; bit++) { | |
670 | ||
671 | if (((intstat[i] >> bit) & 0x00000001) == 0) | |
672 | continue; | |
673 | ||
674 | /* Calculate the interrupt id (0x00 to 0x7f) */ | |
675 | ||
50bcb4ae ST |
676 | intid = (i * 32) + bit; |
677 | if (intid == dev->intfdesc.bInterruptId) { | |
443c1228 ST |
678 | /* A response to an cmd/api call */ |
679 | schedule_work(&dev->workcmd); | |
7615e434 | 680 | } else if (intid == porta->hwcfg.interruptid) { |
443c1228 ST |
681 | |
682 | /* Transport path 1 */ | |
7615e434 | 683 | saa7164_irq_ts(porta); |
443c1228 | 684 | |
7615e434 | 685 | } else if (intid == portb->hwcfg.interruptid) { |
443c1228 ST |
686 | |
687 | /* Transport path 2 */ | |
7615e434 ST |
688 | saa7164_irq_ts(portb); |
689 | ||
690 | } else if (intid == portc->hwcfg.interruptid) { | |
691 | ||
692 | /* Encoder path 1 */ | |
693 | saa7164_irq_encoder(portc); | |
694 | ||
695 | } else if (intid == portd->hwcfg.interruptid) { | |
696 | ||
e8ce2f21 | 697 | /* Encoder path 2 */ |
7615e434 | 698 | saa7164_irq_encoder(portd); |
443c1228 | 699 | |
e8ce2f21 ST |
700 | } else if (intid == porte->hwcfg.interruptid) { |
701 | ||
702 | /* VBI path 1 */ | |
703 | saa7164_irq_vbi(porte); | |
704 | ||
705 | } else if (intid == portf->hwcfg.interruptid) { | |
706 | ||
707 | /* VBI path 2 */ | |
708 | saa7164_irq_vbi(portf); | |
709 | ||
443c1228 ST |
710 | } else { |
711 | /* Find the function */ | |
712 | dprintk(DBGLVL_IRQ, | |
713 | "%s() unhandled interrupt " | |
714 | "reg 0x%x bit 0x%x " | |
715 | "intid = 0x%x\n", | |
50bcb4ae | 716 | __func__, i, bit, intid); |
443c1228 ST |
717 | } |
718 | } | |
719 | ||
443c1228 | 720 | /* Ack it */ |
1a6450d4 | 721 | saa7164_writel(dev->int_ack + (i * 4), intstat[i]); |
443c1228 ST |
722 | |
723 | } | |
724 | } | |
725 | out: | |
726 | return IRQ_RETVAL(handled); | |
727 | } | |
728 | ||
729 | void saa7164_getfirmwarestatus(struct saa7164_dev *dev) | |
730 | { | |
731 | struct saa7164_fw_status *s = &dev->fw_status; | |
732 | ||
733 | dev->fw_status.status = saa7164_readl(SAA_DEVICE_SYSINIT_STATUS); | |
734 | dev->fw_status.mode = saa7164_readl(SAA_DEVICE_SYSINIT_MODE); | |
735 | dev->fw_status.spec = saa7164_readl(SAA_DEVICE_SYSINIT_SPEC); | |
736 | dev->fw_status.inst = saa7164_readl(SAA_DEVICE_SYSINIT_INST); | |
737 | dev->fw_status.cpuload = saa7164_readl(SAA_DEVICE_SYSINIT_CPULOAD); | |
738 | dev->fw_status.remainheap = | |
739 | saa7164_readl(SAA_DEVICE_SYSINIT_REMAINHEAP); | |
740 | ||
741 | dprintk(1, "Firmware status:\n"); | |
742 | dprintk(1, " .status = 0x%08x\n", s->status); | |
743 | dprintk(1, " .mode = 0x%08x\n", s->mode); | |
744 | dprintk(1, " .spec = 0x%08x\n", s->spec); | |
745 | dprintk(1, " .inst = 0x%08x\n", s->inst); | |
746 | dprintk(1, " .cpuload = 0x%08x\n", s->cpuload); | |
747 | dprintk(1, " .remainheap = 0x%08x\n", s->remainheap); | |
748 | } | |
749 | ||
750 | u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev) | |
751 | { | |
752 | u32 reg; | |
753 | ||
754 | reg = saa7164_readl(SAA_DEVICE_VERSION); | |
755 | dprintk(1, "Device running firmware version %d.%d.%d.%d (0x%x)\n", | |
756 | (reg & 0x0000fc00) >> 10, | |
757 | (reg & 0x000003e0) >> 5, | |
758 | (reg & 0x0000001f), | |
759 | (reg & 0xffff0000) >> 16, | |
760 | reg); | |
761 | ||
762 | return reg; | |
763 | } | |
764 | ||
443c1228 ST |
765 | /* TODO: Debugging func, remove */ |
766 | void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr) | |
767 | { | |
768 | int i; | |
769 | ||
770 | dprintk(1, "--------------------> " | |
771 | "00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); | |
772 | ||
773 | for (i = 0; i < 0x100; i += 16) | |
774 | dprintk(1, "region0[0x%08x] = " | |
775 | "%02x %02x %02x %02x %02x %02x %02x %02x" | |
776 | " %02x %02x %02x %02x %02x %02x %02x %02x\n", i, | |
777 | (u8)saa7164_readb(addr + i + 0), | |
778 | (u8)saa7164_readb(addr + i + 1), | |
779 | (u8)saa7164_readb(addr + i + 2), | |
780 | (u8)saa7164_readb(addr + i + 3), | |
781 | (u8)saa7164_readb(addr + i + 4), | |
782 | (u8)saa7164_readb(addr + i + 5), | |
783 | (u8)saa7164_readb(addr + i + 6), | |
784 | (u8)saa7164_readb(addr + i + 7), | |
785 | (u8)saa7164_readb(addr + i + 8), | |
786 | (u8)saa7164_readb(addr + i + 9), | |
787 | (u8)saa7164_readb(addr + i + 10), | |
788 | (u8)saa7164_readb(addr + i + 11), | |
789 | (u8)saa7164_readb(addr + i + 12), | |
790 | (u8)saa7164_readb(addr + i + 13), | |
791 | (u8)saa7164_readb(addr + i + 14), | |
792 | (u8)saa7164_readb(addr + i + 15) | |
793 | ); | |
794 | } | |
795 | ||
796 | static void saa7164_dump_hwdesc(struct saa7164_dev *dev) | |
797 | { | |
4d270cfb MCC |
798 | dprintk(1, "@0x%p hwdesc sizeof(struct tmComResHWDescr) = %d bytes\n", |
799 | &dev->hwdesc, (u32)sizeof(struct tmComResHWDescr)); | |
443c1228 ST |
800 | |
801 | dprintk(1, " .bLength = 0x%x\n", dev->hwdesc.bLength); | |
802 | dprintk(1, " .bDescriptorType = 0x%x\n", dev->hwdesc.bDescriptorType); | |
803 | dprintk(1, " .bDescriptorSubtype = 0x%x\n", | |
804 | dev->hwdesc.bDescriptorSubtype); | |
805 | ||
806 | dprintk(1, " .bcdSpecVersion = 0x%x\n", dev->hwdesc.bcdSpecVersion); | |
807 | dprintk(1, " .dwClockFrequency = 0x%x\n", dev->hwdesc.dwClockFrequency); | |
808 | dprintk(1, " .dwClockUpdateRes = 0x%x\n", dev->hwdesc.dwClockUpdateRes); | |
809 | dprintk(1, " .bCapabilities = 0x%x\n", dev->hwdesc.bCapabilities); | |
810 | dprintk(1, " .dwDeviceRegistersLocation = 0x%x\n", | |
811 | dev->hwdesc.dwDeviceRegistersLocation); | |
812 | ||
813 | dprintk(1, " .dwHostMemoryRegion = 0x%x\n", | |
814 | dev->hwdesc.dwHostMemoryRegion); | |
815 | ||
816 | dprintk(1, " .dwHostMemoryRegionSize = 0x%x\n", | |
817 | dev->hwdesc.dwHostMemoryRegionSize); | |
818 | ||
819 | dprintk(1, " .dwHostHibernatMemRegion = 0x%x\n", | |
820 | dev->hwdesc.dwHostHibernatMemRegion); | |
821 | ||
822 | dprintk(1, " .dwHostHibernatMemRegionSize = 0x%x\n", | |
823 | dev->hwdesc.dwHostHibernatMemRegionSize); | |
824 | } | |
825 | ||
826 | static void saa7164_dump_intfdesc(struct saa7164_dev *dev) | |
827 | { | |
828 | dprintk(1, "@0x%p intfdesc " | |
4d270cfb MCC |
829 | "sizeof(struct tmComResInterfaceDescr) = %d bytes\n", |
830 | &dev->intfdesc, (u32)sizeof(struct tmComResInterfaceDescr)); | |
443c1228 ST |
831 | |
832 | dprintk(1, " .bLength = 0x%x\n", dev->intfdesc.bLength); | |
833 | dprintk(1, " .bDescriptorType = 0x%x\n", dev->intfdesc.bDescriptorType); | |
834 | dprintk(1, " .bDescriptorSubtype = 0x%x\n", | |
835 | dev->intfdesc.bDescriptorSubtype); | |
836 | ||
837 | dprintk(1, " .bFlags = 0x%x\n", dev->intfdesc.bFlags); | |
838 | dprintk(1, " .bInterfaceType = 0x%x\n", dev->intfdesc.bInterfaceType); | |
839 | dprintk(1, " .bInterfaceId = 0x%x\n", dev->intfdesc.bInterfaceId); | |
840 | dprintk(1, " .bBaseInterface = 0x%x\n", dev->intfdesc.bBaseInterface); | |
841 | dprintk(1, " .bInterruptId = 0x%x\n", dev->intfdesc.bInterruptId); | |
842 | dprintk(1, " .bDebugInterruptId = 0x%x\n", | |
843 | dev->intfdesc.bDebugInterruptId); | |
844 | ||
845 | dprintk(1, " .BARLocation = 0x%x\n", dev->intfdesc.BARLocation); | |
846 | } | |
847 | ||
848 | static void saa7164_dump_busdesc(struct saa7164_dev *dev) | |
849 | { | |
4d270cfb MCC |
850 | dprintk(1, "@0x%p busdesc sizeof(struct tmComResBusDescr) = %d bytes\n", |
851 | &dev->busdesc, (u32)sizeof(struct tmComResBusDescr)); | |
443c1228 ST |
852 | |
853 | dprintk(1, " .CommandRing = 0x%016Lx\n", dev->busdesc.CommandRing); | |
854 | dprintk(1, " .ResponseRing = 0x%016Lx\n", dev->busdesc.ResponseRing); | |
855 | dprintk(1, " .CommandWrite = 0x%x\n", dev->busdesc.CommandWrite); | |
856 | dprintk(1, " .CommandRead = 0x%x\n", dev->busdesc.CommandRead); | |
857 | dprintk(1, " .ResponseWrite = 0x%x\n", dev->busdesc.ResponseWrite); | |
858 | dprintk(1, " .ResponseRead = 0x%x\n", dev->busdesc.ResponseRead); | |
859 | } | |
860 | ||
861 | /* Much of the hardware configuration and PCI registers are configured | |
862 | * dynamically depending on firmware. We have to cache some initial | |
863 | * structures then use these to locate other important structures | |
864 | * from PCI space. | |
865 | */ | |
866 | static void saa7164_get_descriptors(struct saa7164_dev *dev) | |
867 | { | |
4d270cfb MCC |
868 | memcpy_fromio(&dev->hwdesc, dev->bmmio, sizeof(struct tmComResHWDescr)); |
869 | memcpy_fromio(&dev->intfdesc, dev->bmmio + sizeof(struct tmComResHWDescr), | |
870 | sizeof(struct tmComResInterfaceDescr)); | |
12d3203e | 871 | memcpy_fromio(&dev->busdesc, dev->bmmio + dev->intfdesc.BARLocation, |
4d270cfb | 872 | sizeof(struct tmComResBusDescr)); |
443c1228 | 873 | |
4d270cfb MCC |
874 | if (dev->hwdesc.bLength != sizeof(struct tmComResHWDescr)) { |
875 | printk(KERN_ERR "Structure struct tmComResHWDescr is mangled\n"); | |
207b42c4 | 876 | printk(KERN_ERR "Need %x got %d\n", dev->hwdesc.bLength, |
4d270cfb | 877 | (u32)sizeof(struct tmComResHWDescr)); |
443c1228 ST |
878 | } else |
879 | saa7164_dump_hwdesc(dev); | |
880 | ||
4d270cfb MCC |
881 | if (dev->intfdesc.bLength != sizeof(struct tmComResInterfaceDescr)) { |
882 | printk(KERN_ERR "struct struct tmComResInterfaceDescr is mangled\n"); | |
207b42c4 | 883 | printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength, |
4d270cfb | 884 | (u32)sizeof(struct tmComResInterfaceDescr)); |
443c1228 ST |
885 | } else |
886 | saa7164_dump_intfdesc(dev); | |
887 | ||
888 | saa7164_dump_busdesc(dev); | |
889 | } | |
890 | ||
891 | static int saa7164_pci_quirks(struct saa7164_dev *dev) | |
892 | { | |
893 | return 0; | |
894 | } | |
895 | ||
896 | static int get_resources(struct saa7164_dev *dev) | |
897 | { | |
898 | if (request_mem_region(pci_resource_start(dev->pci, 0), | |
899 | pci_resource_len(dev->pci, 0), dev->name)) { | |
900 | ||
901 | if (request_mem_region(pci_resource_start(dev->pci, 2), | |
902 | pci_resource_len(dev->pci, 2), dev->name)) | |
903 | return 0; | |
904 | } | |
905 | ||
906 | printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx or 0x%llx\n", | |
907 | dev->name, | |
908 | (u64)pci_resource_start(dev->pci, 0), | |
909 | (u64)pci_resource_start(dev->pci, 2)); | |
910 | ||
911 | return -EBUSY; | |
912 | } | |
913 | ||
7615e434 ST |
914 | static int saa7164_port_init(struct saa7164_dev *dev, int portnr) |
915 | { | |
61ca1500 | 916 | struct saa7164_port *port = NULL; |
7615e434 ST |
917 | |
918 | if ((portnr < 0) || (portnr >= SAA7164_MAX_PORTS)) | |
919 | BUG(); | |
920 | ||
c7e242ba | 921 | port = &dev->ports[portnr]; |
7615e434 ST |
922 | |
923 | port->dev = dev; | |
924 | port->nr = portnr; | |
925 | ||
926 | if ((portnr == SAA7164_PORT_TS1) || (portnr == SAA7164_PORT_TS2)) | |
927 | port->type = SAA7164_MPEG_DVB; | |
928 | else | |
e8ce2f21 | 929 | if ((portnr == SAA7164_PORT_ENC1) || (portnr == SAA7164_PORT_ENC2)) { |
7615e434 | 930 | port->type = SAA7164_MPEG_ENCODER; |
e8ce2f21 ST |
931 | |
932 | /* We need a deferred interrupt handler for cmd handling */ | |
933 | INIT_WORK(&port->workenc, saa7164_work_enchandler); | |
bc250684 | 934 | } else if ((portnr == SAA7164_PORT_VBI1) || (portnr == SAA7164_PORT_VBI2)) { |
e8ce2f21 ST |
935 | port->type = SAA7164_MPEG_VBI; |
936 | ||
937 | /* We need a deferred interrupt handler for cmd handling */ | |
938 | INIT_WORK(&port->workenc, saa7164_work_vbihandler); | |
939 | } else | |
7615e434 ST |
940 | BUG(); |
941 | ||
942 | /* Init all the critical resources */ | |
943 | mutex_init(&port->dvb.lock); | |
944 | INIT_LIST_HEAD(&port->dmaqueue.list); | |
945 | mutex_init(&port->dmaqueue_lock); | |
946 | ||
947 | INIT_LIST_HEAD(&port->list_buf_used.list); | |
948 | INIT_LIST_HEAD(&port->list_buf_free.list); | |
949 | init_waitqueue_head(&port->wait_read); | |
91d80189 | 950 | |
91d80189 ST |
951 | |
952 | saa7164_histogram_reset(&port->irq_interval, "irq intervals"); | |
953 | saa7164_histogram_reset(&port->svc_interval, "deferred intervals"); | |
954 | saa7164_histogram_reset(&port->irq_svc_interval, | |
955 | "irq to deferred intervals"); | |
58acca10 | 956 | saa7164_histogram_reset(&port->read_interval, |
e8ce2f21 | 957 | "encoder/vbi read() intervals"); |
58acca10 | 958 | saa7164_histogram_reset(&port->poll_interval, |
e8ce2f21 | 959 | "encoder/vbi poll() intervals"); |
91d80189 | 960 | |
7615e434 ST |
961 | return 0; |
962 | } | |
963 | ||
443c1228 ST |
964 | static int saa7164_dev_setup(struct saa7164_dev *dev) |
965 | { | |
966 | int i; | |
967 | ||
968 | mutex_init(&dev->lock); | |
969 | atomic_inc(&dev->refcount); | |
970 | dev->nr = saa7164_devcount++; | |
971 | ||
0e72cc8b | 972 | snprintf(dev->name, sizeof(dev->name), "saa7164[%d]", dev->nr); |
443c1228 ST |
973 | |
974 | mutex_lock(&devlist); | |
975 | list_add_tail(&dev->devlist, &saa7164_devlist); | |
976 | mutex_unlock(&devlist); | |
977 | ||
978 | /* board config */ | |
979 | dev->board = UNSET; | |
980 | if (card[dev->nr] < saa7164_bcount) | |
981 | dev->board = card[dev->nr]; | |
982 | ||
983 | for (i = 0; UNSET == dev->board && i < saa7164_idcount; i++) | |
984 | if (dev->pci->subsystem_vendor == saa7164_subids[i].subvendor && | |
985 | dev->pci->subsystem_device == | |
986 | saa7164_subids[i].subdevice) | |
987 | dev->board = saa7164_subids[i].card; | |
988 | ||
989 | if (UNSET == dev->board) { | |
990 | dev->board = SAA7164_BOARD_UNKNOWN; | |
991 | saa7164_card_list(dev); | |
992 | } | |
993 | ||
994 | dev->pci_bus = dev->pci->bus->number; | |
995 | dev->pci_slot = PCI_SLOT(dev->pci->devfn); | |
996 | ||
997 | /* I2C Defaults / setup */ | |
998 | dev->i2c_bus[0].dev = dev; | |
999 | dev->i2c_bus[0].nr = 0; | |
1000 | dev->i2c_bus[1].dev = dev; | |
1001 | dev->i2c_bus[1].nr = 1; | |
1002 | dev->i2c_bus[2].dev = dev; | |
1003 | dev->i2c_bus[2].nr = 2; | |
1004 | ||
7615e434 ST |
1005 | /* Transport + Encoder ports 1, 2, 3, 4 - Defaults / setup */ |
1006 | saa7164_port_init(dev, SAA7164_PORT_TS1); | |
1007 | saa7164_port_init(dev, SAA7164_PORT_TS2); | |
1008 | saa7164_port_init(dev, SAA7164_PORT_ENC1); | |
1009 | saa7164_port_init(dev, SAA7164_PORT_ENC2); | |
e8ce2f21 ST |
1010 | saa7164_port_init(dev, SAA7164_PORT_VBI1); |
1011 | saa7164_port_init(dev, SAA7164_PORT_VBI2); | |
443c1228 ST |
1012 | |
1013 | if (get_resources(dev) < 0) { | |
1014 | printk(KERN_ERR "CORE %s No more PCIe resources for " | |
1015 | "subsystem: %04x:%04x\n", | |
1016 | dev->name, dev->pci->subsystem_vendor, | |
1017 | dev->pci->subsystem_device); | |
1018 | ||
1019 | saa7164_devcount--; | |
1020 | return -ENODEV; | |
1021 | } | |
1022 | ||
1023 | /* PCI/e allocations */ | |
1024 | dev->lmmio = ioremap(pci_resource_start(dev->pci, 0), | |
1025 | pci_resource_len(dev->pci, 0)); | |
1026 | ||
1027 | dev->lmmio2 = ioremap(pci_resource_start(dev->pci, 2), | |
1028 | pci_resource_len(dev->pci, 2)); | |
1029 | ||
443c1228 ST |
1030 | dev->bmmio = (u8 __iomem *)dev->lmmio; |
1031 | dev->bmmio2 = (u8 __iomem *)dev->lmmio2; | |
443c1228 | 1032 | |
1a6450d4 ST |
1033 | /* Inerrupt and ack register locations offset of bmmio */ |
1034 | dev->int_status = 0x183000 + 0xf80; | |
1035 | dev->int_ack = 0x183000 + 0xf90; | |
443c1228 ST |
1036 | |
1037 | printk(KERN_INFO | |
1038 | "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", | |
1039 | dev->name, dev->pci->subsystem_vendor, | |
1040 | dev->pci->subsystem_device, saa7164_boards[dev->board].name, | |
1041 | dev->board, card[dev->nr] == dev->board ? | |
1042 | "insmod option" : "autodetected"); | |
1043 | ||
1044 | saa7164_pci_quirks(dev); | |
1045 | ||
1046 | return 0; | |
1047 | } | |
1048 | ||
1049 | static void saa7164_dev_unregister(struct saa7164_dev *dev) | |
1050 | { | |
1051 | dprintk(1, "%s()\n", __func__); | |
1052 | ||
1053 | release_mem_region(pci_resource_start(dev->pci, 0), | |
1054 | pci_resource_len(dev->pci, 0)); | |
1055 | ||
1056 | release_mem_region(pci_resource_start(dev->pci, 2), | |
1057 | pci_resource_len(dev->pci, 2)); | |
1058 | ||
1059 | if (!atomic_dec_and_test(&dev->refcount)) | |
1060 | return; | |
1061 | ||
1062 | iounmap(dev->lmmio); | |
1063 | iounmap(dev->lmmio2); | |
1064 | ||
1065 | return; | |
1066 | } | |
1067 | ||
e48836b8 ST |
1068 | #ifdef CONFIG_PROC_FS |
1069 | static int saa7164_proc_show(struct seq_file *m, void *v) | |
1070 | { | |
1071 | struct saa7164_dev *dev; | |
4d270cfb | 1072 | struct tmComResBusInfo *b; |
e48836b8 ST |
1073 | struct list_head *list; |
1074 | int i, c; | |
1075 | ||
1076 | if (saa7164_devcount == 0) | |
1077 | return 0; | |
1078 | ||
1079 | list_for_each(list, &saa7164_devlist) { | |
1080 | dev = list_entry(list, struct saa7164_dev, devlist); | |
1081 | seq_printf(m, "%s = %p\n", dev->name, dev); | |
1082 | ||
e48836b8 ST |
1083 | /* Lock the bus from any other access */ |
1084 | b = &dev->bus; | |
1085 | mutex_lock(&b->lock); | |
0b62ceb0 ST |
1086 | |
1087 | seq_printf(m, " .m_pdwSetWritePos = 0x%x (0x%08x)\n", | |
1088 | b->m_dwSetReadPos, saa7164_readl(b->m_dwSetReadPos)); | |
1089 | ||
1090 | seq_printf(m, " .m_pdwSetReadPos = 0x%x (0x%08x)\n", | |
1091 | b->m_dwSetWritePos, saa7164_readl(b->m_dwSetWritePos)); | |
1092 | ||
1093 | seq_printf(m, " .m_pdwGetWritePos = 0x%x (0x%08x)\n", | |
1094 | b->m_dwGetReadPos, saa7164_readl(b->m_dwGetReadPos)); | |
1095 | ||
1096 | seq_printf(m, " .m_pdwGetReadPos = 0x%x (0x%08x)\n", | |
1097 | b->m_dwGetWritePos, saa7164_readl(b->m_dwGetWritePos)); | |
1098 | c = 0; | |
1099 | seq_printf(m, "\n Set Ring:\n"); | |
1100 | seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); | |
1101 | for (i = 0; i < b->m_dwSizeSetRing; i++) { | |
1102 | if (c == 0) | |
1103 | seq_printf(m, " %04x:", i); | |
1104 | ||
065e1477 | 1105 | seq_printf(m, " %02x", readb(b->m_pdwSetRing + i)); |
0b62ceb0 ST |
1106 | |
1107 | if (++c == 16) { | |
1108 | seq_printf(m, "\n"); | |
1109 | c = 0; | |
1110 | } | |
1111 | } | |
1112 | ||
1113 | c = 0; | |
1114 | seq_printf(m, "\n Get Ring:\n"); | |
1115 | seq_printf(m, "\n addr 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n"); | |
1116 | for (i = 0; i < b->m_dwSizeGetRing; i++) { | |
1117 | if (c == 0) | |
1118 | seq_printf(m, " %04x:", i); | |
1119 | ||
065e1477 | 1120 | seq_printf(m, " %02x", readb(b->m_pdwGetRing + i)); |
0b62ceb0 ST |
1121 | |
1122 | if (++c == 16) { | |
1123 | seq_printf(m, "\n"); | |
1124 | c = 0; | |
1125 | } | |
1126 | } | |
1127 | ||
e48836b8 ST |
1128 | mutex_unlock(&b->lock); |
1129 | ||
1130 | } | |
1131 | ||
1132 | return 0; | |
1133 | } | |
1134 | ||
1135 | static int saa7164_proc_open(struct inode *inode, struct file *filp) | |
1136 | { | |
1137 | return single_open(filp, saa7164_proc_show, NULL); | |
1138 | } | |
1139 | ||
bc250684 | 1140 | static const struct file_operations saa7164_proc_fops = { |
e48836b8 ST |
1141 | .open = saa7164_proc_open, |
1142 | .read = seq_read, | |
1143 | .llseek = seq_lseek, | |
1144 | .release = single_release, | |
1145 | }; | |
1146 | ||
1147 | static int saa7164_proc_create(void) | |
1148 | { | |
1149 | struct proc_dir_entry *pe; | |
1150 | ||
1151 | pe = proc_create("saa7164", S_IRUGO, NULL, &saa7164_proc_fops); | |
1152 | if (!pe) | |
1153 | return -ENOMEM; | |
1154 | ||
1155 | return 0; | |
1156 | } | |
1157 | #endif | |
1158 | ||
0b62ceb0 ST |
1159 | static int saa7164_thread_function(void *data) |
1160 | { | |
1161 | struct saa7164_dev *dev = data; | |
4d270cfb | 1162 | struct tmFwInfoStruct fwinfo; |
1247ff5c | 1163 | u64 last_poll_time = 0; |
0b62ceb0 ST |
1164 | |
1165 | dprintk(DBGLVL_THR, "thread started\n"); | |
1166 | ||
1167 | set_freezable(); | |
1168 | ||
1169 | while (1) { | |
1170 | msleep_interruptible(100); | |
1171 | if (kthread_should_stop()) | |
1172 | break; | |
1173 | try_to_freeze(); | |
1174 | ||
1175 | dprintk(DBGLVL_THR, "thread running\n"); | |
1176 | ||
1177 | /* Dump the firmware debug message to console */ | |
1247ff5c ST |
1178 | /* Polling this costs us 1-2% of the arm CPU */ |
1179 | /* convert this into a respnde to interrupt 0x7a */ | |
0b62ceb0 ST |
1180 | saa7164_api_collect_debug(dev); |
1181 | ||
1247ff5c ST |
1182 | /* Monitor CPU load every 1 second */ |
1183 | if ((last_poll_time + 1000 /* ms */) < jiffies_to_msecs(jiffies)) { | |
1184 | saa7164_api_get_load_info(dev, &fwinfo); | |
1185 | last_poll_time = jiffies_to_msecs(jiffies); | |
1186 | } | |
1187 | ||
0b62ceb0 ST |
1188 | } |
1189 | ||
1190 | dprintk(DBGLVL_THR, "thread exiting\n"); | |
1191 | return 0; | |
1192 | } | |
1193 | ||
77978089 BM |
1194 | static bool saa7164_enable_msi(struct pci_dev *pci_dev, struct saa7164_dev *dev) |
1195 | { | |
1196 | int err; | |
1197 | ||
1198 | if (!enable_msi) { | |
1199 | printk(KERN_WARNING "%s() MSI disabled by module parameter 'enable_msi'" | |
1200 | , __func__); | |
1201 | return false; | |
1202 | } | |
1203 | ||
1204 | err = pci_enable_msi(pci_dev); | |
1205 | ||
1206 | if (err) { | |
1207 | printk(KERN_ERR "%s() Failed to enable MSI interrupt." | |
1208 | " Falling back to a shared IRQ\n", __func__); | |
1209 | return false; | |
1210 | } | |
1211 | ||
1212 | /* no error - so request an msi interrupt */ | |
1213 | err = request_irq(pci_dev->irq, saa7164_irq, 0, | |
1214 | dev->name, dev); | |
1215 | ||
1216 | if (err) { | |
1217 | /* fall back to legacy interrupt */ | |
1218 | printk(KERN_ERR "%s() Failed to get an MSI interrupt." | |
1219 | " Falling back to a shared IRQ\n", __func__); | |
1220 | pci_disable_msi(pci_dev); | |
1221 | return false; | |
1222 | } | |
1223 | ||
1224 | return true; | |
1225 | } | |
1226 | ||
4c62e976 GKH |
1227 | static int saa7164_initdev(struct pci_dev *pci_dev, |
1228 | const struct pci_device_id *pci_id) | |
443c1228 ST |
1229 | { |
1230 | struct saa7164_dev *dev; | |
1231 | int err, i; | |
1232 | u32 version; | |
1233 | ||
1234 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | |
1235 | if (NULL == dev) | |
1236 | return -ENOMEM; | |
1237 | ||
fd8d30bf HV |
1238 | err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); |
1239 | if (err < 0) { | |
d66de790 HV |
1240 | dev_err(&pci_dev->dev, "v4l2_device_register failed\n"); |
1241 | goto fail_free; | |
1242 | } | |
1243 | ||
443c1228 ST |
1244 | /* pci init */ |
1245 | dev->pci = pci_dev; | |
1246 | if (pci_enable_device(pci_dev)) { | |
1247 | err = -EIO; | |
1248 | goto fail_free; | |
1249 | } | |
1250 | ||
1251 | if (saa7164_dev_setup(dev) < 0) { | |
1252 | err = -EINVAL; | |
1253 | goto fail_free; | |
1254 | } | |
1255 | ||
1256 | /* print pci info */ | |
abd34d8d | 1257 | dev->pci_rev = pci_dev->revision; |
443c1228 ST |
1258 | pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); |
1259 | printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " | |
1260 | "latency: %d, mmio: 0x%llx\n", dev->name, | |
1261 | pci_name(pci_dev), dev->pci_rev, pci_dev->irq, | |
1262 | dev->pci_lat, | |
1263 | (unsigned long long)pci_resource_start(pci_dev, 0)); | |
1264 | ||
1265 | pci_set_master(pci_dev); | |
1266 | /* TODO */ | |
45fa9c03 | 1267 | if (!pci_set_dma_mask(pci_dev, 0xffffffff)) { |
443c1228 ST |
1268 | printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); |
1269 | err = -EIO; | |
1270 | goto fail_irq; | |
1271 | } | |
1272 | ||
77978089 BM |
1273 | /* irq bit */ |
1274 | if (saa7164_enable_msi(pci_dev, dev)) { | |
1275 | dev->msi = true; | |
1276 | } else { | |
1277 | /* if we have an error (i.e. we don't have an interrupt) | |
1278 | or msi is not enabled - fallback to shared interrupt */ | |
1279 | ||
1280 | err = request_irq(pci_dev->irq, saa7164_irq, | |
1281 | IRQF_SHARED, dev->name, dev); | |
1282 | ||
1283 | if (err < 0) { | |
1284 | printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name, | |
1285 | pci_dev->irq); | |
1286 | err = -EIO; | |
1287 | goto fail_irq; | |
1288 | } | |
443c1228 ST |
1289 | } |
1290 | ||
1291 | pci_set_drvdata(pci_dev, dev); | |
1292 | ||
443c1228 ST |
1293 | /* Init the internal command list */ |
1294 | for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) { | |
1295 | dev->cmds[i].seqno = i; | |
1296 | dev->cmds[i].inuse = 0; | |
1297 | mutex_init(&dev->cmds[i].lock); | |
1298 | init_waitqueue_head(&dev->cmds[i].wait); | |
1299 | } | |
1300 | ||
1301 | /* We need a deferred interrupt handler for cmd handling */ | |
1302 | INIT_WORK(&dev->workcmd, saa7164_work_cmdhandler); | |
1303 | ||
1304 | /* Only load the firmware if we know the board */ | |
1305 | if (dev->board != SAA7164_BOARD_UNKNOWN) { | |
1306 | ||
1307 | err = saa7164_downloadfirmware(dev); | |
1308 | if (err < 0) { | |
1309 | printk(KERN_ERR | |
50bcb4ae ST |
1310 | "Failed to boot firmware, no features " |
1311 | "registered\n"); | |
1312 | goto fail_fw; | |
443c1228 ST |
1313 | } |
1314 | ||
1315 | saa7164_get_descriptors(dev); | |
1316 | saa7164_dumpregs(dev, 0); | |
1317 | saa7164_getcurrentfirmwareversion(dev); | |
1318 | saa7164_getfirmwarestatus(dev); | |
1319 | err = saa7164_bus_setup(dev); | |
1320 | if (err < 0) | |
1321 | printk(KERN_ERR | |
1322 | "Failed to setup the bus, will continue\n"); | |
1323 | saa7164_bus_dump(dev); | |
1324 | ||
1325 | /* Ping the running firmware via the command bus and get the | |
1326 | * firmware version, this checks the bus is running OK. | |
1327 | */ | |
1328 | version = 0; | |
1329 | if (saa7164_api_get_fw_version(dev, &version) == SAA_OK) | |
1330 | dprintk(1, "Bus is operating correctly using " | |
1331 | "version %d.%d.%d.%d (0x%x)\n", | |
1332 | (version & 0x0000fc00) >> 10, | |
1333 | (version & 0x000003e0) >> 5, | |
1334 | (version & 0x0000001f), | |
1335 | (version & 0xffff0000) >> 16, | |
1336 | version); | |
1337 | else | |
1338 | printk(KERN_ERR | |
1339 | "Failed to communicate with the firmware\n"); | |
1340 | ||
1341 | /* Bring up the I2C buses */ | |
1342 | saa7164_i2c_register(&dev->i2c_bus[0]); | |
1343 | saa7164_i2c_register(&dev->i2c_bus[1]); | |
1344 | saa7164_i2c_register(&dev->i2c_bus[2]); | |
1345 | saa7164_gpio_setup(dev); | |
1346 | saa7164_card_setup(dev); | |
1347 | ||
443c1228 ST |
1348 | /* Parse the dynamic device configuration, find various |
1349 | * media endpoints (MPEG, WMV, PS, TS) and cache their | |
1350 | * configuration details into the driver, so we can | |
1351 | * reference them later during simething_register() func, | |
1352 | * interrupt handlers, deferred work handlers etc. | |
1353 | */ | |
1354 | saa7164_api_enum_subdevs(dev); | |
1355 | ||
443c1228 ST |
1356 | /* Begin to create the video sub-systems and register funcs */ |
1357 | if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) { | |
c7e242ba | 1358 | if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS1]) < 0) { |
443c1228 ST |
1359 | printk(KERN_ERR "%s() Failed to register " |
1360 | "dvb adapters on porta\n", | |
1361 | __func__); | |
1362 | } | |
1363 | } | |
1364 | ||
1365 | if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) { | |
c7e242ba | 1366 | if (saa7164_dvb_register(&dev->ports[SAA7164_PORT_TS2]) < 0) { |
443c1228 ST |
1367 | printk(KERN_ERR"%s() Failed to register " |
1368 | "dvb adapters on portb\n", | |
1369 | __func__); | |
1370 | } | |
1371 | } | |
1372 | ||
7615e434 | 1373 | if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) { |
c7e242ba | 1374 | if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC1]) < 0) { |
7615e434 ST |
1375 | printk(KERN_ERR"%s() Failed to register " |
1376 | "mpeg encoder\n", __func__); | |
1377 | } | |
1378 | } | |
1379 | ||
1380 | if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) { | |
c7e242ba | 1381 | if (saa7164_encoder_register(&dev->ports[SAA7164_PORT_ENC2]) < 0) { |
7615e434 ST |
1382 | printk(KERN_ERR"%s() Failed to register " |
1383 | "mpeg encoder\n", __func__); | |
1384 | } | |
1385 | } | |
1386 | ||
e8ce2f21 | 1387 | if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) { |
c7e242ba | 1388 | if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI1]) < 0) { |
e8ce2f21 ST |
1389 | printk(KERN_ERR"%s() Failed to register " |
1390 | "vbi device\n", __func__); | |
1391 | } | |
1392 | } | |
1393 | ||
1394 | if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) { | |
c7e242ba | 1395 | if (saa7164_vbi_register(&dev->ports[SAA7164_PORT_VBI2]) < 0) { |
e8ce2f21 ST |
1396 | printk(KERN_ERR"%s() Failed to register " |
1397 | "vbi device\n", __func__); | |
1398 | } | |
1399 | } | |
e48836b8 | 1400 | saa7164_api_set_debug(dev, fw_debug); |
e8ce2f21 | 1401 | |
0b62ceb0 ST |
1402 | if (fw_debug) { |
1403 | dev->kthread = kthread_run(saa7164_thread_function, dev, | |
1404 | "saa7164 debug"); | |
89f4d45b WY |
1405 | if (IS_ERR(dev->kthread)) { |
1406 | dev->kthread = NULL; | |
0b62ceb0 ST |
1407 | printk(KERN_ERR "%s() Failed to create " |
1408 | "debug kernel thread\n", __func__); | |
89f4d45b | 1409 | } |
0b62ceb0 ST |
1410 | } |
1411 | ||
443c1228 ST |
1412 | } /* != BOARD_UNKNOWN */ |
1413 | else | |
1414 | printk(KERN_ERR "%s() Unsupported board detected, " | |
1415 | "registering without firmware\n", __func__); | |
1416 | ||
b1912a85 | 1417 | dprintk(1, "%s() parameter debug = %d\n", __func__, saa_debug); |
2ceae8fd ST |
1418 | dprintk(1, "%s() parameter waitsecs = %d\n", __func__, waitsecs); |
1419 | ||
50bcb4ae | 1420 | fail_fw: |
443c1228 ST |
1421 | return 0; |
1422 | ||
1423 | fail_irq: | |
1424 | saa7164_dev_unregister(dev); | |
1425 | fail_free: | |
d66de790 | 1426 | v4l2_device_unregister(&dev->v4l2_dev); |
443c1228 ST |
1427 | kfree(dev); |
1428 | return err; | |
1429 | } | |
1430 | ||
1431 | static void saa7164_shutdown(struct saa7164_dev *dev) | |
1432 | { | |
1433 | dprintk(1, "%s()\n", __func__); | |
1434 | } | |
1435 | ||
4c62e976 | 1436 | static void saa7164_finidev(struct pci_dev *pci_dev) |
443c1228 ST |
1437 | { |
1438 | struct saa7164_dev *dev = pci_get_drvdata(pci_dev); | |
1439 | ||
0b62ceb0 ST |
1440 | if (dev->board != SAA7164_BOARD_UNKNOWN) { |
1441 | if (fw_debug && dev->kthread) { | |
1442 | kthread_stop(dev->kthread); | |
1443 | dev->kthread = NULL; | |
1444 | } | |
22760ed3 ST |
1445 | if (dev->firmwareloaded) |
1446 | saa7164_api_set_debug(dev, 0x00); | |
0b62ceb0 | 1447 | } |
e48836b8 | 1448 | |
c7e242ba MCC |
1449 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], |
1450 | &dev->ports[SAA7164_PORT_ENC1].irq_interval); | |
1451 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], | |
1452 | &dev->ports[SAA7164_PORT_ENC1].svc_interval); | |
1453 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], | |
1454 | &dev->ports[SAA7164_PORT_ENC1].irq_svc_interval); | |
1455 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], | |
1456 | &dev->ports[SAA7164_PORT_ENC1].read_interval); | |
1457 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_ENC1], | |
1458 | &dev->ports[SAA7164_PORT_ENC1].poll_interval); | |
1459 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI1], | |
1460 | &dev->ports[SAA7164_PORT_VBI1].read_interval); | |
1461 | saa7164_histogram_print(&dev->ports[SAA7164_PORT_VBI2], | |
1462 | &dev->ports[SAA7164_PORT_VBI2].poll_interval); | |
91d80189 | 1463 | |
443c1228 ST |
1464 | saa7164_shutdown(dev); |
1465 | ||
1466 | if (saa7164_boards[dev->board].porta == SAA7164_MPEG_DVB) | |
c7e242ba | 1467 | saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS1]); |
443c1228 ST |
1468 | |
1469 | if (saa7164_boards[dev->board].portb == SAA7164_MPEG_DVB) | |
c7e242ba | 1470 | saa7164_dvb_unregister(&dev->ports[SAA7164_PORT_TS2]); |
7615e434 ST |
1471 | |
1472 | if (saa7164_boards[dev->board].portc == SAA7164_MPEG_ENCODER) | |
c7e242ba | 1473 | saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC1]); |
7615e434 ST |
1474 | |
1475 | if (saa7164_boards[dev->board].portd == SAA7164_MPEG_ENCODER) | |
c7e242ba | 1476 | saa7164_encoder_unregister(&dev->ports[SAA7164_PORT_ENC2]); |
443c1228 | 1477 | |
e8ce2f21 | 1478 | if (saa7164_boards[dev->board].porte == SAA7164_MPEG_VBI) |
c7e242ba | 1479 | saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI1]); |
e8ce2f21 ST |
1480 | |
1481 | if (saa7164_boards[dev->board].portf == SAA7164_MPEG_VBI) | |
c7e242ba | 1482 | saa7164_vbi_unregister(&dev->ports[SAA7164_PORT_VBI2]); |
e8ce2f21 | 1483 | |
443c1228 ST |
1484 | saa7164_i2c_unregister(&dev->i2c_bus[0]); |
1485 | saa7164_i2c_unregister(&dev->i2c_bus[1]); | |
1486 | saa7164_i2c_unregister(&dev->i2c_bus[2]); | |
1487 | ||
443c1228 ST |
1488 | /* unregister stuff */ |
1489 | free_irq(pci_dev->irq, dev); | |
443c1228 | 1490 | |
77978089 BM |
1491 | if (dev->msi) { |
1492 | pci_disable_msi(pci_dev); | |
1493 | dev->msi = false; | |
1494 | } | |
1495 | ||
3f845f3c OS |
1496 | pci_disable_device(pci_dev); |
1497 | ||
443c1228 ST |
1498 | mutex_lock(&devlist); |
1499 | list_del(&dev->devlist); | |
1500 | mutex_unlock(&devlist); | |
1501 | ||
1502 | saa7164_dev_unregister(dev); | |
d66de790 | 1503 | v4l2_device_unregister(&dev->v4l2_dev); |
443c1228 ST |
1504 | kfree(dev); |
1505 | } | |
1506 | ||
1507 | static struct pci_device_id saa7164_pci_tbl[] = { | |
1508 | { | |
1509 | /* SAA7164 */ | |
1510 | .vendor = 0x1131, | |
1511 | .device = 0x7164, | |
1512 | .subvendor = PCI_ANY_ID, | |
1513 | .subdevice = PCI_ANY_ID, | |
1514 | }, { | |
1515 | /* --- end of list --- */ | |
1516 | } | |
1517 | }; | |
1518 | MODULE_DEVICE_TABLE(pci, saa7164_pci_tbl); | |
1519 | ||
1520 | static struct pci_driver saa7164_pci_driver = { | |
1521 | .name = "saa7164", | |
1522 | .id_table = saa7164_pci_tbl, | |
1523 | .probe = saa7164_initdev, | |
4c62e976 | 1524 | .remove = saa7164_finidev, |
443c1228 ST |
1525 | /* TODO */ |
1526 | .suspend = NULL, | |
1527 | .resume = NULL, | |
1528 | }; | |
1529 | ||
9d440a08 | 1530 | static int __init saa7164_init(void) |
443c1228 ST |
1531 | { |
1532 | printk(KERN_INFO "saa7164 driver loaded\n"); | |
e48836b8 ST |
1533 | |
1534 | #ifdef CONFIG_PROC_FS | |
1535 | saa7164_proc_create(); | |
1536 | #endif | |
443c1228 ST |
1537 | return pci_register_driver(&saa7164_pci_driver); |
1538 | } | |
1539 | ||
9d440a08 | 1540 | static void __exit saa7164_fini(void) |
443c1228 | 1541 | { |
e48836b8 ST |
1542 | #ifdef CONFIG_PROC_FS |
1543 | remove_proc_entry("saa7164", NULL); | |
1544 | #endif | |
443c1228 ST |
1545 | pci_unregister_driver(&saa7164_pci_driver); |
1546 | } | |
1547 | ||
1548 | module_init(saa7164_init); | |
1549 | module_exit(saa7164_fini); | |
1550 |