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