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