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