diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/video/cx88 |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/media/video/cx88')
-rw-r--r-- | drivers/media/video/cx88/Makefile | 11 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-blackbird.c | 911 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-cards.c | 938 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-core.c | 1239 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-dvb.c | 381 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-i2c.c | 213 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-input.c | 396 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-mpeg.c | 466 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-reg.h | 787 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-tvaudio.c | 1032 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-vbi.c | 248 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-video.c | 2277 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88.h | 551 |
13 files changed, 9450 insertions, 0 deletions
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile new file mode 100644 index 000000000000..606d0348da2c --- /dev/null +++ b/drivers/media/video/cx88/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \ | ||
2 | cx88-input.o | ||
3 | cx8800-objs := cx88-video.o cx88-vbi.o | ||
4 | cx8802-objs := cx88-mpeg.o | ||
5 | |||
6 | obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o cx8802.o cx88-blackbird.o | ||
7 | obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o | ||
8 | |||
9 | EXTRA_CFLAGS += -I$(src)/.. | ||
10 | EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/dvb-core | ||
11 | EXTRA_CFLAGS += -I$(srctree)/drivers/media/dvb/frontends | ||
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c new file mode 100644 index 000000000000..46d6778b863b --- /dev/null +++ b/drivers/media/video/cx88/cx88-blackbird.c | |||
@@ -0,0 +1,911 @@ | |||
1 | /* | ||
2 | * $Id: cx88-blackbird.c,v 1.26 2005/03/07 15:58:05 kraxel Exp $ | ||
3 | * | ||
4 | * Support for a cx23416 mpeg encoder via cx2388x host port. | ||
5 | * "blackbird" reference design. | ||
6 | * | ||
7 | * (c) 2004 Jelle Foks <jelle@foks.8m.com> | ||
8 | * (c) 2004 Gerd Knorr <kraxel@bytesex.org> | ||
9 | * | ||
10 | * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/), | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/fs.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/device.h> | ||
33 | #include <linux/firmware.h> | ||
34 | |||
35 | #include "cx88.h" | ||
36 | |||
37 | MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards"); | ||
38 | MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>"); | ||
39 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | |||
42 | static unsigned int mpegbufs = 8; | ||
43 | module_param(mpegbufs,int,0644); | ||
44 | MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32"); | ||
45 | |||
46 | static unsigned int debug = 0; | ||
47 | module_param(debug,int,0644); | ||
48 | MODULE_PARM_DESC(debug,"enable debug messages [blackbird]"); | ||
49 | |||
50 | #define dprintk(level,fmt, arg...) if (debug >= level) \ | ||
51 | printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg) | ||
52 | |||
53 | static LIST_HEAD(cx8802_devlist); | ||
54 | |||
55 | /* ------------------------------------------------------------------ */ | ||
56 | |||
57 | #define BLACKBIRD_FIRM_ENC_FILENAME "blackbird-fw-enc.bin" | ||
58 | #define BLACKBIRD_FIRM_IMAGE_SIZE 256*1024 | ||
59 | |||
60 | /* defines below are from ivtv-driver.h */ | ||
61 | |||
62 | #define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF | ||
63 | |||
64 | /*Firmware API commands*/ | ||
65 | #define IVTV_API_ENC_PING_FW 0x00000080 | ||
66 | #define IVTV_API_ENC_GETVER 0x000000C4 | ||
67 | #define IVTV_API_ENC_HALT_FW 0x000000C3 | ||
68 | #define IVTV_API_STD_TIMEOUT 0x00010000 /*units??*/ | ||
69 | //#define IVTV_API_ASSIGN_PGM_INDEX_INFO 0x000000c7 | ||
70 | #define IVTV_API_ASSIGN_STREAM_TYPE 0x000000b9 | ||
71 | #define IVTV_API_ASSIGN_OUTPUT_PORT 0x000000bb | ||
72 | #define IVTV_API_ASSIGN_FRAMERATE 0x0000008f | ||
73 | #define IVTV_API_ASSIGN_FRAME_SIZE 0x00000091 | ||
74 | #define IVTV_API_ASSIGN_ASPECT_RATIO 0x00000099 | ||
75 | #define IVTV_API_ASSIGN_BITRATES 0x00000095 | ||
76 | #define IVTV_API_ASSIGN_GOP_PROPERTIES 0x00000097 | ||
77 | #define IVTV_API_ASSIGN_3_2_PULLDOWN 0x000000b1 | ||
78 | #define IVTV_API_ASSIGN_GOP_CLOSURE 0x000000c5 | ||
79 | #define IVTV_API_ASSIGN_AUDIO_PROPERTIES 0x000000bd | ||
80 | #define IVTV_API_ASSIGN_DNR_FILTER_MODE 0x0000009b | ||
81 | #define IVTV_API_ASSIGN_DNR_FILTER_PROPS 0x0000009d | ||
82 | #define IVTV_API_ASSIGN_CORING_LEVELS 0x0000009f | ||
83 | #define IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE 0x000000a1 | ||
84 | #define IVTV_API_ASSIGN_FRAME_DROP_RATE 0x000000d0 | ||
85 | #define IVTV_API_ASSIGN_PLACEHOLDER 0x000000d8 | ||
86 | #define IVTV_API_MUTE_VIDEO 0x000000d9 | ||
87 | #define IVTV_API_MUTE_AUDIO 0x000000da | ||
88 | #define IVTV_API_INITIALIZE_INPUT 0x000000cd | ||
89 | #define IVTV_API_REFRESH_INPUT 0x000000d3 | ||
90 | #define IVTV_API_ASSIGN_NUM_VSYNC_LINES 0x000000d6 | ||
91 | #define IVTV_API_BEGIN_CAPTURE 0x00000081 | ||
92 | //#define IVTV_API_PAUSE_ENCODER 0x000000d2 | ||
93 | //#define IVTV_API_EVENT_NOTIFICATION 0x000000d5 | ||
94 | #define IVTV_API_END_CAPTURE 0x00000082 | ||
95 | |||
96 | /* Registers */ | ||
97 | #define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/) | ||
98 | #define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/) | ||
99 | #define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/) | ||
100 | #define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/) | ||
101 | #define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/) | ||
102 | #define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/) | ||
103 | |||
104 | /* ------------------------------------------------------------------ */ | ||
105 | |||
106 | static void host_setup(struct cx88_core *core) | ||
107 | { | ||
108 | /* toggle reset of the host */ | ||
109 | cx_write(MO_GPHST_SOFT_RST, 1); | ||
110 | udelay(100); | ||
111 | cx_write(MO_GPHST_SOFT_RST, 0); | ||
112 | udelay(100); | ||
113 | |||
114 | /* host port setup */ | ||
115 | cx_write(MO_GPHST_WSC, 0x44444444U); | ||
116 | cx_write(MO_GPHST_XFR, 0); | ||
117 | cx_write(MO_GPHST_WDTH, 15); | ||
118 | cx_write(MO_GPHST_HDSHK, 0); | ||
119 | cx_write(MO_GPHST_MUX16, 0x44448888U); | ||
120 | cx_write(MO_GPHST_MODE, 0); | ||
121 | } | ||
122 | |||
123 | /* ------------------------------------------------------------------ */ | ||
124 | |||
125 | #define P1_MDATA0 0x390000 | ||
126 | #define P1_MDATA1 0x390001 | ||
127 | #define P1_MDATA2 0x390002 | ||
128 | #define P1_MDATA3 0x390003 | ||
129 | #define P1_MADDR2 0x390004 | ||
130 | #define P1_MADDR1 0x390005 | ||
131 | #define P1_MADDR0 0x390006 | ||
132 | #define P1_RDATA0 0x390008 | ||
133 | #define P1_RDATA1 0x390009 | ||
134 | #define P1_RDATA2 0x39000A | ||
135 | #define P1_RDATA3 0x39000B | ||
136 | #define P1_RADDR0 0x39000C | ||
137 | #define P1_RADDR1 0x39000D | ||
138 | #define P1_RRDWR 0x39000E | ||
139 | |||
140 | static int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state) | ||
141 | { | ||
142 | unsigned long timeout = jiffies + msecs_to_jiffies(1); | ||
143 | u32 gpio0,need; | ||
144 | |||
145 | need = state ? 2 : 0; | ||
146 | for (;;) { | ||
147 | gpio0 = cx_read(MO_GP0_IO) & 2; | ||
148 | if (need == gpio0) | ||
149 | return 0; | ||
150 | if (time_after(jiffies,timeout)) | ||
151 | return -1; | ||
152 | udelay(1); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | static int memory_write(struct cx88_core *core, u32 address, u32 value) | ||
157 | { | ||
158 | /* Warning: address is dword address (4 bytes) */ | ||
159 | cx_writeb(P1_MDATA0, (unsigned int)value); | ||
160 | cx_writeb(P1_MDATA1, (unsigned int)(value >> 8)); | ||
161 | cx_writeb(P1_MDATA2, (unsigned int)(value >> 16)); | ||
162 | cx_writeb(P1_MDATA3, (unsigned int)(value >> 24)); | ||
163 | cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40); | ||
164 | cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); | ||
165 | cx_writeb(P1_MADDR0, (unsigned int)address); | ||
166 | cx_read(P1_MDATA0); | ||
167 | cx_read(P1_MADDR0); | ||
168 | |||
169 | return wait_ready_gpio0_bit1(core,1); | ||
170 | } | ||
171 | |||
172 | static int memory_read(struct cx88_core *core, u32 address, u32 *value) | ||
173 | { | ||
174 | int retval; | ||
175 | u32 val; | ||
176 | |||
177 | /* Warning: address is dword address (4 bytes) */ | ||
178 | cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0); | ||
179 | cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); | ||
180 | cx_writeb(P1_MADDR0, (unsigned int)address); | ||
181 | cx_read(P1_MADDR0); | ||
182 | |||
183 | retval = wait_ready_gpio0_bit1(core,1); | ||
184 | |||
185 | cx_writeb(P1_MDATA3, 0); | ||
186 | val = (unsigned char)cx_read(P1_MDATA3) << 24; | ||
187 | cx_writeb(P1_MDATA2, 0); | ||
188 | val |= (unsigned char)cx_read(P1_MDATA2) << 16; | ||
189 | cx_writeb(P1_MDATA1, 0); | ||
190 | val |= (unsigned char)cx_read(P1_MDATA1) << 8; | ||
191 | cx_writeb(P1_MDATA0, 0); | ||
192 | val |= (unsigned char)cx_read(P1_MDATA0); | ||
193 | |||
194 | *value = val; | ||
195 | return retval; | ||
196 | } | ||
197 | |||
198 | static int register_write(struct cx88_core *core, u32 address, u32 value) | ||
199 | { | ||
200 | cx_writeb(P1_RDATA0, (unsigned int)value); | ||
201 | cx_writeb(P1_RDATA1, (unsigned int)(value >> 8)); | ||
202 | cx_writeb(P1_RDATA2, (unsigned int)(value >> 16)); | ||
203 | cx_writeb(P1_RDATA3, (unsigned int)(value >> 24)); | ||
204 | cx_writeb(P1_RADDR0, (unsigned int)address); | ||
205 | cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); | ||
206 | cx_writeb(P1_RRDWR, 1); | ||
207 | cx_read(P1_RDATA0); | ||
208 | cx_read(P1_RADDR0); | ||
209 | |||
210 | return wait_ready_gpio0_bit1(core,1); | ||
211 | } | ||
212 | |||
213 | |||
214 | static int register_read(struct cx88_core *core, u32 address, u32 *value) | ||
215 | { | ||
216 | int retval; | ||
217 | u32 val; | ||
218 | |||
219 | cx_writeb(P1_RADDR0, (unsigned int)address); | ||
220 | cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); | ||
221 | cx_writeb(P1_RRDWR, 0); | ||
222 | cx_read(P1_RADDR0); | ||
223 | |||
224 | retval = wait_ready_gpio0_bit1(core,1); | ||
225 | val = (unsigned char)cx_read(P1_RDATA0); | ||
226 | val |= (unsigned char)cx_read(P1_RDATA1) << 8; | ||
227 | val |= (unsigned char)cx_read(P1_RDATA2) << 16; | ||
228 | val |= (unsigned char)cx_read(P1_RDATA3) << 24; | ||
229 | |||
230 | *value = val; | ||
231 | return retval; | ||
232 | } | ||
233 | |||
234 | /* ------------------------------------------------------------------ */ | ||
235 | |||
236 | /* We don't need to call the API often, so using just one mailbox will probably suffice */ | ||
237 | static int blackbird_api_cmd(struct cx8802_dev *dev, u32 command, | ||
238 | u32 inputcnt, u32 outputcnt, ...) | ||
239 | { | ||
240 | unsigned long timeout; | ||
241 | u32 value, flag, retval; | ||
242 | int i; | ||
243 | va_list args; | ||
244 | va_start(args, outputcnt); | ||
245 | |||
246 | dprintk(1,"%s: 0x%X\n", __FUNCTION__, command); | ||
247 | |||
248 | /* this may not be 100% safe if we can't read any memory location | ||
249 | without side effects */ | ||
250 | memory_read(dev->core, dev->mailbox - 4, &value); | ||
251 | if (value != 0x12345678) { | ||
252 | dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n"); | ||
253 | return -1; | ||
254 | } | ||
255 | |||
256 | memory_read(dev->core, dev->mailbox, &flag); | ||
257 | if (flag) { | ||
258 | dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag); | ||
259 | return -1; | ||
260 | } | ||
261 | |||
262 | flag |= 1; /* tell 'em we're working on it */ | ||
263 | memory_write(dev->core, dev->mailbox, flag); | ||
264 | |||
265 | /* write command + args + fill remaining with zeros */ | ||
266 | memory_write(dev->core, dev->mailbox + 1, command); /* command code */ | ||
267 | memory_write(dev->core, dev->mailbox + 3, IVTV_API_STD_TIMEOUT); /* timeout */ | ||
268 | for (i = 0; i < inputcnt ; i++) { | ||
269 | value = va_arg(args, int); | ||
270 | memory_write(dev->core, dev->mailbox + 4 + i, value); | ||
271 | dprintk(1, "API Input %d = %d\n", i, value); | ||
272 | } | ||
273 | for (; i < 16 ; i++) | ||
274 | memory_write(dev->core, dev->mailbox + 4 + i, 0); | ||
275 | |||
276 | flag |= 3; /* tell 'em we're done writing */ | ||
277 | memory_write(dev->core, dev->mailbox, flag); | ||
278 | |||
279 | /* wait for firmware to handle the API command */ | ||
280 | timeout = jiffies + msecs_to_jiffies(10); | ||
281 | for (;;) { | ||
282 | memory_read(dev->core, dev->mailbox, &flag); | ||
283 | if (0 != (flag & 4)) | ||
284 | break; | ||
285 | if (time_after(jiffies,timeout)) { | ||
286 | dprintk(0, "ERROR: API Mailbox timeout\n"); | ||
287 | return -1; | ||
288 | } | ||
289 | udelay(10); | ||
290 | } | ||
291 | |||
292 | /* read output values */ | ||
293 | for (i = 0; i < outputcnt ; i++) { | ||
294 | int *vptr = va_arg(args, int *); | ||
295 | memory_read(dev->core, dev->mailbox + 4 + i, vptr); | ||
296 | dprintk(1, "API Output %d = %d\n", i, *vptr); | ||
297 | } | ||
298 | va_end(args); | ||
299 | |||
300 | memory_read(dev->core, dev->mailbox + 2, &retval); | ||
301 | dprintk(1, "API result = %d\n",retval); | ||
302 | |||
303 | flag = 0; | ||
304 | memory_write(dev->core, dev->mailbox, flag); | ||
305 | return retval; | ||
306 | } | ||
307 | |||
308 | |||
309 | static int blackbird_find_mailbox(struct cx8802_dev *dev) | ||
310 | { | ||
311 | u32 signature[4]={0x12345678, 0x34567812, 0x56781234, 0x78123456}; | ||
312 | int signaturecnt=0; | ||
313 | u32 value; | ||
314 | int i; | ||
315 | |||
316 | for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) { | ||
317 | memory_read(dev->core, i, &value); | ||
318 | if (value == signature[signaturecnt]) | ||
319 | signaturecnt++; | ||
320 | else | ||
321 | signaturecnt = 0; | ||
322 | if (4 == signaturecnt) { | ||
323 | dprintk(1, "Mailbox signature found\n"); | ||
324 | return i+1; | ||
325 | } | ||
326 | } | ||
327 | dprintk(0, "Mailbox signature values not found!\n"); | ||
328 | return -1; | ||
329 | } | ||
330 | |||
331 | static int blackbird_load_firmware(struct cx8802_dev *dev) | ||
332 | { | ||
333 | static const unsigned char magic[8] = { | ||
334 | 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa | ||
335 | }; | ||
336 | const struct firmware *firmware; | ||
337 | int i, retval = 0; | ||
338 | u32 value = 0; | ||
339 | u32 checksum = 0; | ||
340 | u32 *dataptr; | ||
341 | |||
342 | retval = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED); | ||
343 | retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); | ||
344 | retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_REFRESH, 0x80000640); | ||
345 | retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A); | ||
346 | msleep(1); | ||
347 | retval |= register_write(dev->core, IVTV_REG_APU, 0); | ||
348 | |||
349 | if (retval < 0) | ||
350 | dprintk(0, "Error with register_write\n"); | ||
351 | |||
352 | retval = request_firmware(&firmware, BLACKBIRD_FIRM_ENC_FILENAME, | ||
353 | &dev->pci->dev); | ||
354 | if (retval != 0) { | ||
355 | dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n", | ||
356 | BLACKBIRD_FIRM_ENC_FILENAME); | ||
357 | dprintk(0, "Please fix your hotplug setup, the board will " | ||
358 | "not work without firmware loaded!\n"); | ||
359 | return -1; | ||
360 | } | ||
361 | |||
362 | if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) { | ||
363 | dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n", | ||
364 | firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE); | ||
365 | return -1; | ||
366 | } | ||
367 | |||
368 | if (0 != memcmp(firmware->data, magic, 8)) { | ||
369 | dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n"); | ||
370 | return -1; | ||
371 | } | ||
372 | |||
373 | /* transfer to the chip */ | ||
374 | dprintk(1,"Loading firmware ...\n"); | ||
375 | dataptr = (u32*)firmware->data; | ||
376 | for (i = 0; i < (firmware->size >> 2); i++) { | ||
377 | value = *dataptr; | ||
378 | checksum += ~value; | ||
379 | memory_write(dev->core, i, value); | ||
380 | dataptr++; | ||
381 | } | ||
382 | |||
383 | /* read back to verify with the checksum */ | ||
384 | for (i--; i >= 0; i--) { | ||
385 | memory_read(dev->core, i, &value); | ||
386 | checksum -= ~value; | ||
387 | } | ||
388 | if (checksum) { | ||
389 | dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n"); | ||
390 | return -1; | ||
391 | } | ||
392 | release_firmware(firmware); | ||
393 | dprintk(0, "Firmware upload successful.\n"); | ||
394 | |||
395 | retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); | ||
396 | retval |= register_read(dev->core, IVTV_REG_SPU, &value); | ||
397 | retval |= register_write(dev->core, IVTV_REG_SPU, value & 0xFFFFFFFE); | ||
398 | msleep(1); | ||
399 | |||
400 | retval |= register_read(dev->core, IVTV_REG_VPU, &value); | ||
401 | retval |= register_write(dev->core, IVTV_REG_VPU, value & 0xFFFFFFE8); | ||
402 | |||
403 | if (retval < 0) | ||
404 | dprintk(0, "Error with register_write\n"); | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static void blackbird_codec_settings(struct cx8802_dev *dev) | ||
409 | { | ||
410 | int bitrate_mode = 1; | ||
411 | int bitrate = 7500000; | ||
412 | int bitrate_peak = 7500000; | ||
413 | |||
414 | /* assign stream type */ | ||
415 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 0); /* program stream */ | ||
416 | //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 2); /* MPEG1 stream */ | ||
417 | //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 3); /* PES A/V */ | ||
418 | //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 10); /* DVD stream */ | ||
419 | |||
420 | /* assign output port */ | ||
421 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_OUTPUT_PORT, 1, 0, 1); /* 1 = Host */ | ||
422 | |||
423 | /* assign framerate */ | ||
424 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAMERATE, 1, 0, 0); | ||
425 | |||
426 | /* assign frame size */ | ||
427 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAME_SIZE, 2, 0, | ||
428 | dev->height, dev->width); | ||
429 | |||
430 | /* assign aspect ratio */ | ||
431 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_ASPECT_RATIO, 1, 0, 2); | ||
432 | |||
433 | /* assign bitrates */ | ||
434 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_BITRATES, 5, 0, | ||
435 | bitrate_mode, /* mode */ | ||
436 | bitrate, /* bps */ | ||
437 | bitrate_peak / 400, /* peak/400 */ | ||
438 | 0, 0x70); /* encoding buffer, ckennedy */ | ||
439 | |||
440 | /* assign gop properties */ | ||
441 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_PROPERTIES, 2, 0, 15, 3); | ||
442 | //blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_PROPERTIES, 2, 0, 2, 1); | ||
443 | |||
444 | /* assign 3 2 pulldown */ | ||
445 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_3_2_PULLDOWN, 1, 0, 0); | ||
446 | |||
447 | /* note: it's not necessary to set the samplerate, the mpeg encoder seems to autodetect/adjust */ | ||
448 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, (2<<2) | (8<<4)); | ||
449 | |||
450 | /* assign gop closure */ | ||
451 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_CLOSURE, 1, 0, 0); | ||
452 | |||
453 | /* assign audio properties */ | ||
454 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, 0 | (2 << 2) | (14 << 4)); | ||
455 | |||
456 | /* assign dnr filter mode */ | ||
457 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_DNR_FILTER_MODE, 2, 0, 0, 0); | ||
458 | |||
459 | /* assign dnr filter props*/ | ||
460 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_DNR_FILTER_PROPS, 2, 0, 0, 0); | ||
461 | |||
462 | /* assign coring levels (luma_h, luma_l, chroma_h, chroma_l) */ | ||
463 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_CORING_LEVELS, 4, 0, 0, 255, 0, 255); | ||
464 | |||
465 | /* assign spatial filter type: luma_t: 1 = horiz_only, chroma_t: 1 = horiz_only */ | ||
466 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE, 2, 0, 1, 1); | ||
467 | |||
468 | /* assign frame drop rate */ | ||
469 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAME_DROP_RATE, 1, 0, 0); | ||
470 | } | ||
471 | |||
472 | static int blackbird_initialize_codec(struct cx8802_dev *dev) | ||
473 | { | ||
474 | struct cx88_core *core = dev->core; | ||
475 | int version; | ||
476 | int retval; | ||
477 | |||
478 | dprintk(1,"Initialize codec\n"); | ||
479 | retval = blackbird_api_cmd(dev, IVTV_API_ENC_PING_FW, 0, 0); /* ping */ | ||
480 | if (retval < 0) { | ||
481 | /* ping was not successful, reset and upload firmware */ | ||
482 | cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */ | ||
483 | msleep(1); | ||
484 | cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */ | ||
485 | msleep(1); | ||
486 | retval = blackbird_load_firmware(dev); | ||
487 | if (retval < 0) | ||
488 | return retval; | ||
489 | |||
490 | dev->mailbox = blackbird_find_mailbox(dev); | ||
491 | if (dev->mailbox < 0) | ||
492 | return -1; | ||
493 | |||
494 | retval = blackbird_api_cmd(dev, IVTV_API_ENC_PING_FW, 0, 0); /* ping */ | ||
495 | if (retval < 0) { | ||
496 | dprintk(0, "ERROR: Firmware ping failed!\n"); | ||
497 | return -1; | ||
498 | } | ||
499 | |||
500 | retval = blackbird_api_cmd(dev, IVTV_API_ENC_GETVER, 0, 1, &version); | ||
501 | if (retval < 0) { | ||
502 | dprintk(0, "ERROR: Firmware get encoder version failed!\n"); | ||
503 | return -1; | ||
504 | } | ||
505 | dprintk(0, "Firmware version is 0x%08x\n", version); | ||
506 | } | ||
507 | msleep(1); | ||
508 | |||
509 | cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */ | ||
510 | cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */ | ||
511 | cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */ | ||
512 | cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */ | ||
513 | |||
514 | #if 0 /* FIXME */ | ||
515 | set_scale(dev, 720, 480, V4L2_FIELD_INTERLACED); | ||
516 | #endif | ||
517 | blackbird_codec_settings(dev); | ||
518 | msleep(1); | ||
519 | |||
520 | //blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xef, 0xef); | ||
521 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xf0, 0xf0); | ||
522 | //blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0x180, 0x180); | ||
523 | blackbird_api_cmd(dev, IVTV_API_ASSIGN_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); | ||
524 | |||
525 | blackbird_api_cmd(dev, IVTV_API_INITIALIZE_INPUT, 0, 0); /* initialize the video input */ | ||
526 | |||
527 | msleep(1); | ||
528 | |||
529 | blackbird_api_cmd(dev, IVTV_API_MUTE_VIDEO, 1, 0, 0); | ||
530 | msleep(1); | ||
531 | blackbird_api_cmd(dev, IVTV_API_MUTE_AUDIO, 1, 0, 0); | ||
532 | msleep(1); | ||
533 | |||
534 | blackbird_api_cmd(dev, IVTV_API_BEGIN_CAPTURE, 2, 0, 0, 0x13); /* start capturing to the host interface */ | ||
535 | //blackbird_api_cmd(dev, IVTV_API_BEGIN_CAPTURE, 2, 0, 0, 0); /* start capturing to the host interface */ | ||
536 | msleep(1); | ||
537 | |||
538 | blackbird_api_cmd(dev, IVTV_API_REFRESH_INPUT, 0,0); | ||
539 | return 0; | ||
540 | } | ||
541 | |||
542 | /* ------------------------------------------------------------------ */ | ||
543 | |||
544 | static int bb_buf_setup(struct videobuf_queue *q, | ||
545 | unsigned int *count, unsigned int *size) | ||
546 | { | ||
547 | struct cx8802_fh *fh = q->priv_data; | ||
548 | |||
549 | fh->dev->ts_packet_size = 512; | ||
550 | fh->dev->ts_packet_count = 100; | ||
551 | |||
552 | *size = fh->dev->ts_packet_size * fh->dev->ts_packet_count; | ||
553 | if (0 == *count) | ||
554 | *count = mpegbufs; | ||
555 | if (*count < 2) | ||
556 | *count = 2; | ||
557 | if (*count > 32) | ||
558 | *count = 32; | ||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | static int | ||
563 | bb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, | ||
564 | enum v4l2_field field) | ||
565 | { | ||
566 | struct cx8802_fh *fh = q->priv_data; | ||
567 | return cx8802_buf_prepare(fh->dev, (struct cx88_buffer*)vb); | ||
568 | } | ||
569 | |||
570 | static void | ||
571 | bb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
572 | { | ||
573 | struct cx8802_fh *fh = q->priv_data; | ||
574 | cx8802_buf_queue(fh->dev, (struct cx88_buffer*)vb); | ||
575 | } | ||
576 | |||
577 | static void | ||
578 | bb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
579 | { | ||
580 | struct cx8802_fh *fh = q->priv_data; | ||
581 | cx88_free_buffer(fh->dev->pci, (struct cx88_buffer*)vb); | ||
582 | } | ||
583 | |||
584 | static struct videobuf_queue_ops blackbird_qops = { | ||
585 | .buf_setup = bb_buf_setup, | ||
586 | .buf_prepare = bb_buf_prepare, | ||
587 | .buf_queue = bb_buf_queue, | ||
588 | .buf_release = bb_buf_release, | ||
589 | }; | ||
590 | |||
591 | /* ------------------------------------------------------------------ */ | ||
592 | |||
593 | static int mpeg_do_ioctl(struct inode *inode, struct file *file, | ||
594 | unsigned int cmd, void *arg) | ||
595 | { | ||
596 | struct cx8802_fh *fh = file->private_data; | ||
597 | struct cx8802_dev *dev = fh->dev; | ||
598 | |||
599 | if (debug > 1) | ||
600 | cx88_print_ioctl(dev->core->name,cmd); | ||
601 | |||
602 | switch (cmd) { | ||
603 | |||
604 | /* --- capture ioctls ---------------------------------------- */ | ||
605 | case VIDIOC_ENUM_FMT: | ||
606 | { | ||
607 | struct v4l2_fmtdesc *f = arg; | ||
608 | int index; | ||
609 | |||
610 | index = f->index; | ||
611 | if (index != 0) | ||
612 | return -EINVAL; | ||
613 | |||
614 | memset(f,0,sizeof(*f)); | ||
615 | f->index = index; | ||
616 | strlcpy(f->description, "MPEG TS", sizeof(f->description)); | ||
617 | f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
618 | f->pixelformat = V4L2_PIX_FMT_MPEG; | ||
619 | return 0; | ||
620 | } | ||
621 | case VIDIOC_G_FMT: | ||
622 | case VIDIOC_S_FMT: | ||
623 | case VIDIOC_TRY_FMT: | ||
624 | { | ||
625 | /* FIXME -- quick'n'dirty for exactly one size ... */ | ||
626 | struct v4l2_format *f = arg; | ||
627 | |||
628 | memset(f,0,sizeof(*f)); | ||
629 | f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
630 | f->fmt.pix.width = dev->width; | ||
631 | f->fmt.pix.height = dev->height; | ||
632 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; | ||
633 | f->fmt.pix.sizeimage = 1024 * 512 /* FIXME: BUFFER_SIZE */; | ||
634 | } | ||
635 | |||
636 | /* --- streaming capture ------------------------------------- */ | ||
637 | case VIDIOC_REQBUFS: | ||
638 | return videobuf_reqbufs(&fh->mpegq, arg); | ||
639 | |||
640 | case VIDIOC_QUERYBUF: | ||
641 | return videobuf_querybuf(&fh->mpegq, arg); | ||
642 | |||
643 | case VIDIOC_QBUF: | ||
644 | return videobuf_qbuf(&fh->mpegq, arg); | ||
645 | |||
646 | case VIDIOC_DQBUF: | ||
647 | return videobuf_dqbuf(&fh->mpegq, arg, | ||
648 | file->f_flags & O_NONBLOCK); | ||
649 | |||
650 | case VIDIOC_STREAMON: | ||
651 | return videobuf_streamon(&fh->mpegq); | ||
652 | |||
653 | case VIDIOC_STREAMOFF: | ||
654 | return videobuf_streamoff(&fh->mpegq); | ||
655 | |||
656 | default: | ||
657 | return -EINVAL; | ||
658 | } | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static int mpeg_ioctl(struct inode *inode, struct file *file, | ||
663 | unsigned int cmd, unsigned long arg) | ||
664 | { | ||
665 | return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl); | ||
666 | } | ||
667 | |||
668 | static int mpeg_open(struct inode *inode, struct file *file) | ||
669 | { | ||
670 | int minor = iminor(inode); | ||
671 | struct cx8802_dev *h,*dev = NULL; | ||
672 | struct cx8802_fh *fh; | ||
673 | struct list_head *list; | ||
674 | |||
675 | list_for_each(list,&cx8802_devlist) { | ||
676 | h = list_entry(list, struct cx8802_dev, devlist); | ||
677 | if (h->mpeg_dev->minor == minor) | ||
678 | dev = h; | ||
679 | } | ||
680 | if (NULL == dev) | ||
681 | return -ENODEV; | ||
682 | |||
683 | if (blackbird_initialize_codec(dev) < 0) | ||
684 | return -EINVAL; | ||
685 | dprintk(1,"open minor=%d\n",minor); | ||
686 | |||
687 | /* allocate + initialize per filehandle data */ | ||
688 | fh = kmalloc(sizeof(*fh),GFP_KERNEL); | ||
689 | if (NULL == fh) | ||
690 | return -ENOMEM; | ||
691 | memset(fh,0,sizeof(*fh)); | ||
692 | file->private_data = fh; | ||
693 | fh->dev = dev; | ||
694 | |||
695 | /* FIXME: locking against other video device */ | ||
696 | cx88_set_scale(dev->core, dev->width, dev->height, | ||
697 | V4L2_FIELD_INTERLACED); | ||
698 | |||
699 | videobuf_queue_init(&fh->mpegq, &blackbird_qops, | ||
700 | dev->pci, &dev->slock, | ||
701 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
702 | V4L2_FIELD_TOP, | ||
703 | sizeof(struct cx88_buffer), | ||
704 | fh); | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | static int mpeg_release(struct inode *inode, struct file *file) | ||
709 | { | ||
710 | struct cx8802_fh *fh = file->private_data; | ||
711 | |||
712 | blackbird_api_cmd(fh->dev, IVTV_API_END_CAPTURE, 3, 0, 1, 0, 0x13); | ||
713 | |||
714 | /* stop mpeg capture */ | ||
715 | if (fh->mpegq.streaming) | ||
716 | videobuf_streamoff(&fh->mpegq); | ||
717 | if (fh->mpegq.reading) | ||
718 | videobuf_read_stop(&fh->mpegq); | ||
719 | |||
720 | videobuf_mmap_free(&fh->mpegq); | ||
721 | file->private_data = NULL; | ||
722 | kfree(fh); | ||
723 | return 0; | ||
724 | } | ||
725 | |||
726 | static ssize_t | ||
727 | mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | ||
728 | { | ||
729 | struct cx8802_fh *fh = file->private_data; | ||
730 | |||
731 | return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0, | ||
732 | file->f_flags & O_NONBLOCK); | ||
733 | } | ||
734 | |||
735 | static unsigned int | ||
736 | mpeg_poll(struct file *file, struct poll_table_struct *wait) | ||
737 | { | ||
738 | struct cx8802_fh *fh = file->private_data; | ||
739 | |||
740 | return videobuf_poll_stream(file, &fh->mpegq, wait); | ||
741 | } | ||
742 | |||
743 | static int | ||
744 | mpeg_mmap(struct file *file, struct vm_area_struct * vma) | ||
745 | { | ||
746 | struct cx8802_fh *fh = file->private_data; | ||
747 | |||
748 | return videobuf_mmap_mapper(&fh->mpegq, vma); | ||
749 | } | ||
750 | |||
751 | static struct file_operations mpeg_fops = | ||
752 | { | ||
753 | .owner = THIS_MODULE, | ||
754 | .open = mpeg_open, | ||
755 | .release = mpeg_release, | ||
756 | .read = mpeg_read, | ||
757 | .poll = mpeg_poll, | ||
758 | .mmap = mpeg_mmap, | ||
759 | .ioctl = mpeg_ioctl, | ||
760 | .llseek = no_llseek, | ||
761 | }; | ||
762 | |||
763 | static struct video_device cx8802_mpeg_template = | ||
764 | { | ||
765 | .name = "cx8802", | ||
766 | .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES|VID_TYPE_MPEG_ENCODER, | ||
767 | .hardware = 0, | ||
768 | .fops = &mpeg_fops, | ||
769 | .minor = -1, | ||
770 | }; | ||
771 | |||
772 | /* ------------------------------------------------------------------ */ | ||
773 | |||
774 | static void blackbird_unregister_video(struct cx8802_dev *dev) | ||
775 | { | ||
776 | if (dev->mpeg_dev) { | ||
777 | if (-1 != dev->mpeg_dev->minor) | ||
778 | video_unregister_device(dev->mpeg_dev); | ||
779 | else | ||
780 | video_device_release(dev->mpeg_dev); | ||
781 | dev->mpeg_dev = NULL; | ||
782 | } | ||
783 | } | ||
784 | |||
785 | static int blackbird_register_video(struct cx8802_dev *dev) | ||
786 | { | ||
787 | int err; | ||
788 | |||
789 | dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci, | ||
790 | &cx8802_mpeg_template,"mpeg"); | ||
791 | err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1); | ||
792 | if (err < 0) { | ||
793 | printk(KERN_INFO "%s/2: can't register mpeg device\n", | ||
794 | dev->core->name); | ||
795 | return err; | ||
796 | } | ||
797 | printk(KERN_INFO "%s/2: registered device video%d [mpeg]\n", | ||
798 | dev->core->name,dev->mpeg_dev->minor & 0x1f); | ||
799 | return 0; | ||
800 | } | ||
801 | |||
802 | /* ----------------------------------------------------------- */ | ||
803 | |||
804 | static int __devinit blackbird_probe(struct pci_dev *pci_dev, | ||
805 | const struct pci_device_id *pci_id) | ||
806 | { | ||
807 | struct cx8802_dev *dev; | ||
808 | struct cx88_core *core; | ||
809 | int err; | ||
810 | |||
811 | /* general setup */ | ||
812 | core = cx88_core_get(pci_dev); | ||
813 | if (NULL == core) | ||
814 | return -EINVAL; | ||
815 | |||
816 | err = -ENODEV; | ||
817 | if (!cx88_boards[core->board].blackbird) | ||
818 | goto fail_core; | ||
819 | |||
820 | err = -ENOMEM; | ||
821 | dev = kmalloc(sizeof(*dev),GFP_KERNEL); | ||
822 | if (NULL == dev) | ||
823 | goto fail_core; | ||
824 | memset(dev,0,sizeof(*dev)); | ||
825 | dev->pci = pci_dev; | ||
826 | dev->core = core; | ||
827 | dev->width = 720; | ||
828 | dev->height = 480; | ||
829 | |||
830 | err = cx8802_init_common(dev); | ||
831 | if (0 != err) | ||
832 | goto fail_free; | ||
833 | |||
834 | /* blackbird stuff */ | ||
835 | printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n", | ||
836 | core->name); | ||
837 | host_setup(dev->core); | ||
838 | |||
839 | list_add_tail(&dev->devlist,&cx8802_devlist); | ||
840 | blackbird_register_video(dev); | ||
841 | return 0; | ||
842 | |||
843 | fail_free: | ||
844 | kfree(dev); | ||
845 | fail_core: | ||
846 | cx88_core_put(core,pci_dev); | ||
847 | return err; | ||
848 | } | ||
849 | |||
850 | static void __devexit blackbird_remove(struct pci_dev *pci_dev) | ||
851 | { | ||
852 | struct cx8802_dev *dev = pci_get_drvdata(pci_dev); | ||
853 | |||
854 | /* blackbird */ | ||
855 | blackbird_unregister_video(dev); | ||
856 | list_del(&dev->devlist); | ||
857 | |||
858 | /* common */ | ||
859 | cx8802_fini_common(dev); | ||
860 | cx88_core_put(dev->core,dev->pci); | ||
861 | kfree(dev); | ||
862 | } | ||
863 | |||
864 | static struct pci_device_id cx8802_pci_tbl[] = { | ||
865 | { | ||
866 | .vendor = 0x14f1, | ||
867 | .device = 0x8802, | ||
868 | .subvendor = PCI_ANY_ID, | ||
869 | .subdevice = PCI_ANY_ID, | ||
870 | },{ | ||
871 | /* --- end of list --- */ | ||
872 | } | ||
873 | }; | ||
874 | MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); | ||
875 | |||
876 | static struct pci_driver blackbird_pci_driver = { | ||
877 | .name = "cx88-blackbird", | ||
878 | .id_table = cx8802_pci_tbl, | ||
879 | .probe = blackbird_probe, | ||
880 | .remove = __devexit_p(blackbird_remove), | ||
881 | .suspend = cx8802_suspend_common, | ||
882 | .resume = cx8802_resume_common, | ||
883 | }; | ||
884 | |||
885 | static int blackbird_init(void) | ||
886 | { | ||
887 | printk(KERN_INFO "cx2388x blackbird driver version %d.%d.%d loaded\n", | ||
888 | (CX88_VERSION_CODE >> 16) & 0xff, | ||
889 | (CX88_VERSION_CODE >> 8) & 0xff, | ||
890 | CX88_VERSION_CODE & 0xff); | ||
891 | #ifdef SNAPSHOT | ||
892 | printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", | ||
893 | SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); | ||
894 | #endif | ||
895 | return pci_register_driver(&blackbird_pci_driver); | ||
896 | } | ||
897 | |||
898 | static void blackbird_fini(void) | ||
899 | { | ||
900 | pci_unregister_driver(&blackbird_pci_driver); | ||
901 | } | ||
902 | |||
903 | module_init(blackbird_init); | ||
904 | module_exit(blackbird_fini); | ||
905 | |||
906 | /* ----------------------------------------------------------- */ | ||
907 | /* | ||
908 | * Local variables: | ||
909 | * c-basic-offset: 8 | ||
910 | * End: | ||
911 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c new file mode 100644 index 000000000000..367624822d77 --- /dev/null +++ b/drivers/media/video/cx88/cx88-cards.c | |||
@@ -0,0 +1,938 @@ | |||
1 | /* | ||
2 | * $Id: cx88-cards.c,v 1.66 2005/03/04 09:12:23 kraxel Exp $ | ||
3 | * | ||
4 | * device driver for Conexant 2388x based TV cards | ||
5 | * card-specific stuff. | ||
6 | * | ||
7 | * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/init.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/pci.h> | ||
27 | #include <linux/delay.h> | ||
28 | |||
29 | #include "cx88.h" | ||
30 | |||
31 | /* ------------------------------------------------------------------ */ | ||
32 | /* board config info */ | ||
33 | |||
34 | struct cx88_board cx88_boards[] = { | ||
35 | [CX88_BOARD_UNKNOWN] = { | ||
36 | .name = "UNKNOWN/GENERIC", | ||
37 | .tuner_type = UNSET, | ||
38 | .input = {{ | ||
39 | .type = CX88_VMUX_COMPOSITE1, | ||
40 | .vmux = 0, | ||
41 | },{ | ||
42 | .type = CX88_VMUX_COMPOSITE2, | ||
43 | .vmux = 1, | ||
44 | },{ | ||
45 | .type = CX88_VMUX_COMPOSITE3, | ||
46 | .vmux = 2, | ||
47 | },{ | ||
48 | .type = CX88_VMUX_COMPOSITE4, | ||
49 | .vmux = 3, | ||
50 | }}, | ||
51 | }, | ||
52 | [CX88_BOARD_HAUPPAUGE] = { | ||
53 | .name = "Hauppauge WinTV 34xxx models", | ||
54 | .tuner_type = UNSET, | ||
55 | .tda9887_conf = TDA9887_PRESENT, | ||
56 | .input = {{ | ||
57 | .type = CX88_VMUX_TELEVISION, | ||
58 | .vmux = 0, | ||
59 | .gpio0 = 0xff00, // internal decoder | ||
60 | },{ | ||
61 | .type = CX88_VMUX_DEBUG, | ||
62 | .vmux = 0, | ||
63 | .gpio0 = 0xff01, // mono from tuner chip | ||
64 | },{ | ||
65 | .type = CX88_VMUX_COMPOSITE1, | ||
66 | .vmux = 1, | ||
67 | .gpio0 = 0xff02, | ||
68 | },{ | ||
69 | .type = CX88_VMUX_SVIDEO, | ||
70 | .vmux = 2, | ||
71 | .gpio0 = 0xff02, | ||
72 | }}, | ||
73 | .radio = { | ||
74 | .type = CX88_RADIO, | ||
75 | .gpio0 = 0xff01, | ||
76 | }, | ||
77 | }, | ||
78 | [CX88_BOARD_GDI] = { | ||
79 | .name = "GDI Black Gold", | ||
80 | .tuner_type = UNSET, | ||
81 | .input = {{ | ||
82 | .type = CX88_VMUX_TELEVISION, | ||
83 | .vmux = 0, | ||
84 | }}, | ||
85 | }, | ||
86 | [CX88_BOARD_PIXELVIEW] = { | ||
87 | .name = "PixelView", | ||
88 | .tuner_type = 5, | ||
89 | .input = {{ | ||
90 | .type = CX88_VMUX_TELEVISION, | ||
91 | .vmux = 0, | ||
92 | .gpio0 = 0xff00, // internal decoder | ||
93 | },{ | ||
94 | .type = CX88_VMUX_COMPOSITE1, | ||
95 | .vmux = 1, | ||
96 | },{ | ||
97 | .type = CX88_VMUX_SVIDEO, | ||
98 | .vmux = 2, | ||
99 | }}, | ||
100 | .radio = { | ||
101 | .type = CX88_RADIO, | ||
102 | .gpio0 = 0xff10, | ||
103 | }, | ||
104 | }, | ||
105 | [CX88_BOARD_ATI_WONDER_PRO] = { | ||
106 | .name = "ATI TV Wonder Pro", | ||
107 | .tuner_type = 44, | ||
108 | .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER, | ||
109 | .input = {{ | ||
110 | .type = CX88_VMUX_TELEVISION, | ||
111 | .vmux = 0, | ||
112 | .gpio0 = 0x03ff, | ||
113 | },{ | ||
114 | .type = CX88_VMUX_COMPOSITE1, | ||
115 | .vmux = 1, | ||
116 | .gpio0 = 0x03fe, | ||
117 | },{ | ||
118 | .type = CX88_VMUX_SVIDEO, | ||
119 | .vmux = 2, | ||
120 | .gpio0 = 0x03fe, | ||
121 | }}, | ||
122 | }, | ||
123 | [CX88_BOARD_WINFAST2000XP_EXPERT] = { | ||
124 | .name = "Leadtek Winfast 2000XP Expert", | ||
125 | .tuner_type = 44, | ||
126 | .tda9887_conf = TDA9887_PRESENT, | ||
127 | .input = {{ | ||
128 | .type = CX88_VMUX_TELEVISION, | ||
129 | .vmux = 0, | ||
130 | .gpio0 = 0x00F5e700, | ||
131 | .gpio1 = 0x00003004, | ||
132 | .gpio2 = 0x00F5e700, | ||
133 | .gpio3 = 0x02000000, | ||
134 | },{ | ||
135 | .type = CX88_VMUX_COMPOSITE1, | ||
136 | .vmux = 1, | ||
137 | .gpio0 = 0x00F5c700, | ||
138 | .gpio1 = 0x00003004, | ||
139 | .gpio2 = 0x00F5c700, | ||
140 | .gpio3 = 0x02000000, | ||
141 | },{ | ||
142 | .type = CX88_VMUX_SVIDEO, | ||
143 | .vmux = 2, | ||
144 | .gpio0 = 0x00F5c700, | ||
145 | .gpio1 = 0x00003004, | ||
146 | .gpio2 = 0x00F5c700, | ||
147 | .gpio3 = 0x02000000, | ||
148 | }}, | ||
149 | .radio = { | ||
150 | .type = CX88_RADIO, | ||
151 | .gpio0 = 0x00F5d700, | ||
152 | .gpio1 = 0x00003004, | ||
153 | .gpio2 = 0x00F5d700, | ||
154 | .gpio3 = 0x02000000, | ||
155 | }, | ||
156 | }, | ||
157 | [CX88_BOARD_AVERTV_303] = { | ||
158 | .name = "AverTV Studio 303 (M126)", | ||
159 | .tuner_type = 38, | ||
160 | .tda9887_conf = TDA9887_PRESENT, | ||
161 | .input = {{ | ||
162 | .type = CX88_VMUX_TELEVISION, | ||
163 | .vmux = 0, | ||
164 | .gpio1 = 0x309f, | ||
165 | },{ | ||
166 | .type = CX88_VMUX_COMPOSITE1, | ||
167 | .vmux = 1, | ||
168 | .gpio1 = 0x305f, | ||
169 | },{ | ||
170 | .type = CX88_VMUX_SVIDEO, | ||
171 | .vmux = 2, | ||
172 | .gpio1 = 0x305f, | ||
173 | }}, | ||
174 | .radio = { | ||
175 | .type = CX88_RADIO, | ||
176 | }, | ||
177 | }, | ||
178 | [CX88_BOARD_MSI_TVANYWHERE_MASTER] = { | ||
179 | // added gpio values thanks to Michal | ||
180 | // values for PAL from DScaler | ||
181 | .name = "MSI TV-@nywhere Master", | ||
182 | .tuner_type = 33, | ||
183 | .tda9887_conf = TDA9887_PRESENT, | ||
184 | .input = {{ | ||
185 | .type = CX88_VMUX_TELEVISION, | ||
186 | .vmux = 0, | ||
187 | .gpio0 = 0x000040bf, | ||
188 | .gpio1 = 0x000080c0, | ||
189 | .gpio2 = 0x0000ff40, | ||
190 | },{ | ||
191 | .type = CX88_VMUX_COMPOSITE1, | ||
192 | .vmux = 1, | ||
193 | .gpio0 = 0x000040bf, | ||
194 | .gpio1 = 0x000080c0, | ||
195 | .gpio2 = 0x0000ff40, | ||
196 | },{ | ||
197 | .type = CX88_VMUX_SVIDEO, | ||
198 | .vmux = 2, | ||
199 | .gpio0 = 0x000040bf, | ||
200 | .gpio1 = 0x000080c0, | ||
201 | .gpio2 = 0x0000ff40, | ||
202 | }}, | ||
203 | .radio = { | ||
204 | .type = CX88_RADIO, | ||
205 | }, | ||
206 | }, | ||
207 | [CX88_BOARD_WINFAST_DV2000] = { | ||
208 | .name = "Leadtek Winfast DV2000", | ||
209 | .tuner_type = 38, | ||
210 | .tda9887_conf = TDA9887_PRESENT, | ||
211 | .input = {{ | ||
212 | .type = CX88_VMUX_TELEVISION, | ||
213 | .vmux = 0, | ||
214 | .gpio0 = 0x0035e700, | ||
215 | .gpio1 = 0x00003004, | ||
216 | .gpio2 = 0x0035e700, | ||
217 | .gpio3 = 0x02000000, | ||
218 | },{ | ||
219 | |||
220 | .type = CX88_VMUX_COMPOSITE1, | ||
221 | .vmux = 1, | ||
222 | .gpio0 = 0x0035c700, | ||
223 | .gpio1 = 0x00003004, | ||
224 | .gpio2 = 0x0035c700, | ||
225 | .gpio3 = 0x02000000, | ||
226 | },{ | ||
227 | .type = CX88_VMUX_SVIDEO, | ||
228 | .vmux = 2, | ||
229 | .gpio0 = 0x0035c700, | ||
230 | .gpio1 = 0x0035c700, | ||
231 | .gpio2 = 0x02000000, | ||
232 | .gpio3 = 0x02000000, | ||
233 | }}, | ||
234 | .radio = { | ||
235 | .type = CX88_RADIO, | ||
236 | .gpio0 = 0x0035d700, | ||
237 | .gpio1 = 0x00007004, | ||
238 | .gpio2 = 0x0035d700, | ||
239 | .gpio3 = 0x02000000, | ||
240 | }, | ||
241 | }, | ||
242 | [CX88_BOARD_LEADTEK_PVR2000] = { | ||
243 | // gpio values for PAL version from regspy by DScaler | ||
244 | .name = "Leadtek PVR 2000", | ||
245 | .tuner_type = 38, | ||
246 | .tda9887_conf = TDA9887_PRESENT, | ||
247 | .input = {{ | ||
248 | .type = CX88_VMUX_TELEVISION, | ||
249 | .vmux = 0, | ||
250 | .gpio0 = 0x0000bde6, | ||
251 | },{ | ||
252 | .type = CX88_VMUX_COMPOSITE1, | ||
253 | .vmux = 1, | ||
254 | .gpio0 = 0x0000bde6, | ||
255 | },{ | ||
256 | .type = CX88_VMUX_SVIDEO, | ||
257 | .vmux = 2, | ||
258 | .gpio0 = 0x0000bde6, | ||
259 | }}, | ||
260 | .radio = { | ||
261 | .type = CX88_RADIO, | ||
262 | .gpio0 = 0x0000bd62, | ||
263 | }, | ||
264 | .blackbird = 1, | ||
265 | }, | ||
266 | [CX88_BOARD_IODATA_GVVCP3PCI] = { | ||
267 | .name = "IODATA GV-VCP3/PCI", | ||
268 | .tuner_type = TUNER_ABSENT, | ||
269 | .input = {{ | ||
270 | .type = CX88_VMUX_COMPOSITE1, | ||
271 | .vmux = 0, | ||
272 | },{ | ||
273 | .type = CX88_VMUX_COMPOSITE2, | ||
274 | .vmux = 1, | ||
275 | },{ | ||
276 | .type = CX88_VMUX_SVIDEO, | ||
277 | .vmux = 2, | ||
278 | }}, | ||
279 | }, | ||
280 | [CX88_BOARD_PROLINK_PLAYTVPVR] = { | ||
281 | .name = "Prolink PlayTV PVR", | ||
282 | .tuner_type = 43, | ||
283 | .tda9887_conf = TDA9887_PRESENT, | ||
284 | .input = {{ | ||
285 | .type = CX88_VMUX_TELEVISION, | ||
286 | .vmux = 0, | ||
287 | .gpio0 = 0xff00, | ||
288 | },{ | ||
289 | .type = CX88_VMUX_COMPOSITE1, | ||
290 | .vmux = 1, | ||
291 | .gpio0 = 0xff03, | ||
292 | },{ | ||
293 | .type = CX88_VMUX_SVIDEO, | ||
294 | .vmux = 2, | ||
295 | .gpio0 = 0xff03, | ||
296 | }}, | ||
297 | .radio = { | ||
298 | .type = CX88_RADIO, | ||
299 | .gpio0 = 0xff00, | ||
300 | }, | ||
301 | }, | ||
302 | [CX88_BOARD_ASUS_PVR_416] = { | ||
303 | .name = "ASUS PVR-416", | ||
304 | .tuner_type = 43, | ||
305 | .tda9887_conf = TDA9887_PRESENT, | ||
306 | .input = {{ | ||
307 | .type = CX88_VMUX_TELEVISION, | ||
308 | .vmux = 0, | ||
309 | .gpio0 = 0x0000fde6, | ||
310 | },{ | ||
311 | .type = CX88_VMUX_SVIDEO, | ||
312 | .vmux = 2, | ||
313 | .gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in? | ||
314 | }}, | ||
315 | .radio = { | ||
316 | .type = CX88_RADIO, | ||
317 | .gpio0 = 0x0000fde2, | ||
318 | }, | ||
319 | .blackbird = 1, | ||
320 | }, | ||
321 | [CX88_BOARD_MSI_TVANYWHERE] = { | ||
322 | .name = "MSI TV-@nywhere", | ||
323 | .tuner_type = 33, | ||
324 | .tda9887_conf = TDA9887_PRESENT, | ||
325 | .input = {{ | ||
326 | .type = CX88_VMUX_TELEVISION, | ||
327 | .vmux = 0, | ||
328 | .gpio0 = 0x00000fbf, | ||
329 | .gpio2 = 0x0000fc08, | ||
330 | },{ | ||
331 | .type = CX88_VMUX_COMPOSITE1, | ||
332 | .vmux = 1, | ||
333 | .gpio0 = 0x00000fbf, | ||
334 | .gpio2 = 0x0000fc68, | ||
335 | },{ | ||
336 | .type = CX88_VMUX_SVIDEO, | ||
337 | .vmux = 2, | ||
338 | .gpio0 = 0x00000fbf, | ||
339 | .gpio2 = 0x0000fc68, | ||
340 | }}, | ||
341 | }, | ||
342 | [CX88_BOARD_KWORLD_DVB_T] = { | ||
343 | .name = "KWorld/VStream XPert DVB-T", | ||
344 | .tuner_type = TUNER_ABSENT, | ||
345 | .input = {{ | ||
346 | .type = CX88_VMUX_COMPOSITE1, | ||
347 | .vmux = 1, | ||
348 | .gpio0 = 0x0700, | ||
349 | .gpio2 = 0x0101, | ||
350 | },{ | ||
351 | .type = CX88_VMUX_SVIDEO, | ||
352 | .vmux = 2, | ||
353 | .gpio0 = 0x0700, | ||
354 | .gpio2 = 0x0101, | ||
355 | }}, | ||
356 | .dvb = 1, | ||
357 | }, | ||
358 | [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = { | ||
359 | .name = "DVICO FusionHDTV DVB-T1", | ||
360 | .tuner_type = TUNER_ABSENT, /* No analog tuner */ | ||
361 | .input = {{ | ||
362 | .type = CX88_VMUX_COMPOSITE1, | ||
363 | .vmux = 1, | ||
364 | .gpio0 = 0x000027df, | ||
365 | },{ | ||
366 | .type = CX88_VMUX_SVIDEO, | ||
367 | .vmux = 2, | ||
368 | .gpio0 = 0x000027df, | ||
369 | }}, | ||
370 | .dvb = 1, | ||
371 | }, | ||
372 | [CX88_BOARD_KWORLD_LTV883] = { | ||
373 | .name = "KWorld LTV883RF", | ||
374 | .tuner_type = 48, | ||
375 | .input = {{ | ||
376 | .type = CX88_VMUX_TELEVISION, | ||
377 | .vmux = 0, | ||
378 | .gpio0 = 0x07f8, | ||
379 | },{ | ||
380 | .type = CX88_VMUX_DEBUG, | ||
381 | .vmux = 0, | ||
382 | .gpio0 = 0x07f9, // mono from tuner chip | ||
383 | },{ | ||
384 | .type = CX88_VMUX_COMPOSITE1, | ||
385 | .vmux = 1, | ||
386 | .gpio0 = 0x000007fa, | ||
387 | },{ | ||
388 | .type = CX88_VMUX_SVIDEO, | ||
389 | .vmux = 2, | ||
390 | .gpio0 = 0x000007fa, | ||
391 | }}, | ||
392 | .radio = { | ||
393 | .type = CX88_RADIO, | ||
394 | .gpio0 = 0x000007f8, | ||
395 | }, | ||
396 | }, | ||
397 | [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD] = { | ||
398 | .name = "DViCO - FusionHDTV 3 Gold", | ||
399 | .tuner_type = TUNER_MICROTUNE_4042FI5, | ||
400 | /* | ||
401 | GPIO[0] resets DT3302 DTV receiver | ||
402 | 0 - reset asserted | ||
403 | 1 - normal operation | ||
404 | GPIO[1] mutes analog audio output connector | ||
405 | 0 - enable selected source | ||
406 | 1 - mute | ||
407 | GPIO[2] selects source for analog audio output connector | ||
408 | 0 - analog audio input connector on tab | ||
409 | 1 - analog DAC output from CX23881 chip | ||
410 | GPIO[3] selects RF input connector on tuner module | ||
411 | 0 - RF connector labeled CABLE | ||
412 | 1 - RF connector labeled ANT | ||
413 | */ | ||
414 | .input = {{ | ||
415 | .type = CX88_VMUX_TELEVISION, | ||
416 | .vmux = 0, | ||
417 | .gpio0 = 0x0f0d, | ||
418 | },{ | ||
419 | .type = CX88_VMUX_CABLE, | ||
420 | .vmux = 0, | ||
421 | .gpio0 = 0x0f05, | ||
422 | },{ | ||
423 | .type = CX88_VMUX_COMPOSITE1, | ||
424 | .vmux = 1, | ||
425 | .gpio0 = 0x0f00, | ||
426 | },{ | ||
427 | .type = CX88_VMUX_SVIDEO, | ||
428 | .vmux = 2, | ||
429 | .gpio0 = 0x0f00, | ||
430 | }}, | ||
431 | #if 0 | ||
432 | .ts = { | ||
433 | .type = CX88_TS, | ||
434 | .gpio0 = 0x00000f01, /* Hooked to tuner reset bit */ | ||
435 | } | ||
436 | #endif | ||
437 | }, | ||
438 | [CX88_BOARD_HAUPPAUGE_DVB_T1] = { | ||
439 | .name = "Hauppauge Nova-T DVB-T", | ||
440 | .tuner_type = TUNER_ABSENT, | ||
441 | .input = {{ | ||
442 | .type = CX88_VMUX_DVB, | ||
443 | .vmux = 0, | ||
444 | }}, | ||
445 | .dvb = 1, | ||
446 | }, | ||
447 | [CX88_BOARD_CONEXANT_DVB_T1] = { | ||
448 | .name = "Conexant DVB-T reference design", | ||
449 | .tuner_type = TUNER_ABSENT, | ||
450 | .input = {{ | ||
451 | .type = CX88_VMUX_DVB, | ||
452 | .vmux = 0, | ||
453 | }}, | ||
454 | .dvb = 1, | ||
455 | }, | ||
456 | [CX88_BOARD_PROVIDEO_PV259] = { | ||
457 | .name = "Provideo PV259", | ||
458 | .tuner_type = TUNER_PHILIPS_FQ1216ME, | ||
459 | .input = {{ | ||
460 | .type = CX88_VMUX_TELEVISION, | ||
461 | .vmux = 0, | ||
462 | }}, | ||
463 | .blackbird = 1, | ||
464 | }, | ||
465 | [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = { | ||
466 | .name = "DVICO FusionHDTV DVB-T Plus", | ||
467 | .tuner_type = TUNER_ABSENT, /* No analog tuner */ | ||
468 | .input = {{ | ||
469 | .type = CX88_VMUX_COMPOSITE1, | ||
470 | .vmux = 1, | ||
471 | .gpio0 = 0x000027df, | ||
472 | },{ | ||
473 | .type = CX88_VMUX_SVIDEO, | ||
474 | .vmux = 2, | ||
475 | .gpio0 = 0x000027df, | ||
476 | }}, | ||
477 | .dvb = 1, | ||
478 | }, | ||
479 | [CX88_BOARD_DNTV_LIVE_DVB_T] = { | ||
480 | .name = "digitalnow DNTV Live! DVB-T", | ||
481 | .tuner_type = TUNER_ABSENT, | ||
482 | .input = {{ | ||
483 | .type = CX88_VMUX_COMPOSITE1, | ||
484 | .vmux = 1, | ||
485 | .gpio0 = 0x00000700, | ||
486 | .gpio2 = 0x00000101, | ||
487 | },{ | ||
488 | .type = CX88_VMUX_SVIDEO, | ||
489 | .vmux = 2, | ||
490 | .gpio0 = 0x00000700, | ||
491 | .gpio2 = 0x00000101, | ||
492 | }}, | ||
493 | .dvb = 1, | ||
494 | }, | ||
495 | [CX88_BOARD_PCHDTV_HD3000] = { | ||
496 | .name = "pcHDTV HD3000 HDTV", | ||
497 | .tuner_type = TUNER_THOMSON_DTT7610, | ||
498 | .input = {{ | ||
499 | .type = CX88_VMUX_TELEVISION, | ||
500 | .vmux = 0, | ||
501 | .gpio0 = 0x00008484, | ||
502 | .gpio1 = 0x00000000, | ||
503 | .gpio2 = 0x00000000, | ||
504 | .gpio3 = 0x00000000, | ||
505 | },{ | ||
506 | .type = CX88_VMUX_COMPOSITE1, | ||
507 | .vmux = 1, | ||
508 | .gpio0 = 0x00008400, | ||
509 | .gpio1 = 0x00000000, | ||
510 | .gpio2 = 0x00000000, | ||
511 | .gpio3 = 0x00000000, | ||
512 | },{ | ||
513 | .type = CX88_VMUX_SVIDEO, | ||
514 | .vmux = 2, | ||
515 | .gpio0 = 0x00008400, | ||
516 | .gpio1 = 0x00000000, | ||
517 | .gpio2 = 0x00000000, | ||
518 | .gpio3 = 0x00000000, | ||
519 | }}, | ||
520 | .radio = { | ||
521 | .type = CX88_RADIO, | ||
522 | .vmux = 2, | ||
523 | .gpio0 = 0x00008400, | ||
524 | .gpio1 = 0x00000000, | ||
525 | .gpio2 = 0x00000000, | ||
526 | .gpio3 = 0x00000000, | ||
527 | }, | ||
528 | .dvb = 1, | ||
529 | }, | ||
530 | [CX88_BOARD_HAUPPAUGE_ROSLYN] = { | ||
531 | // entry added by Kaustubh D. Bhalerao <bhalerao.1@osu.edu> | ||
532 | // GPIO values obtained from regspy, courtesy Sean Covel | ||
533 | .name = "Hauppauge WinTV 28xxx (Roslyn) models", | ||
534 | .tuner_type = UNSET, | ||
535 | .input = {{ | ||
536 | .type = CX88_VMUX_TELEVISION, | ||
537 | .vmux = 0, | ||
538 | .gpio0 = 0xed12, // internal decoder | ||
539 | .gpio2 = 0x00ff, | ||
540 | },{ | ||
541 | .type = CX88_VMUX_DEBUG, | ||
542 | .vmux = 0, | ||
543 | .gpio0 = 0xff01, // mono from tuner chip | ||
544 | },{ | ||
545 | .type = CX88_VMUX_COMPOSITE1, | ||
546 | .vmux = 1, | ||
547 | .gpio0 = 0xff02, | ||
548 | },{ | ||
549 | .type = CX88_VMUX_SVIDEO, | ||
550 | .vmux = 2, | ||
551 | .gpio0 = 0xed92, | ||
552 | .gpio2 = 0x00ff, | ||
553 | }}, | ||
554 | .radio = { | ||
555 | .type = CX88_RADIO, | ||
556 | .gpio0 = 0xed96, | ||
557 | .gpio2 = 0x00ff, | ||
558 | }, | ||
559 | .blackbird = 1, | ||
560 | }, | ||
561 | [CX88_BOARD_DIGITALLOGIC_MEC] = { | ||
562 | /* params copied over from Leadtek PVR 2000 */ | ||
563 | .name = "Digital-Logic MICROSPACE Entertainment Center (MEC)", | ||
564 | /* not sure yet about the tuner type */ | ||
565 | .tuner_type = 38, | ||
566 | .tda9887_conf = TDA9887_PRESENT, | ||
567 | .input = {{ | ||
568 | .type = CX88_VMUX_TELEVISION, | ||
569 | .vmux = 0, | ||
570 | .gpio0 = 0x0000bde6, | ||
571 | },{ | ||
572 | .type = CX88_VMUX_COMPOSITE1, | ||
573 | .vmux = 1, | ||
574 | .gpio0 = 0x0000bde6, | ||
575 | },{ | ||
576 | .type = CX88_VMUX_SVIDEO, | ||
577 | .vmux = 2, | ||
578 | .gpio0 = 0x0000bde6, | ||
579 | }}, | ||
580 | .radio = { | ||
581 | .type = CX88_RADIO, | ||
582 | .gpio0 = 0x0000bd62, | ||
583 | }, | ||
584 | .blackbird = 1, | ||
585 | }, | ||
586 | [CX88_BOARD_IODATA_GVBCTV7E] = { | ||
587 | .name = "IODATA GV/BCTV7E", | ||
588 | .tuner_type = TUNER_PHILIPS_FQ1286, | ||
589 | .tda9887_conf = TDA9887_PRESENT, | ||
590 | .input = {{ | ||
591 | .type = CX88_VMUX_TELEVISION, | ||
592 | .vmux = 1, | ||
593 | .gpio1 = 0x0000e03f, | ||
594 | },{ | ||
595 | .type = CX88_VMUX_COMPOSITE1, | ||
596 | .vmux = 2, | ||
597 | .gpio1 = 0x0000e07f, | ||
598 | },{ | ||
599 | .type = CX88_VMUX_SVIDEO, | ||
600 | .vmux = 3, | ||
601 | .gpio1 = 0x0000e07f, | ||
602 | }} | ||
603 | }, | ||
604 | }; | ||
605 | const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); | ||
606 | |||
607 | /* ------------------------------------------------------------------ */ | ||
608 | /* PCI subsystem IDs */ | ||
609 | |||
610 | struct cx88_subid cx88_subids[] = { | ||
611 | { | ||
612 | .subvendor = 0x0070, | ||
613 | .subdevice = 0x3400, | ||
614 | .card = CX88_BOARD_HAUPPAUGE, | ||
615 | },{ | ||
616 | .subvendor = 0x0070, | ||
617 | .subdevice = 0x3401, | ||
618 | .card = CX88_BOARD_HAUPPAUGE, | ||
619 | },{ | ||
620 | .subvendor = 0x14c7, | ||
621 | .subdevice = 0x0106, | ||
622 | .card = CX88_BOARD_GDI, | ||
623 | },{ | ||
624 | .subvendor = 0x14c7, | ||
625 | .subdevice = 0x0107, /* with mpeg encoder */ | ||
626 | .card = CX88_BOARD_GDI, | ||
627 | },{ | ||
628 | .subvendor = PCI_VENDOR_ID_ATI, | ||
629 | .subdevice = 0x00f8, | ||
630 | .card = CX88_BOARD_ATI_WONDER_PRO, | ||
631 | },{ | ||
632 | .subvendor = 0x107d, | ||
633 | .subdevice = 0x6611, | ||
634 | .card = CX88_BOARD_WINFAST2000XP_EXPERT, | ||
635 | },{ | ||
636 | .subvendor = 0x107d, | ||
637 | .subdevice = 0x6613, /* NTSC */ | ||
638 | .card = CX88_BOARD_WINFAST2000XP_EXPERT, | ||
639 | },{ | ||
640 | .subvendor = 0x107d, | ||
641 | .subdevice = 0x6620, | ||
642 | .card = CX88_BOARD_WINFAST_DV2000, | ||
643 | },{ | ||
644 | .subvendor = 0x107d, | ||
645 | .subdevice = 0x663b, | ||
646 | .card = CX88_BOARD_LEADTEK_PVR2000, | ||
647 | },{ | ||
648 | .subvendor = 0x107d, | ||
649 | .subdevice = 0x663C, | ||
650 | .card = CX88_BOARD_LEADTEK_PVR2000, | ||
651 | },{ | ||
652 | .subvendor = 0x1461, | ||
653 | .subdevice = 0x000b, | ||
654 | .card = CX88_BOARD_AVERTV_303, | ||
655 | },{ | ||
656 | .subvendor = 0x1462, | ||
657 | .subdevice = 0x8606, | ||
658 | .card = CX88_BOARD_MSI_TVANYWHERE_MASTER, | ||
659 | },{ | ||
660 | .subvendor = 0x10fc, | ||
661 | .subdevice = 0xd003, | ||
662 | .card = CX88_BOARD_IODATA_GVVCP3PCI, | ||
663 | },{ | ||
664 | .subvendor = 0x1043, | ||
665 | .subdevice = 0x4823, /* with mpeg encoder */ | ||
666 | .card = CX88_BOARD_ASUS_PVR_416, | ||
667 | },{ | ||
668 | .subvendor = 0x17de, | ||
669 | .subdevice = 0x08a6, | ||
670 | .card = CX88_BOARD_KWORLD_DVB_T, | ||
671 | },{ | ||
672 | .subvendor = 0x18ac, | ||
673 | .subdevice = 0xd810, | ||
674 | .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD, | ||
675 | },{ | ||
676 | .subvendor = 0x18AC, | ||
677 | .subdevice = 0xDB00, | ||
678 | .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1, | ||
679 | },{ | ||
680 | .subvendor = 0x0070, | ||
681 | .subdevice = 0x9002, | ||
682 | .card = CX88_BOARD_HAUPPAUGE_DVB_T1, | ||
683 | },{ | ||
684 | .subvendor = 0x14f1, | ||
685 | .subdevice = 0x0187, | ||
686 | .card = CX88_BOARD_CONEXANT_DVB_T1, | ||
687 | },{ | ||
688 | .subvendor = 0x1540, | ||
689 | .subdevice = 0x2580, | ||
690 | .card = CX88_BOARD_PROVIDEO_PV259, | ||
691 | },{ | ||
692 | .subvendor = 0x18AC, | ||
693 | .subdevice = 0xDB10, | ||
694 | .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, | ||
695 | },{ | ||
696 | .subvendor = 0x1554, | ||
697 | .subdevice = 0x4811, | ||
698 | .card = CX88_BOARD_PIXELVIEW, | ||
699 | },{ | ||
700 | .subvendor = 0x7063, | ||
701 | .subdevice = 0x3000, /* HD-3000 card */ | ||
702 | .card = CX88_BOARD_PCHDTV_HD3000, | ||
703 | },{ | ||
704 | .subvendor = 0x17DE, | ||
705 | .subdevice = 0xA8A6, | ||
706 | .card = CX88_BOARD_DNTV_LIVE_DVB_T, | ||
707 | },{ | ||
708 | .subvendor = 0x0070, | ||
709 | .subdevice = 0x2801, | ||
710 | .card = CX88_BOARD_HAUPPAUGE_ROSLYN, | ||
711 | },{ | ||
712 | .subvendor = 0x14F1, | ||
713 | .subdevice = 0x0342, | ||
714 | .card = CX88_BOARD_DIGITALLOGIC_MEC, | ||
715 | },{ | ||
716 | .subvendor = 0x10fc, | ||
717 | .subdevice = 0xd035, | ||
718 | .card = CX88_BOARD_IODATA_GVBCTV7E, | ||
719 | } | ||
720 | }; | ||
721 | const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); | ||
722 | |||
723 | /* ----------------------------------------------------------------------- */ | ||
724 | /* some leadtek specific stuff */ | ||
725 | |||
726 | static void __devinit leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data) | ||
727 | { | ||
728 | /* This is just for the "Winfast 2000XP Expert" board ATM; I don't have data on | ||
729 | * any others. | ||
730 | * | ||
731 | * Byte 0 is 1 on the NTSC board. | ||
732 | */ | ||
733 | |||
734 | if (eeprom_data[4] != 0x7d || | ||
735 | eeprom_data[5] != 0x10 || | ||
736 | eeprom_data[7] != 0x66) { | ||
737 | printk(KERN_WARNING "%s: Leadtek eeprom invalid.\n", | ||
738 | core->name); | ||
739 | return; | ||
740 | } | ||
741 | |||
742 | core->has_radio = 1; | ||
743 | core->tuner_type = (eeprom_data[6] == 0x13) ? 43 : 38; | ||
744 | |||
745 | printk(KERN_INFO "%s: Leadtek Winfast 2000XP Expert config: " | ||
746 | "tuner=%d, eeprom[0]=0x%02x\n", | ||
747 | core->name, core->tuner_type, eeprom_data[0]); | ||
748 | } | ||
749 | |||
750 | |||
751 | /* ----------------------------------------------------------------------- */ | ||
752 | |||
753 | static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) | ||
754 | { | ||
755 | struct tveeprom tv; | ||
756 | |||
757 | tveeprom_hauppauge_analog(&tv, eeprom_data); | ||
758 | core->tuner_type = tv.tuner_type; | ||
759 | core->has_radio = tv.has_radio; | ||
760 | } | ||
761 | |||
762 | static int hauppauge_eeprom_dvb(struct cx88_core *core, u8 *ee) | ||
763 | { | ||
764 | int model; | ||
765 | int tuner; | ||
766 | |||
767 | /* Make sure we support the board model */ | ||
768 | model = ee[0x1f] << 24 | ee[0x1e] << 16 | ee[0x1d] << 8 | ee[0x1c]; | ||
769 | switch(model) { | ||
770 | case 90002: | ||
771 | case 90500: | ||
772 | case 90501: | ||
773 | /* known */ | ||
774 | break; | ||
775 | default: | ||
776 | printk("%s: warning: unknown hauppauge model #%d\n", | ||
777 | core->name, model); | ||
778 | break; | ||
779 | } | ||
780 | |||
781 | /* Make sure we support the tuner */ | ||
782 | tuner = ee[0x2d]; | ||
783 | switch(tuner) { | ||
784 | case 0x4B: /* dtt 7595 */ | ||
785 | case 0x4C: /* dtt 7592 */ | ||
786 | break; | ||
787 | default: | ||
788 | printk("%s: error: unknown hauppauge tuner 0x%02x\n", | ||
789 | core->name, tuner); | ||
790 | return -ENODEV; | ||
791 | } | ||
792 | printk(KERN_INFO "%s: hauppauge eeprom: model=%d, tuner=%d\n", | ||
793 | core->name, model, tuner); | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | /* ----------------------------------------------------------------------- */ | ||
798 | /* some GDI (was: Modular Technology) specific stuff */ | ||
799 | |||
800 | static struct { | ||
801 | int id; | ||
802 | int fm; | ||
803 | char *name; | ||
804 | } gdi_tuner[] = { | ||
805 | [ 0x01 ] = { .id = TUNER_ABSENT, | ||
806 | .name = "NTSC_M" }, | ||
807 | [ 0x02 ] = { .id = TUNER_ABSENT, | ||
808 | .name = "PAL_B" }, | ||
809 | [ 0x03 ] = { .id = TUNER_ABSENT, | ||
810 | .name = "PAL_I" }, | ||
811 | [ 0x04 ] = { .id = TUNER_ABSENT, | ||
812 | .name = "PAL_D" }, | ||
813 | [ 0x05 ] = { .id = TUNER_ABSENT, | ||
814 | .name = "SECAM" }, | ||
815 | |||
816 | [ 0x10 ] = { .id = TUNER_ABSENT, | ||
817 | .fm = 1, | ||
818 | .name = "TEMIC_4049" }, | ||
819 | [ 0x11 ] = { .id = TUNER_TEMIC_4136FY5, | ||
820 | .name = "TEMIC_4136" }, | ||
821 | [ 0x12 ] = { .id = TUNER_ABSENT, | ||
822 | .name = "TEMIC_4146" }, | ||
823 | |||
824 | [ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME, | ||
825 | .fm = 1, | ||
826 | .name = "PHILIPS_FQ1216_MK3" }, | ||
827 | [ 0x21 ] = { .id = TUNER_ABSENT, .fm = 1, | ||
828 | .name = "PHILIPS_FQ1236_MK3" }, | ||
829 | [ 0x22 ] = { .id = TUNER_ABSENT, | ||
830 | .name = "PHILIPS_FI1236_MK3" }, | ||
831 | [ 0x23 ] = { .id = TUNER_ABSENT, | ||
832 | .name = "PHILIPS_FI1216_MK3" }, | ||
833 | }; | ||
834 | |||
835 | static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data) | ||
836 | { | ||
837 | char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner)) | ||
838 | ? gdi_tuner[eeprom_data[0x0d]].name : NULL; | ||
839 | |||
840 | printk(KERN_INFO "%s: GDI: tuner=%s\n", core->name, | ||
841 | name ? name : "unknown"); | ||
842 | if (NULL == name) | ||
843 | return; | ||
844 | core->tuner_type = gdi_tuner[eeprom_data[0x0d]].id; | ||
845 | core->has_radio = gdi_tuner[eeprom_data[0x0d]].fm; | ||
846 | } | ||
847 | |||
848 | /* ----------------------------------------------------------------------- */ | ||
849 | |||
850 | void cx88_card_list(struct cx88_core *core, struct pci_dev *pci) | ||
851 | { | ||
852 | int i; | ||
853 | |||
854 | if (0 == pci->subsystem_vendor && | ||
855 | 0 == pci->subsystem_device) { | ||
856 | printk("%s: Your board has no valid PCI Subsystem ID and thus can't\n" | ||
857 | "%s: be autodetected. Please pass card=<n> insmod option to\n" | ||
858 | "%s: workaround that. Redirect complaints to the vendor of\n" | ||
859 | "%s: the TV card. Best regards,\n" | ||
860 | "%s: -- tux\n", | ||
861 | core->name,core->name,core->name,core->name,core->name); | ||
862 | } else { | ||
863 | printk("%s: Your board isn't known (yet) to the driver. You can\n" | ||
864 | "%s: try to pick one of the existing card configs via\n" | ||
865 | "%s: card=<n> insmod option. Updating to the latest\n" | ||
866 | "%s: version might help as well.\n", | ||
867 | core->name,core->name,core->name,core->name); | ||
868 | } | ||
869 | printk("%s: Here is a list of valid choices for the card=<n> insmod option:\n", | ||
870 | core->name); | ||
871 | for (i = 0; i < cx88_bcount; i++) | ||
872 | printk("%s: card=%d -> %s\n", | ||
873 | core->name, i, cx88_boards[i].name); | ||
874 | } | ||
875 | |||
876 | void cx88_card_setup(struct cx88_core *core) | ||
877 | { | ||
878 | static u8 eeprom[128]; | ||
879 | |||
880 | if (0 == core->i2c_rc) { | ||
881 | core->i2c_client.addr = 0xa0 >> 1; | ||
882 | tveeprom_read(&core->i2c_client,eeprom,sizeof(eeprom)); | ||
883 | } | ||
884 | |||
885 | switch (core->board) { | ||
886 | case CX88_BOARD_HAUPPAUGE: | ||
887 | case CX88_BOARD_HAUPPAUGE_ROSLYN: | ||
888 | if (0 == core->i2c_rc) | ||
889 | hauppauge_eeprom(core,eeprom+8); | ||
890 | break; | ||
891 | case CX88_BOARD_GDI: | ||
892 | if (0 == core->i2c_rc) | ||
893 | gdi_eeprom(core,eeprom); | ||
894 | break; | ||
895 | case CX88_BOARD_WINFAST2000XP_EXPERT: | ||
896 | if (0 == core->i2c_rc) | ||
897 | leadtek_eeprom(core,eeprom); | ||
898 | break; | ||
899 | case CX88_BOARD_HAUPPAUGE_DVB_T1: | ||
900 | if (0 == core->i2c_rc) | ||
901 | hauppauge_eeprom_dvb(core,eeprom); | ||
902 | break; | ||
903 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: | ||
904 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: | ||
905 | /* GPIO0:0 is hooked to mt352 reset pin */ | ||
906 | cx_set(MO_GP0_IO, 0x00000101); | ||
907 | cx_clear(MO_GP0_IO, 0x00000001); | ||
908 | msleep(1); | ||
909 | cx_set(MO_GP0_IO, 0x00000101); | ||
910 | break; | ||
911 | case CX88_BOARD_KWORLD_DVB_T: | ||
912 | case CX88_BOARD_DNTV_LIVE_DVB_T: | ||
913 | cx_set(MO_GP0_IO, 0x00000707); | ||
914 | cx_set(MO_GP2_IO, 0x00000101); | ||
915 | cx_clear(MO_GP2_IO, 0x00000001); | ||
916 | msleep(1); | ||
917 | cx_clear(MO_GP0_IO, 0x00000007); | ||
918 | cx_set(MO_GP2_IO, 0x00000101); | ||
919 | break; | ||
920 | } | ||
921 | if (cx88_boards[core->board].radio.type == CX88_RADIO) | ||
922 | core->has_radio = 1; | ||
923 | } | ||
924 | |||
925 | /* ------------------------------------------------------------------ */ | ||
926 | |||
927 | EXPORT_SYMBOL(cx88_boards); | ||
928 | EXPORT_SYMBOL(cx88_bcount); | ||
929 | EXPORT_SYMBOL(cx88_subids); | ||
930 | EXPORT_SYMBOL(cx88_idcount); | ||
931 | EXPORT_SYMBOL(cx88_card_list); | ||
932 | EXPORT_SYMBOL(cx88_card_setup); | ||
933 | |||
934 | /* | ||
935 | * Local variables: | ||
936 | * c-basic-offset: 8 | ||
937 | * End: | ||
938 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c new file mode 100644 index 000000000000..26a6138015cb --- /dev/null +++ b/drivers/media/video/cx88/cx88-core.c | |||
@@ -0,0 +1,1239 @@ | |||
1 | /* | ||
2 | * $Id: cx88-core.c,v 1.24 2005/01/19 12:01:55 kraxel Exp $ | ||
3 | * | ||
4 | * device driver for Conexant 2388x based TV cards | ||
5 | * driver core | ||
6 | * | ||
7 | * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/init.h> | ||
25 | #include <linux/list.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/kmod.h> | ||
31 | #include <linux/sound.h> | ||
32 | #include <linux/interrupt.h> | ||
33 | #include <linux/pci.h> | ||
34 | #include <linux/delay.h> | ||
35 | #include <linux/videodev.h> | ||
36 | |||
37 | #include "cx88.h" | ||
38 | |||
39 | MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); | ||
40 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
41 | MODULE_LICENSE("GPL"); | ||
42 | |||
43 | /* ------------------------------------------------------------------ */ | ||
44 | |||
45 | static unsigned int core_debug = 0; | ||
46 | module_param(core_debug,int,0644); | ||
47 | MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); | ||
48 | |||
49 | static unsigned int latency = UNSET; | ||
50 | module_param(latency,int,0444); | ||
51 | MODULE_PARM_DESC(latency,"pci latency timer"); | ||
52 | |||
53 | static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
54 | static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
55 | |||
56 | module_param_array(tuner, int, NULL, 0444); | ||
57 | module_param_array(card, int, NULL, 0444); | ||
58 | |||
59 | MODULE_PARM_DESC(tuner,"tuner type"); | ||
60 | MODULE_PARM_DESC(card,"card type"); | ||
61 | |||
62 | static unsigned int nicam = 0; | ||
63 | module_param(nicam,int,0644); | ||
64 | MODULE_PARM_DESC(nicam,"tv audio is nicam"); | ||
65 | |||
66 | static unsigned int nocomb = 0; | ||
67 | module_param(nocomb,int,0644); | ||
68 | MODULE_PARM_DESC(nocomb,"disable comb filter"); | ||
69 | |||
70 | #define dprintk(level,fmt, arg...) if (core_debug >= level) \ | ||
71 | printk(KERN_DEBUG "%s: " fmt, core->name , ## arg) | ||
72 | |||
73 | static unsigned int cx88_devcount; | ||
74 | static LIST_HEAD(cx88_devlist); | ||
75 | static DECLARE_MUTEX(devlist); | ||
76 | |||
77 | /* ------------------------------------------------------------------ */ | ||
78 | /* debug help functions */ | ||
79 | |||
80 | static const char *v4l1_ioctls[] = { | ||
81 | "0", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", | ||
82 | "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", | ||
83 | "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", | ||
84 | "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", | ||
85 | "SMICROCODE", "GVBIFMT", "SVBIFMT" }; | ||
86 | #define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) | ||
87 | |||
88 | static const char *v4l2_ioctls[] = { | ||
89 | "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT", | ||
90 | "G_COMP", "S_COMP", "REQBUFS", "QUERYBUF", "G_FBUF", "S_FBUF", | ||
91 | "G_WIN", "S_WIN", "PREVIEW", "QBUF", "16", "DQBUF", "STREAMON", | ||
92 | "STREAMOFF", "G_PERF", "G_PARM", "S_PARM", "G_STD", "S_STD", | ||
93 | "ENUMSTD", "ENUMINPUT", "G_CTRL", "S_CTRL", "G_TUNER", "S_TUNER", | ||
94 | "G_FREQ", "S_FREQ", "G_AUDIO", "S_AUDIO", "35", "QUERYCTRL", | ||
95 | "QUERYMENU", "G_INPUT", "S_INPUT", "ENUMCVT", "41", "42", "43", | ||
96 | "44", "45", "G_OUTPUT", "S_OUTPUT", "ENUMOUTPUT", "G_AUDOUT", | ||
97 | "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR", | ||
98 | "S_MODULATOR" | ||
99 | }; | ||
100 | #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) | ||
101 | |||
102 | void cx88_print_ioctl(char *name, unsigned int cmd) | ||
103 | { | ||
104 | char *dir; | ||
105 | |||
106 | switch (_IOC_DIR(cmd)) { | ||
107 | case _IOC_NONE: dir = "--"; break; | ||
108 | case _IOC_READ: dir = "r-"; break; | ||
109 | case _IOC_WRITE: dir = "-w"; break; | ||
110 | case _IOC_READ | _IOC_WRITE: dir = "rw"; break; | ||
111 | default: dir = "??"; break; | ||
112 | } | ||
113 | switch (_IOC_TYPE(cmd)) { | ||
114 | case 'v': | ||
115 | printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l1, %s, VIDIOC%s)\n", | ||
116 | name, cmd, dir, (_IOC_NR(cmd) < V4L1_IOCTLS) ? | ||
117 | v4l1_ioctls[_IOC_NR(cmd)] : "???"); | ||
118 | break; | ||
119 | case 'V': | ||
120 | printk(KERN_DEBUG "%s: ioctl 0x%08x (v4l2, %s, VIDIOC_%s)\n", | ||
121 | name, cmd, dir, (_IOC_NR(cmd) < V4L2_IOCTLS) ? | ||
122 | v4l2_ioctls[_IOC_NR(cmd)] : "???"); | ||
123 | break; | ||
124 | default: | ||
125 | printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n", | ||
126 | name, cmd, dir, _IOC_NR(cmd)); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | /* ------------------------------------------------------------------ */ | ||
131 | #define NO_SYNC_LINE (-1U) | ||
132 | |||
133 | static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist, | ||
134 | unsigned int offset, u32 sync_line, | ||
135 | unsigned int bpl, unsigned int padding, | ||
136 | unsigned int lines) | ||
137 | { | ||
138 | struct scatterlist *sg; | ||
139 | unsigned int line,todo; | ||
140 | |||
141 | /* sync instruction */ | ||
142 | if (sync_line != NO_SYNC_LINE) | ||
143 | *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); | ||
144 | |||
145 | /* scan lines */ | ||
146 | sg = sglist; | ||
147 | for (line = 0; line < lines; line++) { | ||
148 | while (offset && offset >= sg_dma_len(sg)) { | ||
149 | offset -= sg_dma_len(sg); | ||
150 | sg++; | ||
151 | } | ||
152 | if (bpl <= sg_dma_len(sg)-offset) { | ||
153 | /* fits into current chunk */ | ||
154 | *(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL|RISC_EOL|bpl); | ||
155 | *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); | ||
156 | offset+=bpl; | ||
157 | } else { | ||
158 | /* scanline needs to be splitted */ | ||
159 | todo = bpl; | ||
160 | *(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL| | ||
161 | (sg_dma_len(sg)-offset)); | ||
162 | *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); | ||
163 | todo -= (sg_dma_len(sg)-offset); | ||
164 | offset = 0; | ||
165 | sg++; | ||
166 | while (todo > sg_dma_len(sg)) { | ||
167 | *(rp++)=cpu_to_le32(RISC_WRITE| | ||
168 | sg_dma_len(sg)); | ||
169 | *(rp++)=cpu_to_le32(sg_dma_address(sg)); | ||
170 | todo -= sg_dma_len(sg); | ||
171 | sg++; | ||
172 | } | ||
173 | *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo); | ||
174 | *(rp++)=cpu_to_le32(sg_dma_address(sg)); | ||
175 | offset += todo; | ||
176 | } | ||
177 | offset += padding; | ||
178 | } | ||
179 | |||
180 | return rp; | ||
181 | } | ||
182 | |||
183 | int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
184 | struct scatterlist *sglist, | ||
185 | unsigned int top_offset, unsigned int bottom_offset, | ||
186 | unsigned int bpl, unsigned int padding, unsigned int lines) | ||
187 | { | ||
188 | u32 instructions,fields; | ||
189 | u32 *rp; | ||
190 | int rc; | ||
191 | |||
192 | fields = 0; | ||
193 | if (UNSET != top_offset) | ||
194 | fields++; | ||
195 | if (UNSET != bottom_offset) | ||
196 | fields++; | ||
197 | |||
198 | /* estimate risc mem: worst case is one write per page border + | ||
199 | one write per scan line + syncs + jump (all 2 dwords) */ | ||
200 | instructions = (bpl * lines * fields) / PAGE_SIZE + lines * fields; | ||
201 | instructions += 3 + 4; | ||
202 | if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0) | ||
203 | return rc; | ||
204 | |||
205 | /* write risc instructions */ | ||
206 | rp = risc->cpu; | ||
207 | if (UNSET != top_offset) | ||
208 | rp = cx88_risc_field(rp, sglist, top_offset, 0, | ||
209 | bpl, padding, lines); | ||
210 | if (UNSET != bottom_offset) | ||
211 | rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200, | ||
212 | bpl, padding, lines); | ||
213 | |||
214 | /* save pointer to jmp instruction address */ | ||
215 | risc->jmp = rp; | ||
216 | BUG_ON((risc->jmp - risc->cpu + 2) / 4 > risc->size); | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
221 | struct scatterlist *sglist, unsigned int bpl, | ||
222 | unsigned int lines) | ||
223 | { | ||
224 | u32 instructions; | ||
225 | u32 *rp; | ||
226 | int rc; | ||
227 | |||
228 | /* estimate risc mem: worst case is one write per page border + | ||
229 | one write per scan line + syncs + jump (all 2 dwords) */ | ||
230 | instructions = (bpl * lines) / PAGE_SIZE + lines; | ||
231 | instructions += 3 + 4; | ||
232 | if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0) | ||
233 | return rc; | ||
234 | |||
235 | /* write risc instructions */ | ||
236 | rp = risc->cpu; | ||
237 | rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines); | ||
238 | |||
239 | /* save pointer to jmp instruction address */ | ||
240 | risc->jmp = rp; | ||
241 | BUG_ON((risc->jmp - risc->cpu + 2) / 4 > risc->size); | ||
242 | return 0; | ||
243 | } | ||
244 | |||
245 | int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
246 | u32 reg, u32 mask, u32 value) | ||
247 | { | ||
248 | u32 *rp; | ||
249 | int rc; | ||
250 | |||
251 | if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0) | ||
252 | return rc; | ||
253 | |||
254 | /* write risc instructions */ | ||
255 | rp = risc->cpu; | ||
256 | *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM); | ||
257 | *(rp++) = cpu_to_le32(reg); | ||
258 | *(rp++) = cpu_to_le32(value); | ||
259 | *(rp++) = cpu_to_le32(mask); | ||
260 | *(rp++) = cpu_to_le32(RISC_JUMP); | ||
261 | *(rp++) = cpu_to_le32(risc->dma); | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | void | ||
266 | cx88_free_buffer(struct pci_dev *pci, struct cx88_buffer *buf) | ||
267 | { | ||
268 | if (in_interrupt()) | ||
269 | BUG(); | ||
270 | videobuf_waiton(&buf->vb,0,0); | ||
271 | videobuf_dma_pci_unmap(pci, &buf->vb.dma); | ||
272 | videobuf_dma_free(&buf->vb.dma); | ||
273 | btcx_riscmem_free(pci, &buf->risc); | ||
274 | buf->vb.state = STATE_NEEDS_INIT; | ||
275 | } | ||
276 | |||
277 | /* ------------------------------------------------------------------ */ | ||
278 | /* our SRAM memory layout */ | ||
279 | |||
280 | /* we are going to put all thr risc programs into host memory, so we | ||
281 | * can use the whole SDRAM for the DMA fifos. To simplify things, we | ||
282 | * use a static memory layout. That surely will waste memory in case | ||
283 | * we don't use all DMA channels at the same time (which will be the | ||
284 | * case most of the time). But that still gives us enougth FIFO space | ||
285 | * to be able to deal with insane long pci latencies ... | ||
286 | * | ||
287 | * FIFO space allocations: | ||
288 | * channel 21 (y video) - 10.0k | ||
289 | * channel 22 (u video) - 2.0k | ||
290 | * channel 23 (v video) - 2.0k | ||
291 | * channel 24 (vbi) - 4.0k | ||
292 | * channels 25+26 (audio) - 0.5k | ||
293 | * channel 28 (mpeg) - 4.0k | ||
294 | * TOTAL = 25.5k | ||
295 | * | ||
296 | * Every channel has 160 bytes control data (64 bytes instruction | ||
297 | * queue and 6 CDT entries), which is close to 2k total. | ||
298 | * | ||
299 | * Address layout: | ||
300 | * 0x0000 - 0x03ff CMDs / reserved | ||
301 | * 0x0400 - 0x0bff instruction queues + CDs | ||
302 | * 0x0c00 - FIFOs | ||
303 | */ | ||
304 | |||
305 | struct sram_channel cx88_sram_channels[] = { | ||
306 | [SRAM_CH21] = { | ||
307 | .name = "video y / packed", | ||
308 | .cmds_start = 0x180040, | ||
309 | .ctrl_start = 0x180400, | ||
310 | .cdt = 0x180400 + 64, | ||
311 | .fifo_start = 0x180c00, | ||
312 | .fifo_size = 0x002800, | ||
313 | .ptr1_reg = MO_DMA21_PTR1, | ||
314 | .ptr2_reg = MO_DMA21_PTR2, | ||
315 | .cnt1_reg = MO_DMA21_CNT1, | ||
316 | .cnt2_reg = MO_DMA21_CNT2, | ||
317 | }, | ||
318 | [SRAM_CH22] = { | ||
319 | .name = "video u", | ||
320 | .cmds_start = 0x180080, | ||
321 | .ctrl_start = 0x1804a0, | ||
322 | .cdt = 0x1804a0 + 64, | ||
323 | .fifo_start = 0x183400, | ||
324 | .fifo_size = 0x000800, | ||
325 | .ptr1_reg = MO_DMA22_PTR1, | ||
326 | .ptr2_reg = MO_DMA22_PTR2, | ||
327 | .cnt1_reg = MO_DMA22_CNT1, | ||
328 | .cnt2_reg = MO_DMA22_CNT2, | ||
329 | }, | ||
330 | [SRAM_CH23] = { | ||
331 | .name = "video v", | ||
332 | .cmds_start = 0x1800c0, | ||
333 | .ctrl_start = 0x180540, | ||
334 | .cdt = 0x180540 + 64, | ||
335 | .fifo_start = 0x183c00, | ||
336 | .fifo_size = 0x000800, | ||
337 | .ptr1_reg = MO_DMA23_PTR1, | ||
338 | .ptr2_reg = MO_DMA23_PTR2, | ||
339 | .cnt1_reg = MO_DMA23_CNT1, | ||
340 | .cnt2_reg = MO_DMA23_CNT2, | ||
341 | }, | ||
342 | [SRAM_CH24] = { | ||
343 | .name = "vbi", | ||
344 | .cmds_start = 0x180100, | ||
345 | .ctrl_start = 0x1805e0, | ||
346 | .cdt = 0x1805e0 + 64, | ||
347 | .fifo_start = 0x184400, | ||
348 | .fifo_size = 0x001000, | ||
349 | .ptr1_reg = MO_DMA24_PTR1, | ||
350 | .ptr2_reg = MO_DMA24_PTR2, | ||
351 | .cnt1_reg = MO_DMA24_CNT1, | ||
352 | .cnt2_reg = MO_DMA24_CNT2, | ||
353 | }, | ||
354 | [SRAM_CH25] = { | ||
355 | .name = "audio from", | ||
356 | .cmds_start = 0x180140, | ||
357 | .ctrl_start = 0x180680, | ||
358 | .cdt = 0x180680 + 64, | ||
359 | .fifo_start = 0x185400, | ||
360 | .fifo_size = 0x000200, | ||
361 | .ptr1_reg = MO_DMA25_PTR1, | ||
362 | .ptr2_reg = MO_DMA25_PTR2, | ||
363 | .cnt1_reg = MO_DMA25_CNT1, | ||
364 | .cnt2_reg = MO_DMA25_CNT2, | ||
365 | }, | ||
366 | [SRAM_CH26] = { | ||
367 | .name = "audio to", | ||
368 | .cmds_start = 0x180180, | ||
369 | .ctrl_start = 0x180720, | ||
370 | .cdt = 0x180680 + 64, /* same as audio IN */ | ||
371 | .fifo_start = 0x185400, /* same as audio IN */ | ||
372 | .fifo_size = 0x000200, /* same as audio IN */ | ||
373 | .ptr1_reg = MO_DMA26_PTR1, | ||
374 | .ptr2_reg = MO_DMA26_PTR2, | ||
375 | .cnt1_reg = MO_DMA26_CNT1, | ||
376 | .cnt2_reg = MO_DMA26_CNT2, | ||
377 | }, | ||
378 | [SRAM_CH28] = { | ||
379 | .name = "mpeg", | ||
380 | .cmds_start = 0x180200, | ||
381 | .ctrl_start = 0x1807C0, | ||
382 | .cdt = 0x1807C0 + 64, | ||
383 | .fifo_start = 0x185600, | ||
384 | .fifo_size = 0x001000, | ||
385 | .ptr1_reg = MO_DMA28_PTR1, | ||
386 | .ptr2_reg = MO_DMA28_PTR2, | ||
387 | .cnt1_reg = MO_DMA28_CNT1, | ||
388 | .cnt2_reg = MO_DMA28_CNT2, | ||
389 | }, | ||
390 | }; | ||
391 | |||
392 | int cx88_sram_channel_setup(struct cx88_core *core, | ||
393 | struct sram_channel *ch, | ||
394 | unsigned int bpl, u32 risc) | ||
395 | { | ||
396 | unsigned int i,lines; | ||
397 | u32 cdt; | ||
398 | |||
399 | bpl = (bpl + 7) & ~7; /* alignment */ | ||
400 | cdt = ch->cdt; | ||
401 | lines = ch->fifo_size / bpl; | ||
402 | if (lines > 6) | ||
403 | lines = 6; | ||
404 | BUG_ON(lines < 2); | ||
405 | |||
406 | /* write CDT */ | ||
407 | for (i = 0; i < lines; i++) | ||
408 | cx_write(cdt + 16*i, ch->fifo_start + bpl*i); | ||
409 | |||
410 | /* write CMDS */ | ||
411 | cx_write(ch->cmds_start + 0, risc); | ||
412 | cx_write(ch->cmds_start + 4, cdt); | ||
413 | cx_write(ch->cmds_start + 8, (lines*16) >> 3); | ||
414 | cx_write(ch->cmds_start + 12, ch->ctrl_start); | ||
415 | cx_write(ch->cmds_start + 16, 64 >> 2); | ||
416 | for (i = 20; i < 64; i += 4) | ||
417 | cx_write(ch->cmds_start + i, 0); | ||
418 | |||
419 | /* fill registers */ | ||
420 | cx_write(ch->ptr1_reg, ch->fifo_start); | ||
421 | cx_write(ch->ptr2_reg, cdt); | ||
422 | cx_write(ch->cnt1_reg, (bpl >> 3) -1); | ||
423 | cx_write(ch->cnt2_reg, (lines*16) >> 3); | ||
424 | |||
425 | dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines); | ||
426 | return 0; | ||
427 | } | ||
428 | |||
429 | /* ------------------------------------------------------------------ */ | ||
430 | /* debug helper code */ | ||
431 | |||
432 | int cx88_risc_decode(u32 risc) | ||
433 | { | ||
434 | static char *instr[16] = { | ||
435 | [ RISC_SYNC >> 28 ] = "sync", | ||
436 | [ RISC_WRITE >> 28 ] = "write", | ||
437 | [ RISC_WRITEC >> 28 ] = "writec", | ||
438 | [ RISC_READ >> 28 ] = "read", | ||
439 | [ RISC_READC >> 28 ] = "readc", | ||
440 | [ RISC_JUMP >> 28 ] = "jump", | ||
441 | [ RISC_SKIP >> 28 ] = "skip", | ||
442 | [ RISC_WRITERM >> 28 ] = "writerm", | ||
443 | [ RISC_WRITECM >> 28 ] = "writecm", | ||
444 | [ RISC_WRITECR >> 28 ] = "writecr", | ||
445 | }; | ||
446 | static int incr[16] = { | ||
447 | [ RISC_WRITE >> 28 ] = 2, | ||
448 | [ RISC_JUMP >> 28 ] = 2, | ||
449 | [ RISC_WRITERM >> 28 ] = 3, | ||
450 | [ RISC_WRITECM >> 28 ] = 3, | ||
451 | [ RISC_WRITECR >> 28 ] = 4, | ||
452 | }; | ||
453 | static char *bits[] = { | ||
454 | "12", "13", "14", "resync", | ||
455 | "cnt0", "cnt1", "18", "19", | ||
456 | "20", "21", "22", "23", | ||
457 | "irq1", "irq2", "eol", "sol", | ||
458 | }; | ||
459 | int i; | ||
460 | |||
461 | printk("0x%08x [ %s", risc, | ||
462 | instr[risc >> 28] ? instr[risc >> 28] : "INVALID"); | ||
463 | for (i = ARRAY_SIZE(bits)-1; i >= 0; i--) | ||
464 | if (risc & (1 << (i + 12))) | ||
465 | printk(" %s",bits[i]); | ||
466 | printk(" count=%d ]\n", risc & 0xfff); | ||
467 | return incr[risc >> 28] ? incr[risc >> 28] : 1; | ||
468 | } | ||
469 | |||
470 | #if 0 /* currently unused, but useful for debugging */ | ||
471 | void cx88_risc_disasm(struct cx88_core *core, | ||
472 | struct btcx_riscmem *risc) | ||
473 | { | ||
474 | unsigned int i,j,n; | ||
475 | |||
476 | printk("%s: risc disasm: %p [dma=0x%08lx]\n", | ||
477 | core->name, risc->cpu, (unsigned long)risc->dma); | ||
478 | for (i = 0; i < (risc->size >> 2); i += n) { | ||
479 | printk("%s: %04d: ", core->name, i); | ||
480 | n = cx88_risc_decode(risc->cpu[i]); | ||
481 | for (j = 1; j < n; j++) | ||
482 | printk("%s: %04d: 0x%08x [ arg #%d ]\n", | ||
483 | core->name, i+j, risc->cpu[i+j], j); | ||
484 | if (risc->cpu[i] == RISC_JUMP) | ||
485 | break; | ||
486 | } | ||
487 | } | ||
488 | #endif | ||
489 | |||
490 | void cx88_sram_channel_dump(struct cx88_core *core, | ||
491 | struct sram_channel *ch) | ||
492 | { | ||
493 | static char *name[] = { | ||
494 | "initial risc", | ||
495 | "cdt base", | ||
496 | "cdt size", | ||
497 | "iq base", | ||
498 | "iq size", | ||
499 | "risc pc", | ||
500 | "iq wr ptr", | ||
501 | "iq rd ptr", | ||
502 | "cdt current", | ||
503 | "pci target", | ||
504 | "line / byte", | ||
505 | }; | ||
506 | u32 risc; | ||
507 | unsigned int i,j,n; | ||
508 | |||
509 | printk("%s: %s - dma channel status dump\n", | ||
510 | core->name,ch->name); | ||
511 | for (i = 0; i < ARRAY_SIZE(name); i++) | ||
512 | printk("%s: cmds: %-12s: 0x%08x\n", | ||
513 | core->name,name[i], | ||
514 | cx_read(ch->cmds_start + 4*i)); | ||
515 | for (i = 0; i < 4; i++) { | ||
516 | risc = cx_read(ch->cmds_start + 4 * (i+11)); | ||
517 | printk("%s: risc%d: ", core->name, i); | ||
518 | cx88_risc_decode(risc); | ||
519 | } | ||
520 | for (i = 0; i < 16; i += n) { | ||
521 | risc = cx_read(ch->ctrl_start + 4 * i); | ||
522 | printk("%s: iq %x: ", core->name, i); | ||
523 | n = cx88_risc_decode(risc); | ||
524 | for (j = 1; j < n; j++) { | ||
525 | risc = cx_read(ch->ctrl_start + 4 * (i+j)); | ||
526 | printk("%s: iq %x: 0x%08x [ arg #%d ]\n", | ||
527 | core->name, i+j, risc, j); | ||
528 | } | ||
529 | } | ||
530 | |||
531 | printk("%s: fifo: 0x%08x -> 0x%x\n", | ||
532 | core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size); | ||
533 | printk("%s: ctrl: 0x%08x -> 0x%x\n", | ||
534 | core->name, ch->ctrl_start, ch->ctrl_start+6*16); | ||
535 | printk("%s: ptr1_reg: 0x%08x\n", | ||
536 | core->name,cx_read(ch->ptr1_reg)); | ||
537 | printk("%s: ptr2_reg: 0x%08x\n", | ||
538 | core->name,cx_read(ch->ptr2_reg)); | ||
539 | printk("%s: cnt1_reg: 0x%08x\n", | ||
540 | core->name,cx_read(ch->cnt1_reg)); | ||
541 | printk("%s: cnt2_reg: 0x%08x\n", | ||
542 | core->name,cx_read(ch->cnt2_reg)); | ||
543 | } | ||
544 | |||
545 | char *cx88_pci_irqs[32] = { | ||
546 | "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1", | ||
547 | "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err", | ||
548 | "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err", | ||
549 | "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1" | ||
550 | }; | ||
551 | char *cx88_vid_irqs[32] = { | ||
552 | "y_risci1", "u_risci1", "v_risci1", "vbi_risc1", | ||
553 | "y_risci2", "u_risci2", "v_risci2", "vbi_risc2", | ||
554 | "y_oflow", "u_oflow", "v_oflow", "vbi_oflow", | ||
555 | "y_sync", "u_sync", "v_sync", "vbi_sync", | ||
556 | "opc_err", "par_err", "rip_err", "pci_abort", | ||
557 | }; | ||
558 | char *cx88_mpeg_irqs[32] = { | ||
559 | "ts_risci1", NULL, NULL, NULL, | ||
560 | "ts_risci2", NULL, NULL, NULL, | ||
561 | "ts_oflow", NULL, NULL, NULL, | ||
562 | "ts_sync", NULL, NULL, NULL, | ||
563 | "opc_err", "par_err", "rip_err", "pci_abort", | ||
564 | "ts_err?", | ||
565 | }; | ||
566 | |||
567 | void cx88_print_irqbits(char *name, char *tag, char **strings, | ||
568 | u32 bits, u32 mask) | ||
569 | { | ||
570 | unsigned int i; | ||
571 | |||
572 | printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits); | ||
573 | for (i = 0; i < 32; i++) { | ||
574 | if (!(bits & (1 << i))) | ||
575 | continue; | ||
576 | if (strings[i]) | ||
577 | printk(" %s", strings[i]); | ||
578 | else | ||
579 | printk(" %d", i); | ||
580 | if (!(mask & (1 << i))) | ||
581 | continue; | ||
582 | printk("*"); | ||
583 | } | ||
584 | printk("\n"); | ||
585 | } | ||
586 | |||
587 | /* ------------------------------------------------------------------ */ | ||
588 | |||
589 | int cx88_core_irq(struct cx88_core *core, u32 status) | ||
590 | { | ||
591 | int handled = 0; | ||
592 | |||
593 | if (status & (1<<18)) { | ||
594 | cx88_ir_irq(core); | ||
595 | handled++; | ||
596 | } | ||
597 | if (!handled) | ||
598 | cx88_print_irqbits(core->name, "irq pci", | ||
599 | cx88_pci_irqs, status, | ||
600 | core->pci_irqmask); | ||
601 | return handled; | ||
602 | } | ||
603 | |||
604 | void cx88_wakeup(struct cx88_core *core, | ||
605 | struct cx88_dmaqueue *q, u32 count) | ||
606 | { | ||
607 | struct cx88_buffer *buf; | ||
608 | int bc; | ||
609 | |||
610 | for (bc = 0;; bc++) { | ||
611 | if (list_empty(&q->active)) | ||
612 | break; | ||
613 | buf = list_entry(q->active.next, | ||
614 | struct cx88_buffer, vb.queue); | ||
615 | #if 0 | ||
616 | if (buf->count > count) | ||
617 | break; | ||
618 | #else | ||
619 | /* count comes from the hw and is is 16bit wide -- | ||
620 | * this trick handles wrap-arounds correctly for | ||
621 | * up to 32767 buffers in flight... */ | ||
622 | if ((s16) (count - buf->count) < 0) | ||
623 | break; | ||
624 | #endif | ||
625 | do_gettimeofday(&buf->vb.ts); | ||
626 | dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i, | ||
627 | count, buf->count); | ||
628 | buf->vb.state = STATE_DONE; | ||
629 | list_del(&buf->vb.queue); | ||
630 | wake_up(&buf->vb.done); | ||
631 | } | ||
632 | if (list_empty(&q->active)) { | ||
633 | del_timer(&q->timeout); | ||
634 | } else { | ||
635 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
636 | } | ||
637 | if (bc != 1) | ||
638 | printk("%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc); | ||
639 | } | ||
640 | |||
641 | void cx88_shutdown(struct cx88_core *core) | ||
642 | { | ||
643 | /* disable RISC controller + IRQs */ | ||
644 | cx_write(MO_DEV_CNTRL2, 0); | ||
645 | |||
646 | /* stop dma transfers */ | ||
647 | cx_write(MO_VID_DMACNTRL, 0x0); | ||
648 | cx_write(MO_AUD_DMACNTRL, 0x0); | ||
649 | cx_write(MO_TS_DMACNTRL, 0x0); | ||
650 | cx_write(MO_VIP_DMACNTRL, 0x0); | ||
651 | cx_write(MO_GPHST_DMACNTRL, 0x0); | ||
652 | |||
653 | /* stop interrupts */ | ||
654 | cx_write(MO_PCI_INTMSK, 0x0); | ||
655 | cx_write(MO_VID_INTMSK, 0x0); | ||
656 | cx_write(MO_AUD_INTMSK, 0x0); | ||
657 | cx_write(MO_TS_INTMSK, 0x0); | ||
658 | cx_write(MO_VIP_INTMSK, 0x0); | ||
659 | cx_write(MO_GPHST_INTMSK, 0x0); | ||
660 | |||
661 | /* stop capturing */ | ||
662 | cx_write(VID_CAPTURE_CONTROL, 0); | ||
663 | } | ||
664 | |||
665 | int cx88_reset(struct cx88_core *core) | ||
666 | { | ||
667 | dprintk(1,"%s\n",__FUNCTION__); | ||
668 | cx88_shutdown(core); | ||
669 | |||
670 | /* clear irq status */ | ||
671 | cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int | ||
672 | cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int | ||
673 | cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int | ||
674 | |||
675 | /* wait a bit */ | ||
676 | msleep(100); | ||
677 | |||
678 | /* init sram */ | ||
679 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0); | ||
680 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0); | ||
681 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0); | ||
682 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0); | ||
683 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0); | ||
684 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0); | ||
685 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0); | ||
686 | |||
687 | /* misc init ... */ | ||
688 | cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable | ||
689 | (1 << 12) | // agc gain | ||
690 | (1 << 11) | // adaptibe agc | ||
691 | (0 << 10) | // chroma agc | ||
692 | (0 << 9) | // ckillen | ||
693 | (7))); | ||
694 | |||
695 | /* setup image format */ | ||
696 | cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000); | ||
697 | |||
698 | /* setup FIFO Threshholds */ | ||
699 | cx_write(MO_PDMA_STHRSH, 0x0807); | ||
700 | cx_write(MO_PDMA_DTHRSH, 0x0807); | ||
701 | |||
702 | /* fixes flashing of image */ | ||
703 | cx_write(MO_AGC_SYNC_TIP1, 0x0380000F); | ||
704 | cx_write(MO_AGC_BACK_VBI, 0x00E00555); | ||
705 | |||
706 | cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int | ||
707 | cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int | ||
708 | cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int | ||
709 | |||
710 | /* Reset on-board parts */ | ||
711 | cx_write(MO_SRST_IO, 0); | ||
712 | msleep(10); | ||
713 | cx_write(MO_SRST_IO, 1); | ||
714 | |||
715 | return 0; | ||
716 | } | ||
717 | |||
718 | /* ------------------------------------------------------------------ */ | ||
719 | |||
720 | static unsigned int inline norm_swidth(struct cx88_tvnorm *norm) | ||
721 | { | ||
722 | return (norm->id & V4L2_STD_625_50) ? 922 : 754; | ||
723 | } | ||
724 | |||
725 | static unsigned int inline norm_hdelay(struct cx88_tvnorm *norm) | ||
726 | { | ||
727 | return (norm->id & V4L2_STD_625_50) ? 186 : 135; | ||
728 | } | ||
729 | |||
730 | static unsigned int inline norm_vdelay(struct cx88_tvnorm *norm) | ||
731 | { | ||
732 | return (norm->id & V4L2_STD_625_50) ? 0x24 : 0x18; | ||
733 | } | ||
734 | |||
735 | static unsigned int inline norm_fsc8(struct cx88_tvnorm *norm) | ||
736 | { | ||
737 | static const unsigned int ntsc = 28636360; | ||
738 | static const unsigned int pal = 35468950; | ||
739 | |||
740 | return (norm->id & V4L2_STD_625_50) ? pal : ntsc; | ||
741 | } | ||
742 | |||
743 | static unsigned int inline norm_notchfilter(struct cx88_tvnorm *norm) | ||
744 | { | ||
745 | return (norm->id & V4L2_STD_625_50) | ||
746 | ? HLNotchFilter135PAL | ||
747 | : HLNotchFilter135NTSC; | ||
748 | } | ||
749 | |||
750 | static unsigned int inline norm_htotal(struct cx88_tvnorm *norm) | ||
751 | { | ||
752 | return (norm->id & V4L2_STD_625_50) ? 1135 : 910; | ||
753 | } | ||
754 | |||
755 | static unsigned int inline norm_vbipack(struct cx88_tvnorm *norm) | ||
756 | { | ||
757 | return (norm->id & V4L2_STD_625_50) ? 511 : 288; | ||
758 | } | ||
759 | |||
760 | int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height, | ||
761 | enum v4l2_field field) | ||
762 | { | ||
763 | unsigned int swidth = norm_swidth(core->tvnorm); | ||
764 | unsigned int sheight = norm_maxh(core->tvnorm); | ||
765 | u32 value; | ||
766 | |||
767 | dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height, | ||
768 | V4L2_FIELD_HAS_TOP(field) ? "T" : "", | ||
769 | V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "", | ||
770 | core->tvnorm->name); | ||
771 | if (!V4L2_FIELD_HAS_BOTH(field)) | ||
772 | height *= 2; | ||
773 | |||
774 | // recalc H delay and scale registers | ||
775 | value = (width * norm_hdelay(core->tvnorm)) / swidth; | ||
776 | value &= 0x3fe; | ||
777 | cx_write(MO_HDELAY_EVEN, value); | ||
778 | cx_write(MO_HDELAY_ODD, value); | ||
779 | dprintk(1,"set_scale: hdelay 0x%04x\n", value); | ||
780 | |||
781 | value = (swidth * 4096 / width) - 4096; | ||
782 | cx_write(MO_HSCALE_EVEN, value); | ||
783 | cx_write(MO_HSCALE_ODD, value); | ||
784 | dprintk(1,"set_scale: hscale 0x%04x\n", value); | ||
785 | |||
786 | cx_write(MO_HACTIVE_EVEN, width); | ||
787 | cx_write(MO_HACTIVE_ODD, width); | ||
788 | dprintk(1,"set_scale: hactive 0x%04x\n", width); | ||
789 | |||
790 | // recalc V scale Register (delay is constant) | ||
791 | cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm)); | ||
792 | cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm)); | ||
793 | dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm)); | ||
794 | |||
795 | value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff; | ||
796 | cx_write(MO_VSCALE_EVEN, value); | ||
797 | cx_write(MO_VSCALE_ODD, value); | ||
798 | dprintk(1,"set_scale: vscale 0x%04x\n", value); | ||
799 | |||
800 | cx_write(MO_VACTIVE_EVEN, sheight); | ||
801 | cx_write(MO_VACTIVE_ODD, sheight); | ||
802 | dprintk(1,"set_scale: vactive 0x%04x\n", sheight); | ||
803 | |||
804 | // setup filters | ||
805 | value = 0; | ||
806 | value |= (1 << 19); // CFILT (default) | ||
807 | if (core->tvnorm->id & V4L2_STD_SECAM) { | ||
808 | value |= (1 << 15); | ||
809 | value |= (1 << 16); | ||
810 | } | ||
811 | if (INPUT(core->input)->type == CX88_VMUX_SVIDEO) | ||
812 | value |= (1 << 13) | (1 << 5); | ||
813 | if (V4L2_FIELD_INTERLACED == field) | ||
814 | value |= (1 << 3); // VINT (interlaced vertical scaling) | ||
815 | if (width < 385) | ||
816 | value |= (1 << 0); // 3-tap interpolation | ||
817 | if (width < 193) | ||
818 | value |= (1 << 1); // 5-tap interpolation | ||
819 | if (nocomb) | ||
820 | value |= (3 << 5); // disable comb filter | ||
821 | |||
822 | cx_write(MO_FILTER_EVEN, value); | ||
823 | cx_write(MO_FILTER_ODD, value); | ||
824 | dprintk(1,"set_scale: filter 0x%04x\n", value); | ||
825 | |||
826 | return 0; | ||
827 | } | ||
828 | |||
829 | static const u32 xtal = 28636363; | ||
830 | |||
831 | static int set_pll(struct cx88_core *core, int prescale, u32 ofreq) | ||
832 | { | ||
833 | static u32 pre[] = { 0, 0, 0, 3, 2, 1 }; | ||
834 | u64 pll; | ||
835 | u32 reg; | ||
836 | int i; | ||
837 | |||
838 | if (prescale < 2) | ||
839 | prescale = 2; | ||
840 | if (prescale > 5) | ||
841 | prescale = 5; | ||
842 | |||
843 | pll = ofreq * 8 * prescale * (u64)(1 << 20); | ||
844 | do_div(pll,xtal); | ||
845 | reg = (pll & 0x3ffffff) | (pre[prescale] << 26); | ||
846 | if (((reg >> 20) & 0x3f) < 14) { | ||
847 | printk("%s/0: pll out of range\n",core->name); | ||
848 | return -1; | ||
849 | } | ||
850 | |||
851 | dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n", | ||
852 | reg, cx_read(MO_PLL_REG), ofreq); | ||
853 | cx_write(MO_PLL_REG, reg); | ||
854 | for (i = 0; i < 100; i++) { | ||
855 | reg = cx_read(MO_DEVICE_STATUS); | ||
856 | if (reg & (1<<2)) { | ||
857 | dprintk(1,"pll locked [pre=%d,ofreq=%d]\n", | ||
858 | prescale,ofreq); | ||
859 | return 0; | ||
860 | } | ||
861 | dprintk(1,"pll not locked yet, waiting ...\n"); | ||
862 | msleep(10); | ||
863 | } | ||
864 | dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq); | ||
865 | return -1; | ||
866 | } | ||
867 | |||
868 | static int set_tvaudio(struct cx88_core *core) | ||
869 | { | ||
870 | struct cx88_tvnorm *norm = core->tvnorm; | ||
871 | |||
872 | if (CX88_VMUX_TELEVISION != INPUT(core->input)->type) | ||
873 | return 0; | ||
874 | |||
875 | if (V4L2_STD_PAL_BG & norm->id) { | ||
876 | core->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_BG; | ||
877 | |||
878 | } else if (V4L2_STD_PAL_DK & norm->id) { | ||
879 | core->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_DK; | ||
880 | |||
881 | } else if (V4L2_STD_PAL_I & norm->id) { | ||
882 | core->tvaudio = WW_NICAM_I; | ||
883 | |||
884 | } else if (V4L2_STD_SECAM_L & norm->id) { | ||
885 | core->tvaudio = WW_SYSTEM_L_AM; | ||
886 | |||
887 | } else if (V4L2_STD_SECAM_DK & norm->id) { | ||
888 | core->tvaudio = WW_A2_DK; | ||
889 | |||
890 | } else if ((V4L2_STD_NTSC_M & norm->id) || | ||
891 | (V4L2_STD_PAL_M & norm->id)) { | ||
892 | core->tvaudio = WW_BTSC; | ||
893 | |||
894 | } else if (V4L2_STD_NTSC_M_JP & norm->id) { | ||
895 | core->tvaudio = WW_EIAJ; | ||
896 | |||
897 | } else { | ||
898 | printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n", | ||
899 | core->name, norm->name); | ||
900 | core->tvaudio = 0; | ||
901 | return 0; | ||
902 | } | ||
903 | |||
904 | cx_andor(MO_AFECFG_IO, 0x1f, 0x0); | ||
905 | cx88_set_tvaudio(core); | ||
906 | // cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); | ||
907 | |||
908 | cx_write(MO_AUDD_LNGTH, 128); /* fifo size */ | ||
909 | cx_write(MO_AUDR_LNGTH, 128); /* fifo size */ | ||
910 | cx_write(MO_AUD_DMACNTRL, 0x03); /* need audio fifo */ | ||
911 | return 0; | ||
912 | } | ||
913 | |||
914 | int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) | ||
915 | { | ||
916 | u32 fsc8; | ||
917 | u32 adc_clock; | ||
918 | u32 vdec_clock; | ||
919 | u32 step_db,step_dr; | ||
920 | u64 tmp64; | ||
921 | u32 bdelay,agcdelay,htotal; | ||
922 | |||
923 | core->tvnorm = norm; | ||
924 | fsc8 = norm_fsc8(norm); | ||
925 | adc_clock = xtal; | ||
926 | vdec_clock = fsc8; | ||
927 | step_db = fsc8; | ||
928 | step_dr = fsc8; | ||
929 | |||
930 | if (norm->id & V4L2_STD_SECAM) { | ||
931 | step_db = 4250000 * 8; | ||
932 | step_dr = 4406250 * 8; | ||
933 | } | ||
934 | |||
935 | dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n", | ||
936 | norm->name, fsc8, adc_clock, vdec_clock, step_db, step_dr); | ||
937 | set_pll(core,2,vdec_clock); | ||
938 | |||
939 | dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n", | ||
940 | norm->cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); | ||
941 | cx_andor(MO_INPUT_FORMAT, 0xf, norm->cxiformat); | ||
942 | |||
943 | #if 1 | ||
944 | // FIXME: as-is from DScaler | ||
945 | dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n", | ||
946 | norm->cxoformat, cx_read(MO_OUTPUT_FORMAT)); | ||
947 | cx_write(MO_OUTPUT_FORMAT, norm->cxoformat); | ||
948 | #endif | ||
949 | |||
950 | // MO_SCONV_REG = adc clock / video dec clock * 2^17 | ||
951 | tmp64 = adc_clock * (u64)(1 << 17); | ||
952 | do_div(tmp64, vdec_clock); | ||
953 | dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n", | ||
954 | (u32)tmp64, cx_read(MO_SCONV_REG)); | ||
955 | cx_write(MO_SCONV_REG, (u32)tmp64); | ||
956 | |||
957 | // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22 | ||
958 | tmp64 = step_db * (u64)(1 << 22); | ||
959 | do_div(tmp64, vdec_clock); | ||
960 | dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n", | ||
961 | (u32)tmp64, cx_read(MO_SUB_STEP)); | ||
962 | cx_write(MO_SUB_STEP, (u32)tmp64); | ||
963 | |||
964 | // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22 | ||
965 | tmp64 = step_dr * (u64)(1 << 22); | ||
966 | do_div(tmp64, vdec_clock); | ||
967 | dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n", | ||
968 | (u32)tmp64, cx_read(MO_SUB_STEP_DR)); | ||
969 | cx_write(MO_SUB_STEP_DR, (u32)tmp64); | ||
970 | |||
971 | // bdelay + agcdelay | ||
972 | bdelay = vdec_clock * 65 / 20000000 + 21; | ||
973 | agcdelay = vdec_clock * 68 / 20000000 + 15; | ||
974 | dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n", | ||
975 | (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay); | ||
976 | cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay); | ||
977 | |||
978 | // htotal | ||
979 | tmp64 = norm_htotal(norm) * (u64)vdec_clock; | ||
980 | do_div(tmp64, fsc8); | ||
981 | htotal = (u32)tmp64 | (norm_notchfilter(norm) << 11); | ||
982 | dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n", | ||
983 | htotal, cx_read(MO_HTOTAL), (u32)tmp64); | ||
984 | cx_write(MO_HTOTAL, htotal); | ||
985 | |||
986 | // vbi stuff | ||
987 | cx_write(MO_VBI_PACKET, ((1 << 11) | /* (norm_vdelay(norm) << 11) | */ | ||
988 | norm_vbipack(norm))); | ||
989 | |||
990 | // this is needed as well to set all tvnorm parameter | ||
991 | cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED); | ||
992 | |||
993 | // audio | ||
994 | set_tvaudio(core); | ||
995 | |||
996 | // tell i2c chips | ||
997 | #ifdef V4L2_I2C_CLIENTS | ||
998 | cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm->id); | ||
999 | #else | ||
1000 | { | ||
1001 | struct video_channel c; | ||
1002 | memset(&c,0,sizeof(c)); | ||
1003 | c.channel = core->input; | ||
1004 | c.norm = VIDEO_MODE_PAL; | ||
1005 | if ((norm->id & (V4L2_STD_NTSC_M|V4L2_STD_NTSC_M_JP))) | ||
1006 | c.norm = VIDEO_MODE_NTSC; | ||
1007 | if (norm->id & V4L2_STD_SECAM) | ||
1008 | c.norm = VIDEO_MODE_SECAM; | ||
1009 | cx88_call_i2c_clients(core,VIDIOCSCHAN,&c); | ||
1010 | } | ||
1011 | #endif | ||
1012 | |||
1013 | // done | ||
1014 | return 0; | ||
1015 | } | ||
1016 | |||
1017 | /* ------------------------------------------------------------------ */ | ||
1018 | |||
1019 | static int cx88_pci_quirks(char *name, struct pci_dev *pci) | ||
1020 | { | ||
1021 | unsigned int lat = UNSET; | ||
1022 | u8 ctrl = 0; | ||
1023 | u8 value; | ||
1024 | |||
1025 | /* check pci quirks */ | ||
1026 | if (pci_pci_problems & PCIPCI_TRITON) { | ||
1027 | printk(KERN_INFO "%s: quirk: PCIPCI_TRITON -- set TBFX\n", | ||
1028 | name); | ||
1029 | ctrl |= CX88X_EN_TBFX; | ||
1030 | } | ||
1031 | if (pci_pci_problems & PCIPCI_NATOMA) { | ||
1032 | printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA -- set TBFX\n", | ||
1033 | name); | ||
1034 | ctrl |= CX88X_EN_TBFX; | ||
1035 | } | ||
1036 | if (pci_pci_problems & PCIPCI_VIAETBF) { | ||
1037 | printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF -- set TBFX\n", | ||
1038 | name); | ||
1039 | ctrl |= CX88X_EN_TBFX; | ||
1040 | } | ||
1041 | if (pci_pci_problems & PCIPCI_VSFX) { | ||
1042 | printk(KERN_INFO "%s: quirk: PCIPCI_VSFX -- set VSFX\n", | ||
1043 | name); | ||
1044 | ctrl |= CX88X_EN_VSFX; | ||
1045 | } | ||
1046 | #ifdef PCIPCI_ALIMAGIK | ||
1047 | if (pci_pci_problems & PCIPCI_ALIMAGIK) { | ||
1048 | printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n", | ||
1049 | name); | ||
1050 | lat = 0x0A; | ||
1051 | } | ||
1052 | #endif | ||
1053 | |||
1054 | /* check insmod options */ | ||
1055 | if (UNSET != latency) | ||
1056 | lat = latency; | ||
1057 | |||
1058 | /* apply stuff */ | ||
1059 | if (ctrl) { | ||
1060 | pci_read_config_byte(pci, CX88X_DEVCTRL, &value); | ||
1061 | value |= ctrl; | ||
1062 | pci_write_config_byte(pci, CX88X_DEVCTRL, value); | ||
1063 | } | ||
1064 | if (UNSET != lat) { | ||
1065 | printk(KERN_INFO "%s: setting pci latency timer to %d\n", | ||
1066 | name, latency); | ||
1067 | pci_write_config_byte(pci, PCI_LATENCY_TIMER, latency); | ||
1068 | } | ||
1069 | return 0; | ||
1070 | } | ||
1071 | |||
1072 | /* ------------------------------------------------------------------ */ | ||
1073 | |||
1074 | struct video_device *cx88_vdev_init(struct cx88_core *core, | ||
1075 | struct pci_dev *pci, | ||
1076 | struct video_device *template, | ||
1077 | char *type) | ||
1078 | { | ||
1079 | struct video_device *vfd; | ||
1080 | |||
1081 | vfd = video_device_alloc(); | ||
1082 | if (NULL == vfd) | ||
1083 | return NULL; | ||
1084 | *vfd = *template; | ||
1085 | vfd->minor = -1; | ||
1086 | vfd->dev = &pci->dev; | ||
1087 | vfd->release = video_device_release; | ||
1088 | snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", | ||
1089 | core->name, type, cx88_boards[core->board].name); | ||
1090 | return vfd; | ||
1091 | } | ||
1092 | |||
1093 | static int get_ressources(struct cx88_core *core, struct pci_dev *pci) | ||
1094 | { | ||
1095 | if (request_mem_region(pci_resource_start(pci,0), | ||
1096 | pci_resource_len(pci,0), | ||
1097 | core->name)) | ||
1098 | return 0; | ||
1099 | printk(KERN_ERR "%s: can't get MMIO memory @ 0x%lx\n", | ||
1100 | core->name,pci_resource_start(pci,0)); | ||
1101 | return -EBUSY; | ||
1102 | } | ||
1103 | |||
1104 | struct cx88_core* cx88_core_get(struct pci_dev *pci) | ||
1105 | { | ||
1106 | struct cx88_core *core; | ||
1107 | struct list_head *item; | ||
1108 | int i; | ||
1109 | |||
1110 | down(&devlist); | ||
1111 | list_for_each(item,&cx88_devlist) { | ||
1112 | core = list_entry(item, struct cx88_core, devlist); | ||
1113 | if (pci->bus->number != core->pci_bus) | ||
1114 | continue; | ||
1115 | if (PCI_SLOT(pci->devfn) != core->pci_slot) | ||
1116 | continue; | ||
1117 | |||
1118 | if (0 != get_ressources(core,pci)) | ||
1119 | goto fail_unlock; | ||
1120 | atomic_inc(&core->refcount); | ||
1121 | up(&devlist); | ||
1122 | return core; | ||
1123 | } | ||
1124 | core = kmalloc(sizeof(*core),GFP_KERNEL); | ||
1125 | if (NULL == core) | ||
1126 | goto fail_unlock; | ||
1127 | |||
1128 | memset(core,0,sizeof(*core)); | ||
1129 | atomic_inc(&core->refcount); | ||
1130 | core->pci_bus = pci->bus->number; | ||
1131 | core->pci_slot = PCI_SLOT(pci->devfn); | ||
1132 | core->pci_irqmask = 0x00fc00; | ||
1133 | |||
1134 | core->nr = cx88_devcount++; | ||
1135 | sprintf(core->name,"cx88[%d]",core->nr); | ||
1136 | if (0 != get_ressources(core,pci)) { | ||
1137 | cx88_devcount--; | ||
1138 | goto fail_free; | ||
1139 | } | ||
1140 | list_add_tail(&core->devlist,&cx88_devlist); | ||
1141 | |||
1142 | /* PCI stuff */ | ||
1143 | cx88_pci_quirks(core->name, pci); | ||
1144 | core->lmmio = ioremap(pci_resource_start(pci,0), | ||
1145 | pci_resource_len(pci,0)); | ||
1146 | core->bmmio = (u8 __iomem *)core->lmmio; | ||
1147 | |||
1148 | /* board config */ | ||
1149 | core->board = UNSET; | ||
1150 | if (card[core->nr] < cx88_bcount) | ||
1151 | core->board = card[core->nr]; | ||
1152 | for (i = 0; UNSET == core->board && i < cx88_idcount; i++) | ||
1153 | if (pci->subsystem_vendor == cx88_subids[i].subvendor && | ||
1154 | pci->subsystem_device == cx88_subids[i].subdevice) | ||
1155 | core->board = cx88_subids[i].card; | ||
1156 | if (UNSET == core->board) { | ||
1157 | core->board = CX88_BOARD_UNKNOWN; | ||
1158 | cx88_card_list(core,pci); | ||
1159 | } | ||
1160 | printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", | ||
1161 | core->name,pci->subsystem_vendor, | ||
1162 | pci->subsystem_device,cx88_boards[core->board].name, | ||
1163 | core->board, card[core->nr] == core->board ? | ||
1164 | "insmod option" : "autodetected"); | ||
1165 | |||
1166 | core->tuner_type = tuner[core->nr]; | ||
1167 | if (UNSET == core->tuner_type) | ||
1168 | core->tuner_type = cx88_boards[core->board].tuner_type; | ||
1169 | core->tda9887_conf = cx88_boards[core->board].tda9887_conf; | ||
1170 | |||
1171 | /* init hardware */ | ||
1172 | cx88_reset(core); | ||
1173 | cx88_i2c_init(core,pci); | ||
1174 | cx88_card_setup(core); | ||
1175 | cx88_ir_init(core,pci); | ||
1176 | |||
1177 | up(&devlist); | ||
1178 | return core; | ||
1179 | |||
1180 | fail_free: | ||
1181 | kfree(core); | ||
1182 | fail_unlock: | ||
1183 | up(&devlist); | ||
1184 | return NULL; | ||
1185 | } | ||
1186 | |||
1187 | void cx88_core_put(struct cx88_core *core, struct pci_dev *pci) | ||
1188 | { | ||
1189 | release_mem_region(pci_resource_start(pci,0), | ||
1190 | pci_resource_len(pci,0)); | ||
1191 | |||
1192 | if (!atomic_dec_and_test(&core->refcount)) | ||
1193 | return; | ||
1194 | |||
1195 | down(&devlist); | ||
1196 | cx88_ir_fini(core); | ||
1197 | if (0 == core->i2c_rc) | ||
1198 | i2c_bit_del_bus(&core->i2c_adap); | ||
1199 | list_del(&core->devlist); | ||
1200 | iounmap(core->lmmio); | ||
1201 | cx88_devcount--; | ||
1202 | up(&devlist); | ||
1203 | kfree(core); | ||
1204 | } | ||
1205 | |||
1206 | /* ------------------------------------------------------------------ */ | ||
1207 | |||
1208 | EXPORT_SYMBOL(cx88_print_ioctl); | ||
1209 | EXPORT_SYMBOL(cx88_pci_irqs); | ||
1210 | EXPORT_SYMBOL(cx88_vid_irqs); | ||
1211 | EXPORT_SYMBOL(cx88_mpeg_irqs); | ||
1212 | EXPORT_SYMBOL(cx88_print_irqbits); | ||
1213 | |||
1214 | EXPORT_SYMBOL(cx88_core_irq); | ||
1215 | EXPORT_SYMBOL(cx88_wakeup); | ||
1216 | EXPORT_SYMBOL(cx88_reset); | ||
1217 | EXPORT_SYMBOL(cx88_shutdown); | ||
1218 | |||
1219 | EXPORT_SYMBOL(cx88_risc_buffer); | ||
1220 | EXPORT_SYMBOL(cx88_risc_databuffer); | ||
1221 | EXPORT_SYMBOL(cx88_risc_stopper); | ||
1222 | EXPORT_SYMBOL(cx88_free_buffer); | ||
1223 | |||
1224 | EXPORT_SYMBOL(cx88_sram_channels); | ||
1225 | EXPORT_SYMBOL(cx88_sram_channel_setup); | ||
1226 | EXPORT_SYMBOL(cx88_sram_channel_dump); | ||
1227 | |||
1228 | EXPORT_SYMBOL(cx88_set_tvnorm); | ||
1229 | EXPORT_SYMBOL(cx88_set_scale); | ||
1230 | |||
1231 | EXPORT_SYMBOL(cx88_vdev_init); | ||
1232 | EXPORT_SYMBOL(cx88_core_get); | ||
1233 | EXPORT_SYMBOL(cx88_core_put); | ||
1234 | |||
1235 | /* | ||
1236 | * Local variables: | ||
1237 | * c-basic-offset: 8 | ||
1238 | * End: | ||
1239 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c new file mode 100644 index 000000000000..bc6f18c45357 --- /dev/null +++ b/drivers/media/video/cx88/cx88-dvb.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | * $Id: cx88-dvb.c,v 1.31 2005/03/07 15:58:05 kraxel Exp $ | ||
3 | * | ||
4 | * device driver for Conexant 2388x based TV cards | ||
5 | * MPEG Transport Stream (DVB) routines | ||
6 | * | ||
7 | * (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au> | ||
8 | * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | */ | ||
24 | |||
25 | #include <linux/module.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/device.h> | ||
28 | #include <linux/fs.h> | ||
29 | #include <linux/kthread.h> | ||
30 | #include <linux/file.h> | ||
31 | #include <linux/suspend.h> | ||
32 | |||
33 | /* those two frontends need merging via linuxtv cvs ... */ | ||
34 | #define HAVE_CX22702 0 | ||
35 | #define HAVE_OR51132 1 | ||
36 | |||
37 | #include "cx88.h" | ||
38 | #include "dvb-pll.h" | ||
39 | #include "mt352.h" | ||
40 | #include "mt352_priv.h" | ||
41 | #if HAVE_CX22702 | ||
42 | # include "cx22702.h" | ||
43 | #endif | ||
44 | #if HAVE_OR51132 | ||
45 | # include "or51132.h" | ||
46 | #endif | ||
47 | |||
48 | MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); | ||
49 | MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); | ||
50 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
51 | MODULE_LICENSE("GPL"); | ||
52 | |||
53 | static unsigned int debug = 0; | ||
54 | module_param(debug, int, 0644); | ||
55 | MODULE_PARM_DESC(debug,"enable debug messages [dvb]"); | ||
56 | |||
57 | #define dprintk(level,fmt, arg...) if (debug >= level) \ | ||
58 | printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->core->name , ## arg) | ||
59 | |||
60 | /* ------------------------------------------------------------------ */ | ||
61 | |||
62 | static int dvb_buf_setup(struct videobuf_queue *q, | ||
63 | unsigned int *count, unsigned int *size) | ||
64 | { | ||
65 | struct cx8802_dev *dev = q->priv_data; | ||
66 | |||
67 | dev->ts_packet_size = 188 * 4; | ||
68 | dev->ts_packet_count = 32; | ||
69 | |||
70 | *size = dev->ts_packet_size * dev->ts_packet_count; | ||
71 | *count = 32; | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static int dvb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, | ||
76 | enum v4l2_field field) | ||
77 | { | ||
78 | struct cx8802_dev *dev = q->priv_data; | ||
79 | return cx8802_buf_prepare(dev, (struct cx88_buffer*)vb); | ||
80 | } | ||
81 | |||
82 | static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
83 | { | ||
84 | struct cx8802_dev *dev = q->priv_data; | ||
85 | cx8802_buf_queue(dev, (struct cx88_buffer*)vb); | ||
86 | } | ||
87 | |||
88 | static void dvb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
89 | { | ||
90 | struct cx8802_dev *dev = q->priv_data; | ||
91 | cx88_free_buffer(dev->pci, (struct cx88_buffer*)vb); | ||
92 | } | ||
93 | |||
94 | struct videobuf_queue_ops dvb_qops = { | ||
95 | .buf_setup = dvb_buf_setup, | ||
96 | .buf_prepare = dvb_buf_prepare, | ||
97 | .buf_queue = dvb_buf_queue, | ||
98 | .buf_release = dvb_buf_release, | ||
99 | }; | ||
100 | |||
101 | /* ------------------------------------------------------------------ */ | ||
102 | |||
103 | static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe) | ||
104 | { | ||
105 | static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x39 }; | ||
106 | static u8 reset [] = { RESET, 0x80 }; | ||
107 | static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; | ||
108 | static u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 }; | ||
109 | static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; | ||
110 | static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; | ||
111 | |||
112 | mt352_write(fe, clock_config, sizeof(clock_config)); | ||
113 | udelay(200); | ||
114 | mt352_write(fe, reset, sizeof(reset)); | ||
115 | mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); | ||
116 | |||
117 | mt352_write(fe, agc_cfg, sizeof(agc_cfg)); | ||
118 | mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); | ||
119 | mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe) | ||
124 | { | ||
125 | static u8 clock_config [] = { 0x89, 0x38, 0x39 }; | ||
126 | static u8 reset [] = { 0x50, 0x80 }; | ||
127 | static u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 }; | ||
128 | static u8 agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF, | ||
129 | 0x00, 0xFF, 0x00, 0x40, 0x40 }; | ||
130 | static u8 dntv_extra[] = { 0xB5, 0x7A }; | ||
131 | static u8 capt_range_cfg[] = { 0x75, 0x32 }; | ||
132 | |||
133 | mt352_write(fe, clock_config, sizeof(clock_config)); | ||
134 | udelay(2000); | ||
135 | mt352_write(fe, reset, sizeof(reset)); | ||
136 | mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); | ||
137 | |||
138 | mt352_write(fe, agc_cfg, sizeof(agc_cfg)); | ||
139 | udelay(2000); | ||
140 | mt352_write(fe, dntv_extra, sizeof(dntv_extra)); | ||
141 | mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); | ||
142 | |||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | static int mt352_pll_set(struct dvb_frontend* fe, | ||
147 | struct dvb_frontend_parameters* params, | ||
148 | u8* pllbuf) | ||
149 | { | ||
150 | struct cx8802_dev *dev= fe->dvb->priv; | ||
151 | |||
152 | pllbuf[0] = dev->core->pll_addr << 1; | ||
153 | dvb_pll_configure(dev->core->pll_desc, pllbuf+1, | ||
154 | params->frequency, | ||
155 | params->u.ofdm.bandwidth); | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static struct mt352_config dvico_fusionhdtv = { | ||
160 | .demod_address = 0x0F, | ||
161 | .demod_init = dvico_fusionhdtv_demod_init, | ||
162 | .pll_set = mt352_pll_set, | ||
163 | }; | ||
164 | |||
165 | static struct mt352_config dntv_live_dvbt_config = { | ||
166 | .demod_address = 0x0f, | ||
167 | .demod_init = dntv_live_dvbt_demod_init, | ||
168 | .pll_set = mt352_pll_set, | ||
169 | }; | ||
170 | |||
171 | #if HAVE_CX22702 | ||
172 | static struct cx22702_config connexant_refboard_config = { | ||
173 | .demod_address = 0x43, | ||
174 | .pll_address = 0x60, | ||
175 | .pll_desc = &dvb_pll_thomson_dtt7579, | ||
176 | }; | ||
177 | |||
178 | static struct cx22702_config hauppauge_novat_config = { | ||
179 | .demod_address = 0x43, | ||
180 | .pll_address = 0x61, | ||
181 | .pll_desc = &dvb_pll_thomson_dtt759x, | ||
182 | }; | ||
183 | #endif | ||
184 | |||
185 | #if HAVE_OR51132 | ||
186 | static int or51132_set_ts_param(struct dvb_frontend* fe, | ||
187 | int is_punctured) | ||
188 | { | ||
189 | struct cx8802_dev *dev= fe->dvb->priv; | ||
190 | dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00; | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | struct or51132_config pchdtv_hd3000 = { | ||
195 | .demod_address = 0x15, | ||
196 | .pll_address = 0x61, | ||
197 | .pll_desc = &dvb_pll_thomson_dtt7610, | ||
198 | .set_ts_params = or51132_set_ts_param, | ||
199 | }; | ||
200 | #endif | ||
201 | |||
202 | static int dvb_register(struct cx8802_dev *dev) | ||
203 | { | ||
204 | /* init struct videobuf_dvb */ | ||
205 | dev->dvb.name = dev->core->name; | ||
206 | dev->ts_gen_cntrl = 0x0c; | ||
207 | |||
208 | /* init frontend */ | ||
209 | switch (dev->core->board) { | ||
210 | #if HAVE_CX22702 | ||
211 | case CX88_BOARD_HAUPPAUGE_DVB_T1: | ||
212 | dev->dvb.frontend = cx22702_attach(&hauppauge_novat_config, | ||
213 | &dev->core->i2c_adap); | ||
214 | break; | ||
215 | case CX88_BOARD_CONEXANT_DVB_T1: | ||
216 | dev->dvb.frontend = cx22702_attach(&connexant_refboard_config, | ||
217 | &dev->core->i2c_adap); | ||
218 | break; | ||
219 | #endif | ||
220 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: | ||
221 | dev->core->pll_addr = 0x61; | ||
222 | dev->core->pll_desc = &dvb_pll_lg_z201; | ||
223 | dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv, | ||
224 | &dev->core->i2c_adap); | ||
225 | break; | ||
226 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: | ||
227 | dev->core->pll_addr = 0x60; | ||
228 | dev->core->pll_desc = &dvb_pll_thomson_dtt7579; | ||
229 | dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv, | ||
230 | &dev->core->i2c_adap); | ||
231 | break; | ||
232 | case CX88_BOARD_KWORLD_DVB_T: | ||
233 | case CX88_BOARD_DNTV_LIVE_DVB_T: | ||
234 | dev->core->pll_addr = 0x61; | ||
235 | dev->core->pll_desc = &dvb_pll_unknown_1; | ||
236 | dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_config, | ||
237 | &dev->core->i2c_adap); | ||
238 | break; | ||
239 | #if HAVE_OR51132 | ||
240 | case CX88_BOARD_PCHDTV_HD3000: | ||
241 | dev->dvb.frontend = or51132_attach(&pchdtv_hd3000, | ||
242 | &dev->core->i2c_adap); | ||
243 | break; | ||
244 | #endif | ||
245 | default: | ||
246 | printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n" | ||
247 | "%s: you might want to look out for patches here:\n" | ||
248 | "%s: http://dl.bytesex.org/patches/\n", | ||
249 | dev->core->name, dev->core->name, dev->core->name); | ||
250 | break; | ||
251 | } | ||
252 | if (NULL == dev->dvb.frontend) { | ||
253 | printk("%s: frontend initialization failed\n",dev->core->name); | ||
254 | return -1; | ||
255 | } | ||
256 | |||
257 | if (dev->core->pll_desc) { | ||
258 | dev->dvb.frontend->ops->info.frequency_min = dev->core->pll_desc->min; | ||
259 | dev->dvb.frontend->ops->info.frequency_max = dev->core->pll_desc->max; | ||
260 | } | ||
261 | |||
262 | /* Copy the board name into the DVB structure */ | ||
263 | strlcpy(dev->dvb.frontend->ops->info.name, | ||
264 | cx88_boards[dev->core->board].name, | ||
265 | sizeof(dev->dvb.frontend->ops->info.name)); | ||
266 | |||
267 | /* register everything */ | ||
268 | return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev); | ||
269 | } | ||
270 | |||
271 | /* ----------------------------------------------------------- */ | ||
272 | |||
273 | static int __devinit dvb_probe(struct pci_dev *pci_dev, | ||
274 | const struct pci_device_id *pci_id) | ||
275 | { | ||
276 | struct cx8802_dev *dev; | ||
277 | struct cx88_core *core; | ||
278 | int err; | ||
279 | |||
280 | /* general setup */ | ||
281 | core = cx88_core_get(pci_dev); | ||
282 | if (NULL == core) | ||
283 | return -EINVAL; | ||
284 | |||
285 | err = -ENODEV; | ||
286 | if (!cx88_boards[core->board].dvb) | ||
287 | goto fail_core; | ||
288 | |||
289 | err = -ENOMEM; | ||
290 | dev = kmalloc(sizeof(*dev),GFP_KERNEL); | ||
291 | if (NULL == dev) | ||
292 | goto fail_core; | ||
293 | memset(dev,0,sizeof(*dev)); | ||
294 | dev->pci = pci_dev; | ||
295 | dev->core = core; | ||
296 | |||
297 | err = cx8802_init_common(dev); | ||
298 | if (0 != err) | ||
299 | goto fail_free; | ||
300 | |||
301 | /* dvb stuff */ | ||
302 | printk("%s/2: cx2388x based dvb card\n", core->name); | ||
303 | videobuf_queue_init(&dev->dvb.dvbq, &dvb_qops, | ||
304 | dev->pci, &dev->slock, | ||
305 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
306 | V4L2_FIELD_TOP, | ||
307 | sizeof(struct cx88_buffer), | ||
308 | dev); | ||
309 | err = dvb_register(dev); | ||
310 | if (0 != err) | ||
311 | goto fail_free; | ||
312 | return 0; | ||
313 | |||
314 | fail_free: | ||
315 | kfree(dev); | ||
316 | fail_core: | ||
317 | cx88_core_put(core,pci_dev); | ||
318 | return err; | ||
319 | } | ||
320 | |||
321 | static void __devexit dvb_remove(struct pci_dev *pci_dev) | ||
322 | { | ||
323 | struct cx8802_dev *dev = pci_get_drvdata(pci_dev); | ||
324 | |||
325 | /* dvb */ | ||
326 | videobuf_dvb_unregister(&dev->dvb); | ||
327 | |||
328 | /* common */ | ||
329 | cx8802_fini_common(dev); | ||
330 | cx88_core_put(dev->core,dev->pci); | ||
331 | kfree(dev); | ||
332 | } | ||
333 | |||
334 | static struct pci_device_id cx8802_pci_tbl[] = { | ||
335 | { | ||
336 | .vendor = 0x14f1, | ||
337 | .device = 0x8802, | ||
338 | .subvendor = PCI_ANY_ID, | ||
339 | .subdevice = PCI_ANY_ID, | ||
340 | },{ | ||
341 | /* --- end of list --- */ | ||
342 | } | ||
343 | }; | ||
344 | MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); | ||
345 | |||
346 | static struct pci_driver dvb_pci_driver = { | ||
347 | .name = "cx88-dvb", | ||
348 | .id_table = cx8802_pci_tbl, | ||
349 | .probe = dvb_probe, | ||
350 | .remove = __devexit_p(dvb_remove), | ||
351 | .suspend = cx8802_suspend_common, | ||
352 | .resume = cx8802_resume_common, | ||
353 | }; | ||
354 | |||
355 | static int dvb_init(void) | ||
356 | { | ||
357 | printk(KERN_INFO "cx2388x dvb driver version %d.%d.%d loaded\n", | ||
358 | (CX88_VERSION_CODE >> 16) & 0xff, | ||
359 | (CX88_VERSION_CODE >> 8) & 0xff, | ||
360 | CX88_VERSION_CODE & 0xff); | ||
361 | #ifdef SNAPSHOT | ||
362 | printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", | ||
363 | SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); | ||
364 | #endif | ||
365 | return pci_register_driver(&dvb_pci_driver); | ||
366 | } | ||
367 | |||
368 | static void dvb_fini(void) | ||
369 | { | ||
370 | pci_unregister_driver(&dvb_pci_driver); | ||
371 | } | ||
372 | |||
373 | module_init(dvb_init); | ||
374 | module_exit(dvb_fini); | ||
375 | |||
376 | /* | ||
377 | * Local variables: | ||
378 | * c-basic-offset: 8 | ||
379 | * compile-command: "make DVB=1" | ||
380 | * End: | ||
381 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c new file mode 100644 index 000000000000..60800172c026 --- /dev/null +++ b/drivers/media/video/cx88/cx88-i2c.c | |||
@@ -0,0 +1,213 @@ | |||
1 | /* | ||
2 | $Id: cx88-i2c.c,v 1.20 2005/02/15 15:59:35 kraxel Exp $ | ||
3 | |||
4 | cx88-i2c.c -- all the i2c code is here | ||
5 | |||
6 | Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) | ||
7 | & Marcus Metzler (mocm@thp.uni-koeln.de) | ||
8 | (c) 2002 Yurij Sysoev <yurij@naturesoft.net> | ||
9 | (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org> | ||
10 | |||
11 | This program is free software; you can redistribute it and/or modify | ||
12 | it under the terms of the GNU General Public License as published by | ||
13 | the Free Software Foundation; either version 2 of the License, or | ||
14 | (at your option) any later version. | ||
15 | |||
16 | This program is distributed in the hope that it will be useful, | ||
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | GNU General Public License for more details. | ||
20 | |||
21 | You should have received a copy of the GNU General Public License | ||
22 | along with this program; if not, write to the Free Software | ||
23 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | |||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <linux/init.h> | ||
30 | |||
31 | #include <asm/io.h> | ||
32 | |||
33 | #include "cx88.h" | ||
34 | |||
35 | static unsigned int i2c_debug = 0; | ||
36 | module_param(i2c_debug, int, 0644); | ||
37 | MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]"); | ||
38 | |||
39 | static unsigned int i2c_scan = 0; | ||
40 | module_param(i2c_scan, int, 0444); | ||
41 | MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); | ||
42 | |||
43 | #define dprintk(level,fmt, arg...) if (i2c_debug >= level) \ | ||
44 | printk(KERN_DEBUG "%s: " fmt, core->name , ## arg) | ||
45 | |||
46 | /* ----------------------------------------------------------------------- */ | ||
47 | |||
48 | void cx8800_bit_setscl(void *data, int state) | ||
49 | { | ||
50 | struct cx88_core *core = data; | ||
51 | |||
52 | if (state) | ||
53 | core->i2c_state |= 0x02; | ||
54 | else | ||
55 | core->i2c_state &= ~0x02; | ||
56 | cx_write(MO_I2C, core->i2c_state); | ||
57 | cx_read(MO_I2C); | ||
58 | } | ||
59 | |||
60 | void cx8800_bit_setsda(void *data, int state) | ||
61 | { | ||
62 | struct cx88_core *core = data; | ||
63 | |||
64 | if (state) | ||
65 | core->i2c_state |= 0x01; | ||
66 | else | ||
67 | core->i2c_state &= ~0x01; | ||
68 | cx_write(MO_I2C, core->i2c_state); | ||
69 | cx_read(MO_I2C); | ||
70 | } | ||
71 | |||
72 | static int cx8800_bit_getscl(void *data) | ||
73 | { | ||
74 | struct cx88_core *core = data; | ||
75 | u32 state; | ||
76 | |||
77 | state = cx_read(MO_I2C); | ||
78 | return state & 0x02 ? 1 : 0; | ||
79 | } | ||
80 | |||
81 | static int cx8800_bit_getsda(void *data) | ||
82 | { | ||
83 | struct cx88_core *core = data; | ||
84 | u32 state; | ||
85 | |||
86 | state = cx_read(MO_I2C); | ||
87 | return state & 0x01; | ||
88 | } | ||
89 | |||
90 | /* ----------------------------------------------------------------------- */ | ||
91 | |||
92 | static int attach_inform(struct i2c_client *client) | ||
93 | { | ||
94 | struct cx88_core *core = i2c_get_adapdata(client->adapter); | ||
95 | |||
96 | dprintk(1, "i2c attach [addr=0x%x,client=%s]\n", | ||
97 | client->addr, i2c_clientname(client)); | ||
98 | if (!client->driver->command) | ||
99 | return 0; | ||
100 | |||
101 | if (core->tuner_type != UNSET) | ||
102 | client->driver->command(client, TUNER_SET_TYPE, &core->tuner_type); | ||
103 | if (core->tda9887_conf) | ||
104 | client->driver->command(client, TDA9887_SET_CONFIG, &core->tda9887_conf); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int detach_inform(struct i2c_client *client) | ||
109 | { | ||
110 | struct cx88_core *core = i2c_get_adapdata(client->adapter); | ||
111 | |||
112 | dprintk(1, "i2c detach [client=%s]\n", i2c_clientname(client)); | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg) | ||
117 | { | ||
118 | if (0 != core->i2c_rc) | ||
119 | return; | ||
120 | i2c_clients_command(&core->i2c_adap, cmd, arg); | ||
121 | } | ||
122 | |||
123 | static struct i2c_algo_bit_data cx8800_i2c_algo_template = { | ||
124 | .setsda = cx8800_bit_setsda, | ||
125 | .setscl = cx8800_bit_setscl, | ||
126 | .getsda = cx8800_bit_getsda, | ||
127 | .getscl = cx8800_bit_getscl, | ||
128 | .udelay = 16, | ||
129 | .mdelay = 10, | ||
130 | .timeout = 200, | ||
131 | }; | ||
132 | |||
133 | /* ----------------------------------------------------------------------- */ | ||
134 | |||
135 | static struct i2c_adapter cx8800_i2c_adap_template = { | ||
136 | I2C_DEVNAME("cx2388x"), | ||
137 | .owner = THIS_MODULE, | ||
138 | .id = I2C_HW_B_CX2388x, | ||
139 | .client_register = attach_inform, | ||
140 | .client_unregister = detach_inform, | ||
141 | }; | ||
142 | |||
143 | static struct i2c_client cx8800_i2c_client_template = { | ||
144 | I2C_DEVNAME("cx88xx internal"), | ||
145 | }; | ||
146 | |||
147 | static char *i2c_devs[128] = { | ||
148 | [ 0x86 >> 1 ] = "tda9887/cx22702", | ||
149 | [ 0xa0 >> 1 ] = "eeprom", | ||
150 | [ 0xc0 >> 1 ] = "tuner (analog)", | ||
151 | [ 0xc2 >> 1 ] = "tuner (analog/dvb)", | ||
152 | }; | ||
153 | |||
154 | static void do_i2c_scan(char *name, struct i2c_client *c) | ||
155 | { | ||
156 | unsigned char buf; | ||
157 | int i,rc; | ||
158 | |||
159 | for (i = 0; i < 128; i++) { | ||
160 | c->addr = i; | ||
161 | rc = i2c_master_recv(c,&buf,0); | ||
162 | if (rc < 0) | ||
163 | continue; | ||
164 | printk("%s: i2c scan: found device @ 0x%x [%s]\n", | ||
165 | name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); | ||
166 | } | ||
167 | } | ||
168 | |||
169 | /* init + register i2c algo-bit adapter */ | ||
170 | int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) | ||
171 | { | ||
172 | memcpy(&core->i2c_adap, &cx8800_i2c_adap_template, | ||
173 | sizeof(core->i2c_adap)); | ||
174 | memcpy(&core->i2c_algo, &cx8800_i2c_algo_template, | ||
175 | sizeof(core->i2c_algo)); | ||
176 | memcpy(&core->i2c_client, &cx8800_i2c_client_template, | ||
177 | sizeof(core->i2c_client)); | ||
178 | |||
179 | if (core->tuner_type != TUNER_ABSENT) | ||
180 | core->i2c_adap.class |= I2C_CLASS_TV_ANALOG; | ||
181 | if (cx88_boards[core->board].dvb) | ||
182 | core->i2c_adap.class |= I2C_CLASS_TV_DIGITAL; | ||
183 | |||
184 | core->i2c_adap.dev.parent = &pci->dev; | ||
185 | strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name)); | ||
186 | core->i2c_algo.data = core; | ||
187 | i2c_set_adapdata(&core->i2c_adap,core); | ||
188 | core->i2c_adap.algo_data = &core->i2c_algo; | ||
189 | core->i2c_client.adapter = &core->i2c_adap; | ||
190 | |||
191 | cx8800_bit_setscl(core,1); | ||
192 | cx8800_bit_setsda(core,1); | ||
193 | |||
194 | core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap); | ||
195 | if (0 == core->i2c_rc) { | ||
196 | dprintk(1, "i2c register ok\n"); | ||
197 | if (i2c_scan) | ||
198 | do_i2c_scan(core->name,&core->i2c_client); | ||
199 | } else | ||
200 | printk("%s: i2c register FAILED\n", core->name); | ||
201 | return core->i2c_rc; | ||
202 | } | ||
203 | |||
204 | /* ----------------------------------------------------------------------- */ | ||
205 | |||
206 | EXPORT_SYMBOL(cx88_call_i2c_clients); | ||
207 | EXPORT_SYMBOL(cx88_i2c_init); | ||
208 | |||
209 | /* | ||
210 | * Local variables: | ||
211 | * c-basic-offset: 8 | ||
212 | * End: | ||
213 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c new file mode 100644 index 000000000000..af6ad8cdbdb7 --- /dev/null +++ b/drivers/media/video/cx88/cx88-input.c | |||
@@ -0,0 +1,396 @@ | |||
1 | /* | ||
2 | * $Id: cx88-input.c,v 1.9 2005/03/04 09:12:23 kraxel Exp $ | ||
3 | * | ||
4 | * Device driver for GPIO attached remote control interfaces | ||
5 | * on Conexant 2388x based TV/DVB cards. | ||
6 | * | ||
7 | * Copyright (c) 2003 Pavel Machek | ||
8 | * Copyright (c) 2004 Gerd Knorr | ||
9 | * Copyright (c) 2004 Chris Pascoe | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/init.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/input.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/moduleparam.h> | ||
32 | |||
33 | #include <media/ir-common.h> | ||
34 | |||
35 | #include "cx88.h" | ||
36 | |||
37 | /* ---------------------------------------------------------------------- */ | ||
38 | |||
39 | /* DigitalNow DNTV Live DVB-T Remote */ | ||
40 | static IR_KEYTAB_TYPE ir_codes_dntv_live_dvb_t[IR_KEYTAB_SIZE] = { | ||
41 | [ 0x00 ] = KEY_ESC, // 'go up a level?' | ||
42 | [ 0x01 ] = KEY_KP1, // '1' | ||
43 | [ 0x02 ] = KEY_KP2, // '2' | ||
44 | [ 0x03 ] = KEY_KP3, // '3' | ||
45 | [ 0x04 ] = KEY_KP4, // '4' | ||
46 | [ 0x05 ] = KEY_KP5, // '5' | ||
47 | [ 0x06 ] = KEY_KP6, // '6' | ||
48 | [ 0x07 ] = KEY_KP7, // '7' | ||
49 | [ 0x08 ] = KEY_KP8, // '8' | ||
50 | [ 0x09 ] = KEY_KP9, // '9' | ||
51 | [ 0x0a ] = KEY_KP0, // '0' | ||
52 | [ 0x0b ] = KEY_TUNER, // 'tv/fm' | ||
53 | [ 0x0c ] = KEY_SEARCH, // 'scan' | ||
54 | [ 0x0d ] = KEY_STOP, // 'stop' | ||
55 | [ 0x0e ] = KEY_PAUSE, // 'pause' | ||
56 | [ 0x0f ] = KEY_LIST, // 'source' | ||
57 | |||
58 | [ 0x10 ] = KEY_MUTE, // 'mute' | ||
59 | [ 0x11 ] = KEY_REWIND, // 'backward <<' | ||
60 | [ 0x12 ] = KEY_POWER, // 'power' | ||
61 | [ 0x13 ] = KEY_S, // 'snap' | ||
62 | [ 0x14 ] = KEY_AUDIO, // 'stereo' | ||
63 | [ 0x15 ] = KEY_CLEAR, // 'reset' | ||
64 | [ 0x16 ] = KEY_PLAY, // 'play' | ||
65 | [ 0x17 ] = KEY_ENTER, // 'enter' | ||
66 | [ 0x18 ] = KEY_ZOOM, // 'full screen' | ||
67 | [ 0x19 ] = KEY_FASTFORWARD, // 'forward >>' | ||
68 | [ 0x1a ] = KEY_CHANNELUP, // 'channel +' | ||
69 | [ 0x1b ] = KEY_VOLUMEUP, // 'volume +' | ||
70 | [ 0x1c ] = KEY_INFO, // 'preview' | ||
71 | [ 0x1d ] = KEY_RECORD, // 'record' | ||
72 | [ 0x1e ] = KEY_CHANNELDOWN, // 'channel -' | ||
73 | [ 0x1f ] = KEY_VOLUMEDOWN, // 'volume -' | ||
74 | }; | ||
75 | |||
76 | /* ---------------------------------------------------------------------- */ | ||
77 | |||
78 | /* IO-DATA BCTV7E Remote */ | ||
79 | static IR_KEYTAB_TYPE ir_codes_iodata_bctv7e[IR_KEYTAB_SIZE] = { | ||
80 | [ 0x40 ] = KEY_TV, // TV | ||
81 | [ 0x20 ] = KEY_RADIO, // FM | ||
82 | [ 0x60 ] = KEY_EPG, // EPG | ||
83 | [ 0x00 ] = KEY_POWER, // power | ||
84 | |||
85 | [ 0x50 ] = KEY_KP1, // 1 | ||
86 | [ 0x30 ] = KEY_KP2, // 2 | ||
87 | [ 0x70 ] = KEY_KP3, // 3 | ||
88 | [ 0x10 ] = KEY_L, // Live | ||
89 | |||
90 | [ 0x48 ] = KEY_KP4, // 4 | ||
91 | [ 0x28 ] = KEY_KP5, // 5 | ||
92 | [ 0x68 ] = KEY_KP6, // 6 | ||
93 | [ 0x08 ] = KEY_T, // Time Shift | ||
94 | |||
95 | [ 0x58 ] = KEY_KP7, // 7 | ||
96 | [ 0x38 ] = KEY_KP8, // 8 | ||
97 | [ 0x78 ] = KEY_KP9, // 9 | ||
98 | [ 0x18 ] = KEY_PLAYPAUSE, // Play | ||
99 | |||
100 | [ 0x44 ] = KEY_KP0, // 10 | ||
101 | [ 0x24 ] = KEY_ENTER, // 11 | ||
102 | [ 0x64 ] = KEY_ESC, // 12 | ||
103 | [ 0x04 ] = KEY_M, // Multi | ||
104 | |||
105 | [ 0x54 ] = KEY_VIDEO, // VIDEO | ||
106 | [ 0x34 ] = KEY_CHANNELUP, // channel + | ||
107 | [ 0x74 ] = KEY_VOLUMEUP, // volume + | ||
108 | [ 0x14 ] = KEY_MUTE, // Mute | ||
109 | |||
110 | [ 0x4c ] = KEY_S, // SVIDEO | ||
111 | [ 0x2c ] = KEY_CHANNELDOWN, // channel - | ||
112 | [ 0x6c ] = KEY_VOLUMEDOWN, // volume - | ||
113 | [ 0x0c ] = KEY_ZOOM, // Zoom | ||
114 | |||
115 | [ 0x5c ] = KEY_PAUSE, // pause | ||
116 | [ 0x3c ] = KEY_C, // || (red) | ||
117 | [ 0x7c ] = KEY_RECORD, // recording | ||
118 | [ 0x1c ] = KEY_STOP, // stop | ||
119 | |||
120 | [ 0x41 ] = KEY_REWIND, // backward << | ||
121 | [ 0x21 ] = KEY_PLAY, // play | ||
122 | [ 0x61 ] = KEY_FASTFORWARD, // forward >> | ||
123 | [ 0x01 ] = KEY_NEXT, // skip >| | ||
124 | }; | ||
125 | |||
126 | /* ---------------------------------------------------------------------- */ | ||
127 | |||
128 | struct cx88_IR { | ||
129 | struct cx88_core *core; | ||
130 | struct input_dev input; | ||
131 | struct ir_input_state ir; | ||
132 | char name[32]; | ||
133 | char phys[32]; | ||
134 | |||
135 | /* sample from gpio pin 16 */ | ||
136 | int sampling; | ||
137 | u32 samples[16]; | ||
138 | int scount; | ||
139 | unsigned long release; | ||
140 | |||
141 | /* poll external decoder */ | ||
142 | int polling; | ||
143 | struct work_struct work; | ||
144 | struct timer_list timer; | ||
145 | u32 gpio_addr; | ||
146 | u32 last_gpio; | ||
147 | u32 mask_keycode; | ||
148 | u32 mask_keydown; | ||
149 | u32 mask_keyup; | ||
150 | }; | ||
151 | |||
152 | static int ir_debug = 0; | ||
153 | module_param(ir_debug, int, 0644); /* debug level [IR] */ | ||
154 | MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); | ||
155 | |||
156 | #define ir_dprintk(fmt, arg...) if (ir_debug) \ | ||
157 | printk(KERN_DEBUG "%s IR: " fmt , ir->core->name, ## arg) | ||
158 | |||
159 | /* ---------------------------------------------------------------------- */ | ||
160 | |||
161 | static void cx88_ir_handle_key(struct cx88_IR *ir) | ||
162 | { | ||
163 | struct cx88_core *core = ir->core; | ||
164 | u32 gpio, data; | ||
165 | |||
166 | /* read gpio value */ | ||
167 | gpio = cx_read(ir->gpio_addr); | ||
168 | if (ir->polling) { | ||
169 | if (ir->last_gpio == gpio) | ||
170 | return; | ||
171 | ir->last_gpio = gpio; | ||
172 | } | ||
173 | |||
174 | /* extract data */ | ||
175 | data = ir_extract_bits(gpio, ir->mask_keycode); | ||
176 | ir_dprintk("irq gpio=0x%x code=%d | %s%s%s\n", | ||
177 | gpio, data, | ||
178 | ir->polling ? "poll" : "irq", | ||
179 | (gpio & ir->mask_keydown) ? " down" : "", | ||
180 | (gpio & ir->mask_keyup) ? " up" : ""); | ||
181 | |||
182 | if (ir->mask_keydown) { | ||
183 | /* bit set on keydown */ | ||
184 | if (gpio & ir->mask_keydown) { | ||
185 | ir_input_keydown(&ir->input,&ir->ir,data,data); | ||
186 | } else { | ||
187 | ir_input_nokey(&ir->input,&ir->ir); | ||
188 | } | ||
189 | |||
190 | } else if (ir->mask_keyup) { | ||
191 | /* bit cleared on keydown */ | ||
192 | if (0 == (gpio & ir->mask_keyup)) { | ||
193 | ir_input_keydown(&ir->input,&ir->ir,data,data); | ||
194 | } else { | ||
195 | ir_input_nokey(&ir->input,&ir->ir); | ||
196 | } | ||
197 | |||
198 | } else { | ||
199 | /* can't distinguish keydown/up :-/ */ | ||
200 | ir_input_keydown(&ir->input,&ir->ir,data,data); | ||
201 | ir_input_nokey(&ir->input,&ir->ir); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | static void ir_timer(unsigned long data) | ||
206 | { | ||
207 | struct cx88_IR *ir = (struct cx88_IR*)data; | ||
208 | |||
209 | schedule_work(&ir->work); | ||
210 | } | ||
211 | |||
212 | static void cx88_ir_work(void *data) | ||
213 | { | ||
214 | struct cx88_IR *ir = data; | ||
215 | unsigned long timeout; | ||
216 | |||
217 | cx88_ir_handle_key(ir); | ||
218 | timeout = jiffies + (ir->polling * HZ / 1000); | ||
219 | mod_timer(&ir->timer, timeout); | ||
220 | } | ||
221 | |||
222 | /* ---------------------------------------------------------------------- */ | ||
223 | |||
224 | int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) | ||
225 | { | ||
226 | struct cx88_IR *ir; | ||
227 | IR_KEYTAB_TYPE *ir_codes = NULL; | ||
228 | int ir_type = IR_TYPE_OTHER; | ||
229 | |||
230 | ir = kmalloc(sizeof(*ir),GFP_KERNEL); | ||
231 | if (NULL == ir) | ||
232 | return -ENOMEM; | ||
233 | memset(ir,0,sizeof(*ir)); | ||
234 | |||
235 | /* detect & configure */ | ||
236 | switch (core->board) { | ||
237 | case CX88_BOARD_DNTV_LIVE_DVB_T: | ||
238 | ir_codes = ir_codes_dntv_live_dvb_t; | ||
239 | ir->gpio_addr = MO_GP1_IO; | ||
240 | ir->mask_keycode = 0x1f; | ||
241 | ir->mask_keyup = 0x60; | ||
242 | ir->polling = 50; // ms | ||
243 | break; | ||
244 | case CX88_BOARD_HAUPPAUGE: | ||
245 | case CX88_BOARD_HAUPPAUGE_DVB_T1: | ||
246 | ir_codes = ir_codes_hauppauge_new; | ||
247 | ir_type = IR_TYPE_RC5; | ||
248 | ir->sampling = 1; | ||
249 | break; | ||
250 | case CX88_BOARD_WINFAST2000XP_EXPERT: | ||
251 | ir_codes = ir_codes_winfast; | ||
252 | ir->gpio_addr = MO_GP0_IO; | ||
253 | ir->mask_keycode = 0x8f8; | ||
254 | ir->mask_keyup = 0x100; | ||
255 | ir->polling = 1; // ms | ||
256 | break; | ||
257 | case CX88_BOARD_IODATA_GVBCTV7E: | ||
258 | ir_codes = ir_codes_iodata_bctv7e; | ||
259 | ir->gpio_addr = MO_GP0_IO; | ||
260 | ir->mask_keycode = 0xfd; | ||
261 | ir->mask_keydown = 0x02; | ||
262 | ir->polling = 5; // ms | ||
263 | break; | ||
264 | } | ||
265 | if (NULL == ir_codes) { | ||
266 | kfree(ir); | ||
267 | return -ENODEV; | ||
268 | } | ||
269 | |||
270 | /* init input device */ | ||
271 | snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", | ||
272 | cx88_boards[core->board].name); | ||
273 | snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", | ||
274 | pci_name(pci)); | ||
275 | |||
276 | ir_input_init(&ir->input, &ir->ir, ir_type, ir_codes); | ||
277 | ir->input.name = ir->name; | ||
278 | ir->input.phys = ir->phys; | ||
279 | ir->input.id.bustype = BUS_PCI; | ||
280 | ir->input.id.version = 1; | ||
281 | if (pci->subsystem_vendor) { | ||
282 | ir->input.id.vendor = pci->subsystem_vendor; | ||
283 | ir->input.id.product = pci->subsystem_device; | ||
284 | } else { | ||
285 | ir->input.id.vendor = pci->vendor; | ||
286 | ir->input.id.product = pci->device; | ||
287 | } | ||
288 | |||
289 | /* record handles to ourself */ | ||
290 | ir->core = core; | ||
291 | core->ir = ir; | ||
292 | |||
293 | if (ir->polling) { | ||
294 | INIT_WORK(&ir->work, cx88_ir_work, ir); | ||
295 | init_timer(&ir->timer); | ||
296 | ir->timer.function = ir_timer; | ||
297 | ir->timer.data = (unsigned long)ir; | ||
298 | schedule_work(&ir->work); | ||
299 | } | ||
300 | if (ir->sampling) { | ||
301 | core->pci_irqmask |= (1<<18); // IR_SMP_INT | ||
302 | cx_write(MO_DDS_IO, 0xa80a80); // 4 kHz sample rate | ||
303 | cx_write(MO_DDSCFG_IO, 0x5); // enable | ||
304 | } | ||
305 | |||
306 | /* all done */ | ||
307 | input_register_device(&ir->input); | ||
308 | printk("%s: registered IR remote control\n", core->name); | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | int cx88_ir_fini(struct cx88_core *core) | ||
314 | { | ||
315 | struct cx88_IR *ir = core->ir; | ||
316 | |||
317 | /* skip detach on non attached boards */ | ||
318 | if (NULL == ir) | ||
319 | return 0; | ||
320 | |||
321 | if (ir->polling) { | ||
322 | del_timer(&ir->timer); | ||
323 | flush_scheduled_work(); | ||
324 | } | ||
325 | |||
326 | input_unregister_device(&ir->input); | ||
327 | kfree(ir); | ||
328 | |||
329 | /* done */ | ||
330 | core->ir = NULL; | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | /* ---------------------------------------------------------------------- */ | ||
335 | |||
336 | void cx88_ir_irq(struct cx88_core *core) | ||
337 | { | ||
338 | struct cx88_IR *ir = core->ir; | ||
339 | u32 samples,rc5; | ||
340 | int i; | ||
341 | |||
342 | if (NULL == ir) | ||
343 | return; | ||
344 | if (!ir->sampling) | ||
345 | return; | ||
346 | |||
347 | samples = cx_read(MO_SAMPLE_IO); | ||
348 | if (0 != samples && 0xffffffff != samples) { | ||
349 | /* record sample data */ | ||
350 | if (ir->scount < ARRAY_SIZE(ir->samples)) | ||
351 | ir->samples[ir->scount++] = samples; | ||
352 | return; | ||
353 | } | ||
354 | if (!ir->scount) { | ||
355 | /* nothing to sample */ | ||
356 | if (ir->ir.keypressed && time_after(jiffies,ir->release)) | ||
357 | ir_input_nokey(&ir->input,&ir->ir); | ||
358 | return; | ||
359 | } | ||
360 | |||
361 | /* have a complete sample */ | ||
362 | if (ir->scount < ARRAY_SIZE(ir->samples)) | ||
363 | ir->samples[ir->scount++] = samples; | ||
364 | for (i = 0; i < ir->scount; i++) | ||
365 | ir->samples[i] = ~ir->samples[i]; | ||
366 | if (ir_debug) | ||
367 | ir_dump_samples(ir->samples,ir->scount); | ||
368 | |||
369 | /* decode it */ | ||
370 | switch (core->board) { | ||
371 | case CX88_BOARD_HAUPPAUGE: | ||
372 | case CX88_BOARD_HAUPPAUGE_DVB_T1: | ||
373 | rc5 = ir_decode_biphase(ir->samples,ir->scount,5,7); | ||
374 | ir_dprintk("biphase decoded: %x\n",rc5); | ||
375 | if ((rc5 & 0xfffff000) != 0x3000) | ||
376 | break; | ||
377 | ir_input_keydown(&ir->input, &ir->ir, rc5 & 0x3f, rc5); | ||
378 | ir->release = jiffies + msecs_to_jiffies(120); | ||
379 | break; | ||
380 | } | ||
381 | |||
382 | ir->scount = 0; | ||
383 | return; | ||
384 | } | ||
385 | |||
386 | /* ---------------------------------------------------------------------- */ | ||
387 | |||
388 | MODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe"); | ||
389 | MODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls"); | ||
390 | MODULE_LICENSE("GPL"); | ||
391 | |||
392 | /* | ||
393 | * Local variables: | ||
394 | * c-basic-offset: 8 | ||
395 | * End: | ||
396 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c new file mode 100644 index 000000000000..07aae1899e17 --- /dev/null +++ b/drivers/media/video/cx88/cx88-mpeg.c | |||
@@ -0,0 +1,466 @@ | |||
1 | /* | ||
2 | * $Id: cx88-mpeg.c,v 1.25 2005/03/07 14:18:00 kraxel Exp $ | ||
3 | * | ||
4 | * Support for the mpeg transport stream transfers | ||
5 | * PCI function #2 of the cx2388x. | ||
6 | * | ||
7 | * (c) 2004 Jelle Foks <jelle@foks.8m.com> | ||
8 | * (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au> | ||
9 | * (c) 2004 Gerd Knorr <kraxel@bytesex.org> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | */ | ||
25 | |||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/device.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <asm/delay.h> | ||
32 | |||
33 | #include "cx88.h" | ||
34 | |||
35 | /* ------------------------------------------------------------------ */ | ||
36 | |||
37 | MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards"); | ||
38 | MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>"); | ||
39 | MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); | ||
40 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
41 | MODULE_LICENSE("GPL"); | ||
42 | |||
43 | static unsigned int debug = 0; | ||
44 | module_param(debug,int,0644); | ||
45 | MODULE_PARM_DESC(debug,"enable debug messages [mpeg]"); | ||
46 | |||
47 | #define dprintk(level,fmt, arg...) if (debug >= level) \ | ||
48 | printk(KERN_DEBUG "%s/2: " fmt, dev->core->name , ## arg) | ||
49 | |||
50 | /* ------------------------------------------------------------------ */ | ||
51 | |||
52 | static int cx8802_start_dma(struct cx8802_dev *dev, | ||
53 | struct cx88_dmaqueue *q, | ||
54 | struct cx88_buffer *buf) | ||
55 | { | ||
56 | struct cx88_core *core = dev->core; | ||
57 | |||
58 | dprintk(1, "cx8802_start_mpegport_dma %d\n", buf->vb.width); | ||
59 | |||
60 | /* setup fifo + format */ | ||
61 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], | ||
62 | dev->ts_packet_size, buf->risc.dma); | ||
63 | |||
64 | /* write TS length to chip */ | ||
65 | cx_write(MO_TS_LNGTH, buf->vb.width); | ||
66 | |||
67 | #if 1 | ||
68 | /* FIXME: this needs a review. | ||
69 | * also: move to cx88-blackbird + cx88-dvb source files? */ | ||
70 | |||
71 | if (cx88_boards[core->board].dvb) { | ||
72 | /* negedge driven & software reset */ | ||
73 | cx_write(TS_GEN_CNTRL, 0x40); | ||
74 | udelay(100); | ||
75 | cx_write(MO_PINMUX_IO, 0x00); | ||
76 | cx_write(TS_HW_SOP_CNTRL,47<<16|188<<4|0x00); | ||
77 | cx_write(TS_SOP_STAT,0x00); | ||
78 | cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl); | ||
79 | udelay(100); | ||
80 | } | ||
81 | |||
82 | if (cx88_boards[core->board].blackbird) { | ||
83 | cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */ | ||
84 | |||
85 | // cx_write(TS_F2_CMD_STAT_MM, 0x2900106); /* F2_CMD_STAT_MM defaults + master + memory space */ | ||
86 | cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */ | ||
87 | udelay(100); | ||
88 | |||
89 | cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */ | ||
90 | //cx_write(TS_HW_SOP_CNTRL, 0x2F0BC0); /* mpeg start byte ts: 0x2F0BC0 ? */ | ||
91 | cx_write(TS_VALERR_CNTRL, 0x2000); | ||
92 | |||
93 | cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */ | ||
94 | udelay(100); | ||
95 | } | ||
96 | #endif | ||
97 | |||
98 | /* reset counter */ | ||
99 | cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET); | ||
100 | q->count = 1; | ||
101 | |||
102 | /* enable irqs */ | ||
103 | cx_set(MO_PCI_INTMSK, core->pci_irqmask | 0x04); | ||
104 | cx_write(MO_TS_INTMSK, 0x1f0011); | ||
105 | |||
106 | /* start dma */ | ||
107 | cx_write(MO_DEV_CNTRL2, (1<<5)); /* FIXME: s/write/set/ ??? */ | ||
108 | cx_write(MO_TS_DMACNTRL, 0x11); | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int cx8802_stop_dma(struct cx8802_dev *dev) | ||
113 | { | ||
114 | struct cx88_core *core = dev->core; | ||
115 | |||
116 | /* stop dma */ | ||
117 | cx_clear(MO_TS_DMACNTRL, 0x11); | ||
118 | |||
119 | /* disable irqs */ | ||
120 | cx_clear(MO_PCI_INTMSK, 0x000004); | ||
121 | cx_clear(MO_TS_INTMSK, 0x1f0011); | ||
122 | |||
123 | /* Reset the controller */ | ||
124 | cx_write(TS_GEN_CNTRL, 0xcd); | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int cx8802_restart_queue(struct cx8802_dev *dev, | ||
129 | struct cx88_dmaqueue *q) | ||
130 | { | ||
131 | struct cx88_buffer *buf; | ||
132 | struct list_head *item; | ||
133 | |||
134 | if (list_empty(&q->active)) | ||
135 | return 0; | ||
136 | |||
137 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
138 | dprintk(2,"restart_queue [%p/%d]: restart dma\n", | ||
139 | buf, buf->vb.i); | ||
140 | cx8802_start_dma(dev, q, buf); | ||
141 | list_for_each(item,&q->active) { | ||
142 | buf = list_entry(item, struct cx88_buffer, vb.queue); | ||
143 | buf->count = q->count++; | ||
144 | } | ||
145 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | /* ------------------------------------------------------------------ */ | ||
150 | |||
151 | int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf) | ||
152 | { | ||
153 | int size = dev->ts_packet_size * dev->ts_packet_count; | ||
154 | int rc; | ||
155 | |||
156 | dprintk(1, "%s: %p\n", __FUNCTION__, buf); | ||
157 | if (0 != buf->vb.baddr && buf->vb.bsize < size) | ||
158 | return -EINVAL; | ||
159 | |||
160 | if (STATE_NEEDS_INIT == buf->vb.state) { | ||
161 | buf->vb.width = dev->ts_packet_size; | ||
162 | buf->vb.height = dev->ts_packet_count; | ||
163 | buf->vb.size = size; | ||
164 | buf->vb.field = V4L2_FIELD_TOP; | ||
165 | |||
166 | if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL))) | ||
167 | goto fail; | ||
168 | cx88_risc_databuffer(dev->pci, &buf->risc, | ||
169 | buf->vb.dma.sglist, | ||
170 | buf->vb.width, buf->vb.height); | ||
171 | } | ||
172 | buf->vb.state = STATE_PREPARED; | ||
173 | return 0; | ||
174 | |||
175 | fail: | ||
176 | cx88_free_buffer(dev->pci,buf); | ||
177 | return rc; | ||
178 | } | ||
179 | |||
180 | void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) | ||
181 | { | ||
182 | struct cx88_buffer *prev; | ||
183 | struct cx88_dmaqueue *q = &dev->mpegq; | ||
184 | |||
185 | /* add jump to stopper */ | ||
186 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); | ||
187 | buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); | ||
188 | |||
189 | if (list_empty(&q->active)) { | ||
190 | list_add_tail(&buf->vb.queue,&q->active); | ||
191 | cx8802_start_dma(dev, q, buf); | ||
192 | buf->vb.state = STATE_ACTIVE; | ||
193 | buf->count = q->count++; | ||
194 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
195 | dprintk(2,"[%p/%d] %s - first active\n", | ||
196 | buf, buf->vb.i, __FUNCTION__); | ||
197 | |||
198 | } else { | ||
199 | prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); | ||
200 | list_add_tail(&buf->vb.queue,&q->active); | ||
201 | buf->vb.state = STATE_ACTIVE; | ||
202 | buf->count = q->count++; | ||
203 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
204 | dprintk(2,"[%p/%d] %s - append to active\n", | ||
205 | buf, buf->vb.i, __FUNCTION__); | ||
206 | } | ||
207 | } | ||
208 | |||
209 | /* ----------------------------------------------------------- */ | ||
210 | |||
211 | static void do_cancel_buffers(struct cx8802_dev *dev, char *reason, int restart) | ||
212 | { | ||
213 | struct cx88_dmaqueue *q = &dev->mpegq; | ||
214 | struct cx88_buffer *buf; | ||
215 | unsigned long flags; | ||
216 | |||
217 | spin_lock_irqsave(&dev->slock,flags); | ||
218 | while (!list_empty(&q->active)) { | ||
219 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
220 | list_del(&buf->vb.queue); | ||
221 | buf->vb.state = STATE_ERROR; | ||
222 | wake_up(&buf->vb.done); | ||
223 | dprintk(1,"[%p/%d] %s - dma=0x%08lx\n", | ||
224 | buf, buf->vb.i, reason, (unsigned long)buf->risc.dma); | ||
225 | } | ||
226 | if (restart) | ||
227 | cx8802_restart_queue(dev,q); | ||
228 | spin_unlock_irqrestore(&dev->slock,flags); | ||
229 | } | ||
230 | |||
231 | void cx8802_cancel_buffers(struct cx8802_dev *dev) | ||
232 | { | ||
233 | struct cx88_dmaqueue *q = &dev->mpegq; | ||
234 | |||
235 | del_timer_sync(&q->timeout); | ||
236 | cx8802_stop_dma(dev); | ||
237 | do_cancel_buffers(dev,"cancel",0); | ||
238 | } | ||
239 | |||
240 | static void cx8802_timeout(unsigned long data) | ||
241 | { | ||
242 | struct cx8802_dev *dev = (struct cx8802_dev*)data; | ||
243 | |||
244 | dprintk(1, "%s\n",__FUNCTION__); | ||
245 | |||
246 | if (debug) | ||
247 | cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); | ||
248 | cx8802_stop_dma(dev); | ||
249 | do_cancel_buffers(dev,"timeout",1); | ||
250 | } | ||
251 | |||
252 | static void cx8802_mpeg_irq(struct cx8802_dev *dev) | ||
253 | { | ||
254 | struct cx88_core *core = dev->core; | ||
255 | u32 status, mask, count; | ||
256 | |||
257 | status = cx_read(MO_TS_INTSTAT); | ||
258 | mask = cx_read(MO_TS_INTMSK); | ||
259 | if (0 == (status & mask)) | ||
260 | return; | ||
261 | |||
262 | cx_write(MO_TS_INTSTAT, status); | ||
263 | if (debug || (status & mask & ~0xff)) | ||
264 | cx88_print_irqbits(core->name, "irq mpeg ", | ||
265 | cx88_mpeg_irqs, status, mask); | ||
266 | |||
267 | /* risc op code error */ | ||
268 | if (status & (1 << 16)) { | ||
269 | printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name); | ||
270 | cx_clear(MO_TS_DMACNTRL, 0x11); | ||
271 | cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); | ||
272 | } | ||
273 | |||
274 | /* risc1 y */ | ||
275 | if (status & 0x01) { | ||
276 | spin_lock(&dev->slock); | ||
277 | count = cx_read(MO_TS_GPCNT); | ||
278 | cx88_wakeup(dev->core, &dev->mpegq, count); | ||
279 | spin_unlock(&dev->slock); | ||
280 | } | ||
281 | |||
282 | /* risc2 y */ | ||
283 | if (status & 0x10) { | ||
284 | spin_lock(&dev->slock); | ||
285 | cx8802_restart_queue(dev,&dev->mpegq); | ||
286 | spin_unlock(&dev->slock); | ||
287 | } | ||
288 | |||
289 | /* other general errors */ | ||
290 | if (status & 0x1f0100) { | ||
291 | spin_lock(&dev->slock); | ||
292 | cx8802_stop_dma(dev); | ||
293 | cx8802_restart_queue(dev,&dev->mpegq); | ||
294 | spin_unlock(&dev->slock); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | static irqreturn_t cx8802_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
299 | { | ||
300 | struct cx8802_dev *dev = dev_id; | ||
301 | struct cx88_core *core = dev->core; | ||
302 | u32 status; | ||
303 | int loop, handled = 0; | ||
304 | |||
305 | for (loop = 0; loop < 10; loop++) { | ||
306 | status = cx_read(MO_PCI_INTSTAT) & (core->pci_irqmask | 0x04); | ||
307 | if (0 == status) | ||
308 | goto out; | ||
309 | handled = 1; | ||
310 | cx_write(MO_PCI_INTSTAT, status); | ||
311 | |||
312 | if (status & core->pci_irqmask) | ||
313 | cx88_core_irq(core,status); | ||
314 | if (status & 0x04) | ||
315 | cx8802_mpeg_irq(dev); | ||
316 | }; | ||
317 | if (10 == loop) { | ||
318 | printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", | ||
319 | core->name); | ||
320 | cx_write(MO_PCI_INTMSK,0); | ||
321 | } | ||
322 | |||
323 | out: | ||
324 | return IRQ_RETVAL(handled); | ||
325 | } | ||
326 | |||
327 | /* ----------------------------------------------------------- */ | ||
328 | /* exported stuff */ | ||
329 | |||
330 | int cx8802_init_common(struct cx8802_dev *dev) | ||
331 | { | ||
332 | struct cx88_core *core = dev->core; | ||
333 | int err; | ||
334 | |||
335 | /* pci init */ | ||
336 | if (pci_enable_device(dev->pci)) | ||
337 | return -EIO; | ||
338 | pci_set_master(dev->pci); | ||
339 | if (!pci_dma_supported(dev->pci,0xffffffff)) { | ||
340 | printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name); | ||
341 | return -EIO; | ||
342 | } | ||
343 | |||
344 | pci_read_config_byte(dev->pci, PCI_CLASS_REVISION, &dev->pci_rev); | ||
345 | pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat); | ||
346 | printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, " | ||
347 | "latency: %d, mmio: 0x%lx\n", dev->core->name, | ||
348 | pci_name(dev->pci), dev->pci_rev, dev->pci->irq, | ||
349 | dev->pci_lat,pci_resource_start(dev->pci,0)); | ||
350 | |||
351 | /* initialize driver struct */ | ||
352 | init_MUTEX(&dev->lock); | ||
353 | spin_lock_init(&dev->slock); | ||
354 | |||
355 | /* init dma queue */ | ||
356 | INIT_LIST_HEAD(&dev->mpegq.active); | ||
357 | INIT_LIST_HEAD(&dev->mpegq.queued); | ||
358 | dev->mpegq.timeout.function = cx8802_timeout; | ||
359 | dev->mpegq.timeout.data = (unsigned long)dev; | ||
360 | init_timer(&dev->mpegq.timeout); | ||
361 | cx88_risc_stopper(dev->pci,&dev->mpegq.stopper, | ||
362 | MO_TS_DMACNTRL,0x11,0x00); | ||
363 | |||
364 | /* get irq */ | ||
365 | err = request_irq(dev->pci->irq, cx8802_irq, | ||
366 | SA_SHIRQ | SA_INTERRUPT, dev->core->name, dev); | ||
367 | if (err < 0) { | ||
368 | printk(KERN_ERR "%s: can't get IRQ %d\n", | ||
369 | dev->core->name, dev->pci->irq); | ||
370 | return err; | ||
371 | } | ||
372 | cx_set(MO_PCI_INTMSK, core->pci_irqmask); | ||
373 | |||
374 | /* everything worked */ | ||
375 | pci_set_drvdata(dev->pci,dev); | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | void cx8802_fini_common(struct cx8802_dev *dev) | ||
380 | { | ||
381 | cx8802_stop_dma(dev); | ||
382 | pci_disable_device(dev->pci); | ||
383 | |||
384 | /* unregister stuff */ | ||
385 | free_irq(dev->pci->irq, dev); | ||
386 | pci_set_drvdata(dev->pci, NULL); | ||
387 | |||
388 | /* free memory */ | ||
389 | btcx_riscmem_free(dev->pci,&dev->mpegq.stopper); | ||
390 | } | ||
391 | |||
392 | /* ----------------------------------------------------------- */ | ||
393 | |||
394 | int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state) | ||
395 | { | ||
396 | struct cx8802_dev *dev = pci_get_drvdata(pci_dev); | ||
397 | struct cx88_core *core = dev->core; | ||
398 | |||
399 | /* stop mpeg dma */ | ||
400 | spin_lock(&dev->slock); | ||
401 | if (!list_empty(&dev->mpegq.active)) { | ||
402 | printk("%s: suspend mpeg\n", core->name); | ||
403 | cx8802_stop_dma(dev); | ||
404 | del_timer(&dev->mpegq.timeout); | ||
405 | } | ||
406 | spin_unlock(&dev->slock); | ||
407 | |||
408 | #if 1 | ||
409 | /* FIXME -- shutdown device */ | ||
410 | cx88_shutdown(dev->core); | ||
411 | #endif | ||
412 | |||
413 | pci_save_state(pci_dev); | ||
414 | if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) { | ||
415 | pci_disable_device(pci_dev); | ||
416 | dev->state.disabled = 1; | ||
417 | } | ||
418 | return 0; | ||
419 | } | ||
420 | |||
421 | int cx8802_resume_common(struct pci_dev *pci_dev) | ||
422 | { | ||
423 | struct cx8802_dev *dev = pci_get_drvdata(pci_dev); | ||
424 | struct cx88_core *core = dev->core; | ||
425 | |||
426 | if (dev->state.disabled) { | ||
427 | pci_enable_device(pci_dev); | ||
428 | dev->state.disabled = 0; | ||
429 | } | ||
430 | pci_set_power_state(pci_dev, PCI_D0); | ||
431 | pci_restore_state(pci_dev); | ||
432 | |||
433 | #if 1 | ||
434 | /* FIXME: re-initialize hardware */ | ||
435 | cx88_reset(dev->core); | ||
436 | #endif | ||
437 | |||
438 | /* restart video+vbi capture */ | ||
439 | spin_lock(&dev->slock); | ||
440 | if (!list_empty(&dev->mpegq.active)) { | ||
441 | printk("%s: resume mpeg\n", core->name); | ||
442 | cx8802_restart_queue(dev,&dev->mpegq); | ||
443 | } | ||
444 | spin_unlock(&dev->slock); | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | /* ----------------------------------------------------------- */ | ||
450 | |||
451 | EXPORT_SYMBOL(cx8802_buf_prepare); | ||
452 | EXPORT_SYMBOL(cx8802_buf_queue); | ||
453 | EXPORT_SYMBOL(cx8802_cancel_buffers); | ||
454 | |||
455 | EXPORT_SYMBOL(cx8802_init_common); | ||
456 | EXPORT_SYMBOL(cx8802_fini_common); | ||
457 | |||
458 | EXPORT_SYMBOL(cx8802_suspend_common); | ||
459 | EXPORT_SYMBOL(cx8802_resume_common); | ||
460 | |||
461 | /* ----------------------------------------------------------- */ | ||
462 | /* | ||
463 | * Local variables: | ||
464 | * c-basic-offset: 8 | ||
465 | * End: | ||
466 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-reg.h b/drivers/media/video/cx88/cx88-reg.h new file mode 100644 index 000000000000..8638ce57d84c --- /dev/null +++ b/drivers/media/video/cx88/cx88-reg.h | |||
@@ -0,0 +1,787 @@ | |||
1 | /* | ||
2 | $Id: cx88-reg.h,v 1.6 2004/10/13 10:39:00 kraxel Exp $ | ||
3 | |||
4 | cx88x-hw.h - CX2388x register offsets | ||
5 | |||
6 | Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) | ||
7 | 2001 Michael Eskin | ||
8 | 2002 Yurij Sysoev <yurij@naturesoft.net> | ||
9 | 2003 Gerd Knorr <kraxel@bytesex.org> | ||
10 | |||
11 | This program is free software; you can redistribute it and/or modify | ||
12 | it under the terms of the GNU General Public License as published by | ||
13 | the Free Software Foundation; either version 2 of the License, or | ||
14 | (at your option) any later version. | ||
15 | |||
16 | This program is distributed in the hope that it will be useful, | ||
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | GNU General Public License for more details. | ||
20 | |||
21 | You should have received a copy of the GNU General Public License | ||
22 | along with this program; if not, write to the Free Software | ||
23 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | */ | ||
25 | |||
26 | #ifndef _CX88_REG_H_ | ||
27 | #define _CX88_REG_H_ | ||
28 | |||
29 | /* ---------------------------------------------------------------------- */ | ||
30 | /* PCI IDs and config space */ | ||
31 | |||
32 | #ifndef PCI_VENDOR_ID_CONEXANT | ||
33 | # define PCI_VENDOR_ID_CONEXANT 0x14F1 | ||
34 | #endif | ||
35 | #ifndef PCI_DEVICE_ID_CX2300_VID | ||
36 | # define PCI_DEVICE_ID_CX2300_VID 0x8800 | ||
37 | #endif | ||
38 | |||
39 | #define CX88X_DEVCTRL 0x40 | ||
40 | #define CX88X_EN_TBFX 0x02 | ||
41 | #define CX88X_EN_VSFX 0x04 | ||
42 | |||
43 | |||
44 | /* ---------------------------------------------------------------------- */ | ||
45 | /* DMA Controller registers */ | ||
46 | |||
47 | #define MO_PDMA_STHRSH 0x200000 // Source threshold | ||
48 | #define MO_PDMA_STADRS 0x200004 // Source target address | ||
49 | #define MO_PDMA_SIADRS 0x200008 // Source internal address | ||
50 | #define MO_PDMA_SCNTRL 0x20000C // Source control | ||
51 | #define MO_PDMA_DTHRSH 0x200010 // Destination threshold | ||
52 | #define MO_PDMA_DTADRS 0x200014 // Destination target address | ||
53 | #define MO_PDMA_DIADRS 0x200018 // Destination internal address | ||
54 | #define MO_PDMA_DCNTRL 0x20001C // Destination control | ||
55 | #define MO_LD_SSID 0x200030 // Load subsystem ID | ||
56 | #define MO_DEV_CNTRL2 0x200034 // Device control | ||
57 | #define MO_PCI_INTMSK 0x200040 // PCI interrupt mask | ||
58 | #define MO_PCI_INTSTAT 0x200044 // PCI interrupt status | ||
59 | #define MO_PCI_INTMSTAT 0x200048 // PCI interrupt masked status | ||
60 | #define MO_VID_INTMSK 0x200050 // Video interrupt mask | ||
61 | #define MO_VID_INTSTAT 0x200054 // Video interrupt status | ||
62 | #define MO_VID_INTMSTAT 0x200058 // Video interrupt masked status | ||
63 | #define MO_VID_INTSSTAT 0x20005C // Video interrupt set status | ||
64 | #define MO_AUD_INTMSK 0x200060 // Audio interrupt mask | ||
65 | #define MO_AUD_INTSTAT 0x200064 // Audio interrupt status | ||
66 | #define MO_AUD_INTMSTAT 0x200068 // Audio interrupt masked status | ||
67 | #define MO_AUD_INTSSTAT 0x20006C // Audio interrupt set status | ||
68 | #define MO_TS_INTMSK 0x200070 // Transport stream interrupt mask | ||
69 | #define MO_TS_INTSTAT 0x200074 // Transport stream interrupt status | ||
70 | #define MO_TS_INTMSTAT 0x200078 // Transport stream interrupt mask status | ||
71 | #define MO_TS_INTSSTAT 0x20007C // Transport stream interrupt set status | ||
72 | #define MO_VIP_INTMSK 0x200080 // VIP interrupt mask | ||
73 | #define MO_VIP_INTSTAT 0x200084 // VIP interrupt status | ||
74 | #define MO_VIP_INTMSTAT 0x200088 // VIP interrupt masked status | ||
75 | #define MO_VIP_INTSSTAT 0x20008C // VIP interrupt set status | ||
76 | #define MO_GPHST_INTMSK 0x200090 // Host interrupt mask | ||
77 | #define MO_GPHST_INTSTAT 0x200094 // Host interrupt status | ||
78 | #define MO_GPHST_INTMSTAT 0x200098 // Host interrupt masked status | ||
79 | #define MO_GPHST_INTSSTAT 0x20009C // Host interrupt set status | ||
80 | |||
81 | // DMA Channels 1-6 belong to SPIPE | ||
82 | #define MO_DMA7_PTR1 0x300018 // {24}RW* DMA Current Ptr : Ch#7 | ||
83 | #define MO_DMA8_PTR1 0x30001C // {24}RW* DMA Current Ptr : Ch#8 | ||
84 | |||
85 | // DMA Channels 9-20 belong to SPIPE | ||
86 | #define MO_DMA21_PTR1 0x300080 // {24}R0* DMA Current Ptr : Ch#21 | ||
87 | #define MO_DMA22_PTR1 0x300084 // {24}R0* DMA Current Ptr : Ch#22 | ||
88 | #define MO_DMA23_PTR1 0x300088 // {24}R0* DMA Current Ptr : Ch#23 | ||
89 | #define MO_DMA24_PTR1 0x30008C // {24}R0* DMA Current Ptr : Ch#24 | ||
90 | #define MO_DMA25_PTR1 0x300090 // {24}R0* DMA Current Ptr : Ch#25 | ||
91 | #define MO_DMA26_PTR1 0x300094 // {24}R0* DMA Current Ptr : Ch#26 | ||
92 | #define MO_DMA27_PTR1 0x300098 // {24}R0* DMA Current Ptr : Ch#27 | ||
93 | #define MO_DMA28_PTR1 0x30009C // {24}R0* DMA Current Ptr : Ch#28 | ||
94 | #define MO_DMA29_PTR1 0x3000A0 // {24}R0* DMA Current Ptr : Ch#29 | ||
95 | #define MO_DMA30_PTR1 0x3000A4 // {24}R0* DMA Current Ptr : Ch#30 | ||
96 | #define MO_DMA31_PTR1 0x3000A8 // {24}R0* DMA Current Ptr : Ch#31 | ||
97 | #define MO_DMA32_PTR1 0x3000AC // {24}R0* DMA Current Ptr : Ch#32 | ||
98 | |||
99 | #define MO_DMA21_PTR2 0x3000C0 // {24}RW* DMA Tab Ptr : Ch#21 | ||
100 | #define MO_DMA22_PTR2 0x3000C4 // {24}RW* DMA Tab Ptr : Ch#22 | ||
101 | #define MO_DMA23_PTR2 0x3000C8 // {24}RW* DMA Tab Ptr : Ch#23 | ||
102 | #define MO_DMA24_PTR2 0x3000CC // {24}RW* DMA Tab Ptr : Ch#24 | ||
103 | #define MO_DMA25_PTR2 0x3000D0 // {24}RW* DMA Tab Ptr : Ch#25 | ||
104 | #define MO_DMA26_PTR2 0x3000D4 // {24}RW* DMA Tab Ptr : Ch#26 | ||
105 | #define MO_DMA27_PTR2 0x3000D8 // {24}RW* DMA Tab Ptr : Ch#27 | ||
106 | #define MO_DMA28_PTR2 0x3000DC // {24}RW* DMA Tab Ptr : Ch#28 | ||
107 | #define MO_DMA29_PTR2 0x3000E0 // {24}RW* DMA Tab Ptr : Ch#29 | ||
108 | #define MO_DMA30_PTR2 0x3000E4 // {24}RW* DMA Tab Ptr : Ch#30 | ||
109 | #define MO_DMA31_PTR2 0x3000E8 // {24}RW* DMA Tab Ptr : Ch#31 | ||
110 | #define MO_DMA32_PTR2 0x3000EC // {24}RW* DMA Tab Ptr : Ch#32 | ||
111 | |||
112 | #define MO_DMA21_CNT1 0x300100 // {11}RW* DMA Buffer Size : Ch#21 | ||
113 | #define MO_DMA22_CNT1 0x300104 // {11}RW* DMA Buffer Size : Ch#22 | ||
114 | #define MO_DMA23_CNT1 0x300108 // {11}RW* DMA Buffer Size : Ch#23 | ||
115 | #define MO_DMA24_CNT1 0x30010C // {11}RW* DMA Buffer Size : Ch#24 | ||
116 | #define MO_DMA25_CNT1 0x300110 // {11}RW* DMA Buffer Size : Ch#25 | ||
117 | #define MO_DMA26_CNT1 0x300114 // {11}RW* DMA Buffer Size : Ch#26 | ||
118 | #define MO_DMA27_CNT1 0x300118 // {11}RW* DMA Buffer Size : Ch#27 | ||
119 | #define MO_DMA28_CNT1 0x30011C // {11}RW* DMA Buffer Size : Ch#28 | ||
120 | #define MO_DMA29_CNT1 0x300120 // {11}RW* DMA Buffer Size : Ch#29 | ||
121 | #define MO_DMA30_CNT1 0x300124 // {11}RW* DMA Buffer Size : Ch#30 | ||
122 | #define MO_DMA31_CNT1 0x300128 // {11}RW* DMA Buffer Size : Ch#31 | ||
123 | #define MO_DMA32_CNT1 0x30012C // {11}RW* DMA Buffer Size : Ch#32 | ||
124 | |||
125 | #define MO_DMA21_CNT2 0x300140 // {11}RW* DMA Table Size : Ch#21 | ||
126 | #define MO_DMA22_CNT2 0x300144 // {11}RW* DMA Table Size : Ch#22 | ||
127 | #define MO_DMA23_CNT2 0x300148 // {11}RW* DMA Table Size : Ch#23 | ||
128 | #define MO_DMA24_CNT2 0x30014C // {11}RW* DMA Table Size : Ch#24 | ||
129 | #define MO_DMA25_CNT2 0x300150 // {11}RW* DMA Table Size : Ch#25 | ||
130 | #define MO_DMA26_CNT2 0x300154 // {11}RW* DMA Table Size : Ch#26 | ||
131 | #define MO_DMA27_CNT2 0x300158 // {11}RW* DMA Table Size : Ch#27 | ||
132 | #define MO_DMA28_CNT2 0x30015C // {11}RW* DMA Table Size : Ch#28 | ||
133 | #define MO_DMA29_CNT2 0x300160 // {11}RW* DMA Table Size : Ch#29 | ||
134 | #define MO_DMA30_CNT2 0x300164 // {11}RW* DMA Table Size : Ch#30 | ||
135 | #define MO_DMA31_CNT2 0x300168 // {11}RW* DMA Table Size : Ch#31 | ||
136 | #define MO_DMA32_CNT2 0x30016C // {11}RW* DMA Table Size : Ch#32 | ||
137 | |||
138 | |||
139 | /* ---------------------------------------------------------------------- */ | ||
140 | /* Video registers */ | ||
141 | |||
142 | #define MO_VIDY_DMA 0x310000 // {64}RWp Video Y | ||
143 | #define MO_VIDU_DMA 0x310008 // {64}RWp Video U | ||
144 | #define MO_VIDV_DMA 0x310010 // {64}RWp Video V | ||
145 | #define MO_VBI_DMA 0x310018 // {64}RWp VBI (Vertical blanking interval) | ||
146 | |||
147 | #define MO_DEVICE_STATUS 0x310100 | ||
148 | #define MO_INPUT_FORMAT 0x310104 | ||
149 | #define MO_AGC_BURST 0x31010c | ||
150 | #define MO_CONTR_BRIGHT 0x310110 | ||
151 | #define MO_UV_SATURATION 0x310114 | ||
152 | #define MO_HUE 0x310118 | ||
153 | #define MO_HTOTAL 0x310120 | ||
154 | #define MO_HDELAY_EVEN 0x310124 | ||
155 | #define MO_HDELAY_ODD 0x310128 | ||
156 | #define MO_VDELAY_ODD 0x31012c | ||
157 | #define MO_VDELAY_EVEN 0x310130 | ||
158 | #define MO_HACTIVE_EVEN 0x31013c | ||
159 | #define MO_HACTIVE_ODD 0x310140 | ||
160 | #define MO_VACTIVE_EVEN 0x310144 | ||
161 | #define MO_VACTIVE_ODD 0x310148 | ||
162 | #define MO_HSCALE_EVEN 0x31014c | ||
163 | #define MO_HSCALE_ODD 0x310150 | ||
164 | #define MO_VSCALE_EVEN 0x310154 | ||
165 | #define MO_FILTER_EVEN 0x31015c | ||
166 | #define MO_VSCALE_ODD 0x310158 | ||
167 | #define MO_FILTER_ODD 0x310160 | ||
168 | #define MO_OUTPUT_FORMAT 0x310164 | ||
169 | |||
170 | #define MO_PLL_REG 0x310168 // PLL register | ||
171 | #define MO_PLL_ADJ_CTRL 0x31016c // PLL adjust control register | ||
172 | #define MO_SCONV_REG 0x310170 // sample rate conversion register | ||
173 | #define MO_SCONV_FIFO 0x310174 // sample rate conversion fifo | ||
174 | #define MO_SUB_STEP 0x310178 // subcarrier step size | ||
175 | #define MO_SUB_STEP_DR 0x31017c // subcarrier step size for DR line | ||
176 | |||
177 | #define MO_CAPTURE_CTRL 0x310180 // capture control | ||
178 | #define MO_COLOR_CTRL 0x310184 | ||
179 | #define MO_VBI_PACKET 0x310188 // vbi packet size / delay | ||
180 | #define MO_FIELD_COUNT 0x310190 // field counter | ||
181 | #define MO_VIP_CONFIG 0x310194 | ||
182 | #define MO_VBOS_CONTROL 0x3101a8 | ||
183 | |||
184 | #define MO_AGC_BACK_VBI 0x310200 | ||
185 | #define MO_AGC_SYNC_TIP1 0x310208 | ||
186 | |||
187 | #define MO_VIDY_GPCNT 0x31C020 // {16}RO Video Y general purpose counter | ||
188 | #define MO_VIDU_GPCNT 0x31C024 // {16}RO Video U general purpose counter | ||
189 | #define MO_VIDV_GPCNT 0x31C028 // {16}RO Video V general purpose counter | ||
190 | #define MO_VBI_GPCNT 0x31C02C // {16}RO VBI general purpose counter | ||
191 | #define MO_VIDY_GPCNTRL 0x31C030 // {2}WO Video Y general purpose control | ||
192 | #define MO_VIDU_GPCNTRL 0x31C034 // {2}WO Video U general purpose control | ||
193 | #define MO_VIDV_GPCNTRL 0x31C038 // {2}WO Video V general purpose control | ||
194 | #define MO_VBI_GPCNTRL 0x31C03C // {2}WO VBI general purpose counter | ||
195 | #define MO_VID_DMACNTRL 0x31C040 // {8}RW Video DMA control | ||
196 | #define MO_VID_XFR_STAT 0x31C044 // {1}RO Video transfer status | ||
197 | |||
198 | |||
199 | /* ---------------------------------------------------------------------- */ | ||
200 | /* audio registers */ | ||
201 | |||
202 | #define MO_AUDD_DMA 0x320000 // {64}RWp Audio downstream | ||
203 | #define MO_AUDU_DMA 0x320008 // {64}RWp Audio upstream | ||
204 | #define MO_AUDR_DMA 0x320010 // {64}RWp Audio RDS (downstream) | ||
205 | #define MO_AUDD_GPCNT 0x32C020 // {16}RO Audio down general purpose counter | ||
206 | #define MO_AUDU_GPCNT 0x32C024 // {16}RO Audio up general purpose counter | ||
207 | #define MO_AUDR_GPCNT 0x32C028 // {16}RO Audio RDS general purpose counter | ||
208 | #define MO_AUDD_GPCNTRL 0x32C030 // {2}WO Audio down general purpose control | ||
209 | #define MO_AUDU_GPCNTRL 0x32C034 // {2}WO Audio up general purpose control | ||
210 | #define MO_AUDR_GPCNTRL 0x32C038 // {2}WO Audio RDS general purpose control | ||
211 | #define MO_AUD_DMACNTRL 0x32C040 // {6}RW Audio DMA control | ||
212 | #define MO_AUD_XFR_STAT 0x32C044 // {1}RO Audio transfer status | ||
213 | #define MO_AUDD_LNGTH 0x32C048 // {12}RW Audio down line length | ||
214 | #define MO_AUDR_LNGTH 0x32C04C // {12}RW Audio RDS line length | ||
215 | |||
216 | #define AUD_INIT 0x320100 | ||
217 | #define AUD_INIT_LD 0x320104 | ||
218 | #define AUD_SOFT_RESET 0x320108 | ||
219 | #define AUD_I2SINPUTCNTL 0x320120 | ||
220 | #define AUD_BAUDRATE 0x320124 | ||
221 | #define AUD_I2SOUTPUTCNTL 0x320128 | ||
222 | #define AAGC_HYST 0x320134 | ||
223 | #define AAGC_GAIN 0x320138 | ||
224 | #define AAGC_DEF 0x32013c | ||
225 | #define AUD_IIR1_0_SEL 0x320150 | ||
226 | #define AUD_IIR1_0_SHIFT 0x320154 | ||
227 | #define AUD_IIR1_1_SEL 0x320158 | ||
228 | #define AUD_IIR1_1_SHIFT 0x32015c | ||
229 | #define AUD_IIR1_2_SEL 0x320160 | ||
230 | #define AUD_IIR1_2_SHIFT 0x320164 | ||
231 | #define AUD_IIR1_3_SEL 0x320168 | ||
232 | #define AUD_IIR1_3_SHIFT 0x32016c | ||
233 | #define AUD_IIR1_4_SEL 0x320170 | ||
234 | #define AUD_IIR1_4_SHIFT 0x32017c | ||
235 | #define AUD_IIR1_5_SEL 0x320180 | ||
236 | #define AUD_IIR1_5_SHIFT 0x320184 | ||
237 | #define AUD_IIR2_0_SEL 0x320190 | ||
238 | #define AUD_IIR2_0_SHIFT 0x320194 | ||
239 | #define AUD_IIR2_1_SEL 0x320198 | ||
240 | #define AUD_IIR2_1_SHIFT 0x32019c | ||
241 | #define AUD_IIR2_2_SEL 0x3201a0 | ||
242 | #define AUD_IIR2_2_SHIFT 0x3201a4 | ||
243 | #define AUD_IIR2_3_SEL 0x3201a8 | ||
244 | #define AUD_IIR2_3_SHIFT 0x3201ac | ||
245 | #define AUD_IIR3_0_SEL 0x3201c0 | ||
246 | #define AUD_IIR3_0_SHIFT 0x3201c4 | ||
247 | #define AUD_IIR3_1_SEL 0x3201c8 | ||
248 | #define AUD_IIR3_1_SHIFT 0x3201cc | ||
249 | #define AUD_IIR3_2_SEL 0x3201d0 | ||
250 | #define AUD_IIR3_2_SHIFT 0x3201d4 | ||
251 | #define AUD_IIR4_0_SEL 0x3201e0 | ||
252 | #define AUD_IIR4_0_SHIFT 0x3201e4 | ||
253 | #define AUD_IIR4_1_SEL 0x3201e8 | ||
254 | #define AUD_IIR4_1_SHIFT 0x3201ec | ||
255 | #define AUD_IIR4_2_SEL 0x3201f0 | ||
256 | #define AUD_IIR4_2_SHIFT 0x3201f4 | ||
257 | #define AUD_IIR4_0_CA0 0x320200 | ||
258 | #define AUD_IIR4_0_CA1 0x320204 | ||
259 | #define AUD_IIR4_0_CA2 0x320208 | ||
260 | #define AUD_IIR4_0_CB0 0x32020c | ||
261 | #define AUD_IIR4_0_CB1 0x320210 | ||
262 | #define AUD_IIR4_1_CA0 0x320214 | ||
263 | #define AUD_IIR4_1_CA1 0x320218 | ||
264 | #define AUD_IIR4_1_CA2 0x32021c | ||
265 | #define AUD_IIR4_1_CB0 0x320220 | ||
266 | #define AUD_IIR4_1_CB1 0x320224 | ||
267 | #define AUD_IIR4_2_CA0 0x320228 | ||
268 | #define AUD_IIR4_2_CA1 0x32022c | ||
269 | #define AUD_IIR4_2_CA2 0x320230 | ||
270 | #define AUD_IIR4_2_CB0 0x320234 | ||
271 | #define AUD_IIR4_2_CB1 0x320238 | ||
272 | #define AUD_HP_MD_IIR4_1 0x320250 | ||
273 | #define AUD_HP_PROG_IIR4_1 0x320254 | ||
274 | #define AUD_FM_MODE_ENABLE 0x320258 | ||
275 | #define AUD_POLY0_DDS_CONSTANT 0x320270 | ||
276 | #define AUD_DN0_FREQ 0x320274 | ||
277 | #define AUD_DN1_FREQ 0x320278 | ||
278 | #define AUD_DN1_FREQ_SHIFT 0x32027c | ||
279 | #define AUD_DN1_AFC 0x320280 | ||
280 | #define AUD_DN1_SRC_SEL 0x320284 | ||
281 | #define AUD_DN1_SHFT 0x320288 | ||
282 | #define AUD_DN2_FREQ 0x32028c | ||
283 | #define AUD_DN2_FREQ_SHIFT 0x320290 | ||
284 | #define AUD_DN2_AFC 0x320294 | ||
285 | #define AUD_DN2_SRC_SEL 0x320298 | ||
286 | #define AUD_DN2_SHFT 0x32029c | ||
287 | #define AUD_CRDC0_SRC_SEL 0x320300 | ||
288 | #define AUD_CRDC0_SHIFT 0x320304 | ||
289 | #define AUD_CORDIC_SHIFT_0 0x320308 | ||
290 | #define AUD_CRDC1_SRC_SEL 0x32030c | ||
291 | #define AUD_CRDC1_SHIFT 0x320310 | ||
292 | #define AUD_CORDIC_SHIFT_1 0x320314 | ||
293 | #define AUD_DCOC_0_SRC 0x320320 | ||
294 | #define AUD_DCOC0_SHIFT 0x320324 | ||
295 | #define AUD_DCOC_0_SHIFT_IN0 0x320328 | ||
296 | #define AUD_DCOC_0_SHIFT_IN1 0x32032c | ||
297 | #define AUD_DCOC_1_SRC 0x320330 | ||
298 | #define AUD_DCOC1_SHIFT 0x320334 | ||
299 | #define AUD_DCOC_1_SHIFT_IN0 0x320338 | ||
300 | #define AUD_DCOC_1_SHIFT_IN1 0x32033c | ||
301 | #define AUD_DCOC_2_SRC 0x320340 | ||
302 | #define AUD_DCOC2_SHIFT 0x320344 | ||
303 | #define AUD_DCOC_2_SHIFT_IN0 0x320348 | ||
304 | #define AUD_DCOC_2_SHIFT_IN1 0x32034c | ||
305 | #define AUD_DCOC_PASS_IN 0x320350 | ||
306 | #define AUD_PDET_SRC 0x320370 | ||
307 | #define AUD_PDET_SHIFT 0x320374 | ||
308 | #define AUD_PILOT_BQD_1_K0 0x320380 | ||
309 | #define AUD_PILOT_BQD_1_K1 0x320384 | ||
310 | #define AUD_PILOT_BQD_1_K2 0x320388 | ||
311 | #define AUD_PILOT_BQD_1_K3 0x32038c | ||
312 | #define AUD_PILOT_BQD_1_K4 0x320390 | ||
313 | #define AUD_PILOT_BQD_2_K0 0x320394 | ||
314 | #define AUD_PILOT_BQD_2_K1 0x320398 | ||
315 | #define AUD_PILOT_BQD_2_K2 0x32039c | ||
316 | #define AUD_PILOT_BQD_2_K3 0x3203a0 | ||
317 | #define AUD_PILOT_BQD_2_K4 0x3203a4 | ||
318 | #define AUD_THR_FR 0x3203c0 | ||
319 | #define AUD_X_PROG 0x3203c4 | ||
320 | #define AUD_Y_PROG 0x3203c8 | ||
321 | #define AUD_HARMONIC_MULT 0x3203cc | ||
322 | #define AUD_C1_UP_THR 0x3203d0 | ||
323 | #define AUD_C1_LO_THR 0x3203d4 | ||
324 | #define AUD_C2_UP_THR 0x3203d8 | ||
325 | #define AUD_C2_LO_THR 0x3203dc | ||
326 | #define AUD_PLL_EN 0x320400 | ||
327 | #define AUD_PLL_SRC 0x320404 | ||
328 | #define AUD_PLL_SHIFT 0x320408 | ||
329 | #define AUD_PLL_IF_SEL 0x32040c | ||
330 | #define AUD_PLL_IF_SHIFT 0x320410 | ||
331 | #define AUD_BIQUAD_PLL_K0 0x320414 | ||
332 | #define AUD_BIQUAD_PLL_K1 0x320418 | ||
333 | #define AUD_BIQUAD_PLL_K2 0x32041c | ||
334 | #define AUD_BIQUAD_PLL_K3 0x320420 | ||
335 | #define AUD_BIQUAD_PLL_K4 0x320424 | ||
336 | #define AUD_DEEMPH0_SRC_SEL 0x320440 | ||
337 | #define AUD_DEEMPH0_SHIFT 0x320444 | ||
338 | #define AUD_DEEMPH0_G0 0x320448 | ||
339 | #define AUD_DEEMPH0_A0 0x32044c | ||
340 | #define AUD_DEEMPH0_B0 0x320450 | ||
341 | #define AUD_DEEMPH0_A1 0x320454 | ||
342 | #define AUD_DEEMPH0_B1 0x320458 | ||
343 | #define AUD_DEEMPH1_SRC_SEL 0x32045c | ||
344 | #define AUD_DEEMPH1_SHIFT 0x320460 | ||
345 | #define AUD_DEEMPH1_G0 0x320464 | ||
346 | #define AUD_DEEMPH1_A0 0x320468 | ||
347 | #define AUD_DEEMPH1_B0 0x32046c | ||
348 | #define AUD_DEEMPH1_A1 0x320470 | ||
349 | #define AUD_DEEMPH1_B1 0x320474 | ||
350 | #define AUD_OUT0_SEL 0x320490 | ||
351 | #define AUD_OUT0_SHIFT 0x320494 | ||
352 | #define AUD_OUT1_SEL 0x320498 | ||
353 | #define AUD_OUT1_SHIFT 0x32049c | ||
354 | #define AUD_RDSI_SEL 0x3204a0 | ||
355 | #define AUD_RDSI_SHIFT 0x3204a4 | ||
356 | #define AUD_RDSQ_SEL 0x3204a8 | ||
357 | #define AUD_RDSQ_SHIFT 0x3204ac | ||
358 | #define AUD_DBX_IN_GAIN 0x320500 | ||
359 | #define AUD_DBX_WBE_GAIN 0x320504 | ||
360 | #define AUD_DBX_SE_GAIN 0x320508 | ||
361 | #define AUD_DBX_RMS_WBE 0x32050c | ||
362 | #define AUD_DBX_RMS_SE 0x320510 | ||
363 | #define AUD_DBX_SE_BYPASS 0x320514 | ||
364 | #define AUD_FAWDETCTL 0x320530 | ||
365 | #define AUD_FAWDETWINCTL 0x320534 | ||
366 | #define AUD_DEEMPHGAIN_R 0x320538 | ||
367 | #define AUD_DEEMPHNUMER1_R 0x32053c | ||
368 | #define AUD_DEEMPHNUMER2_R 0x320540 | ||
369 | #define AUD_DEEMPHDENOM1_R 0x320544 | ||
370 | #define AUD_DEEMPHDENOM2_R 0x320548 | ||
371 | #define AUD_ERRLOGPERIOD_R 0x32054c | ||
372 | #define AUD_ERRINTRPTTHSHLD1_R 0x320550 | ||
373 | #define AUD_ERRINTRPTTHSHLD2_R 0x320554 | ||
374 | #define AUD_ERRINTRPTTHSHLD3_R 0x320558 | ||
375 | #define AUD_NICAM_STATUS1 0x32055c | ||
376 | #define AUD_NICAM_STATUS2 0x320560 | ||
377 | #define AUD_ERRLOG1 0x320564 | ||
378 | #define AUD_ERRLOG2 0x320568 | ||
379 | #define AUD_ERRLOG3 0x32056c | ||
380 | #define AUD_DAC_BYPASS_L 0x320580 | ||
381 | #define AUD_DAC_BYPASS_R 0x320584 | ||
382 | #define AUD_DAC_BYPASS_CTL 0x320588 | ||
383 | #define AUD_CTL 0x32058c | ||
384 | #define AUD_STATUS 0x320590 | ||
385 | #define AUD_VOL_CTL 0x320594 | ||
386 | #define AUD_BAL_CTL 0x320598 | ||
387 | #define AUD_START_TIMER 0x3205b0 | ||
388 | #define AUD_MODE_CHG_TIMER 0x3205b4 | ||
389 | #define AUD_POLYPH80SCALEFAC 0x3205b8 | ||
390 | #define AUD_DMD_RA_DDS 0x3205bc | ||
391 | #define AUD_I2S_RA_DDS 0x3205c0 | ||
392 | #define AUD_RATE_THRES_DMD 0x3205d0 | ||
393 | #define AUD_RATE_THRES_I2S 0x3205d4 | ||
394 | #define AUD_RATE_ADJ1 0x3205d8 | ||
395 | #define AUD_RATE_ADJ2 0x3205dc | ||
396 | #define AUD_RATE_ADJ3 0x3205e0 | ||
397 | #define AUD_RATE_ADJ4 0x3205e4 | ||
398 | #define AUD_RATE_ADJ5 0x3205e8 | ||
399 | #define AUD_APB_IN_RATE_ADJ 0x3205ec | ||
400 | #define AUD_PHASE_FIX_CTL 0x3205f0 | ||
401 | #define AUD_PLL_PRESCALE 0x320600 | ||
402 | #define AUD_PLL_DDS 0x320604 | ||
403 | #define AUD_PLL_INT 0x320608 | ||
404 | #define AUD_PLL_FRAC 0x32060c | ||
405 | #define AUD_PLL_JTAG 0x320620 | ||
406 | #define AUD_PLL_SPMP 0x320624 | ||
407 | #define AUD_AFE_12DB_EN 0x320628 | ||
408 | |||
409 | // Audio QAM Register Addresses | ||
410 | #define AUD_PDF_DDS_CNST_BYTE2 0x320d01 | ||
411 | #define AUD_PDF_DDS_CNST_BYTE1 0x320d02 | ||
412 | #define AUD_PDF_DDS_CNST_BYTE0 0x320d03 | ||
413 | #define AUD_PHACC_FREQ_8MSB 0x320d2a | ||
414 | #define AUD_PHACC_FREQ_8LSB 0x320d2b | ||
415 | #define AUD_QAM_MODE 0x320d04 | ||
416 | |||
417 | |||
418 | /* ---------------------------------------------------------------------- */ | ||
419 | /* transport stream registers */ | ||
420 | |||
421 | #define MO_TS_DMA 0x330000 // {64}RWp Transport stream downstream | ||
422 | #define MO_TS_GPCNT 0x33C020 // {16}RO TS general purpose counter | ||
423 | #define MO_TS_GPCNTRL 0x33C030 // {2}WO TS general purpose control | ||
424 | #define MO_TS_DMACNTRL 0x33C040 // {6}RW TS DMA control | ||
425 | #define MO_TS_XFR_STAT 0x33C044 // {1}RO TS transfer status | ||
426 | #define MO_TS_LNGTH 0x33C048 // {12}RW TS line length | ||
427 | |||
428 | #define TS_HW_SOP_CNTRL 0x33C04C | ||
429 | #define TS_GEN_CNTRL 0x33C050 | ||
430 | #define TS_BD_PKT_STAT 0x33C054 | ||
431 | #define TS_SOP_STAT 0x33C058 | ||
432 | #define TS_FIFO_OVFL_STAT 0x33C05C | ||
433 | #define TS_VALERR_CNTRL 0x33C060 | ||
434 | |||
435 | |||
436 | /* ---------------------------------------------------------------------- */ | ||
437 | /* VIP registers */ | ||
438 | |||
439 | #define MO_VIPD_DMA 0x340000 // {64}RWp VIP downstream | ||
440 | #define MO_VIPU_DMA 0x340008 // {64}RWp VIP upstream | ||
441 | #define MO_VIPD_GPCNT 0x34C020 // {16}RO VIP down general purpose counter | ||
442 | #define MO_VIPU_GPCNT 0x34C024 // {16}RO VIP up general purpose counter | ||
443 | #define MO_VIPD_GPCNTRL 0x34C030 // {2}WO VIP down general purpose control | ||
444 | #define MO_VIPU_GPCNTRL 0x34C034 // {2}WO VIP up general purpose control | ||
445 | #define MO_VIP_DMACNTRL 0x34C040 // {6}RW VIP DMA control | ||
446 | #define MO_VIP_XFR_STAT 0x34C044 // {1}RO VIP transfer status | ||
447 | #define MO_VIP_CFG 0x340048 // VIP configuration | ||
448 | #define MO_VIPU_CNTRL 0x34004C // VIP upstream control #1 | ||
449 | #define MO_VIPD_CNTRL 0x340050 // VIP downstream control #2 | ||
450 | #define MO_VIPD_LNGTH 0x340054 // VIP downstream line length | ||
451 | #define MO_VIP_BRSTLN 0x340058 // VIP burst length | ||
452 | #define MO_VIP_INTCNTRL 0x34C05C // VIP Interrupt Control | ||
453 | #define MO_VIP_XFTERM 0x340060 // VIP transfer terminate | ||
454 | |||
455 | |||
456 | /* ---------------------------------------------------------------------- */ | ||
457 | /* misc registers */ | ||
458 | |||
459 | #define MO_M2M_DMA 0x350000 // {64}RWp Mem2Mem DMA Bfr | ||
460 | #define MO_GP0_IO 0x350010 // {32}RW* GPIOoutput enablesdata I/O | ||
461 | #define MO_GP1_IO 0x350014 // {32}RW* GPIOoutput enablesdata I/O | ||
462 | #define MO_GP2_IO 0x350018 // {32}RW* GPIOoutput enablesdata I/O | ||
463 | #define MO_GP3_IO 0x35001C // {32}RW* GPIO Mode/Ctrloutput enables | ||
464 | #define MO_GPIO 0x350020 // {32}RW* GPIO I2C Ctrldata I/O | ||
465 | #define MO_GPOE 0x350024 // {32}RW GPIO I2C Ctrloutput enables | ||
466 | #define MO_GP_ISM 0x350028 // {16}WO GPIO Intr Sens/Pol | ||
467 | |||
468 | #define MO_PLL_B 0x35C008 // {32}RW* PLL Control for ASB bus clks | ||
469 | #define MO_M2M_CNT 0x35C024 // {32}RW Mem2Mem DMA Cnt | ||
470 | #define MO_M2M_XSUM 0x35C028 // {32}RO M2M XOR-Checksum | ||
471 | #define MO_CRC 0x35C02C // {16}RW CRC16 init/result | ||
472 | #define MO_CRC_D 0x35C030 // {32}WO CRC16 new data in | ||
473 | #define MO_TM_CNT_LDW 0x35C034 // {32}RO Timer : Counter low dword | ||
474 | #define MO_TM_CNT_UW 0x35C038 // {16}RO Timer : Counter high word | ||
475 | #define MO_TM_LMT_LDW 0x35C03C // {32}RW Timer : Limit low dword | ||
476 | #define MO_TM_LMT_UW 0x35C040 // {32}RW Timer : Limit high word | ||
477 | #define MO_PINMUX_IO 0x35C044 // {8}RW Pin Mux Control | ||
478 | #define MO_TSTSEL_IO 0x35C048 // {2}RW Pin Mux Control | ||
479 | #define MO_AFECFG_IO 0x35C04C // AFE configuration reg | ||
480 | #define MO_DDS_IO 0x35C050 // DDS Increment reg | ||
481 | #define MO_DDSCFG_IO 0x35C054 // DDS Configuration reg | ||
482 | #define MO_SAMPLE_IO 0x35C058 // IRIn sample reg | ||
483 | #define MO_SRST_IO 0x35C05C // Output system reset reg | ||
484 | |||
485 | #define MO_INT1_MSK 0x35C060 // DMA RISC interrupt mask | ||
486 | #define MO_INT1_STAT 0x35C064 // DMA RISC interrupt status | ||
487 | #define MO_INT1_MSTAT 0x35C068 // DMA RISC interrupt masked status | ||
488 | |||
489 | |||
490 | /* ---------------------------------------------------------------------- */ | ||
491 | /* i2c bus registers */ | ||
492 | |||
493 | #define MO_I2C 0x368000 // I2C data/control | ||
494 | #define MO_I2C_DIV (0xf<<4) | ||
495 | #define MO_I2C_SYNC (1<<3) | ||
496 | #define MO_I2C_W3B (1<<2) | ||
497 | #define MO_I2C_SCL (1<<1) | ||
498 | #define MO_I2C_SDA (1<<0) | ||
499 | |||
500 | |||
501 | /* ---------------------------------------------------------------------- */ | ||
502 | /* general purpose host registers */ | ||
503 | /* FIXME: tyops? s/0x35/0x38/ ?? */ | ||
504 | |||
505 | #define MO_GPHSTD_DMA 0x350000 // {64}RWp Host downstream | ||
506 | #define MO_GPHSTU_DMA 0x350008 // {64}RWp Host upstream | ||
507 | #define MO_GPHSTU_CNTRL 0x380048 // Host upstream control #1 | ||
508 | #define MO_GPHSTD_CNTRL 0x38004C // Host downstream control #2 | ||
509 | #define MO_GPHSTD_LNGTH 0x380050 // Host downstream line length | ||
510 | #define MO_GPHST_WSC 0x380054 // Host wait state control | ||
511 | #define MO_GPHST_XFR 0x380058 // Host transfer control | ||
512 | #define MO_GPHST_WDTH 0x38005C // Host interface width | ||
513 | #define MO_GPHST_HDSHK 0x380060 // Host peripheral handshake | ||
514 | #define MO_GPHST_MUX16 0x380064 // Host muxed 16-bit transfer parameters | ||
515 | #define MO_GPHST_MODE 0x380068 // Host mode select | ||
516 | |||
517 | #define MO_GPHSTD_GPCNT 0x35C020 // Host down general purpose counter | ||
518 | #define MO_GPHSTU_GPCNT 0x35C024 // Host up general purpose counter | ||
519 | #define MO_GPHSTD_GPCNTRL 0x38C030 // Host down general purpose control | ||
520 | #define MO_GPHSTU_GPCNTRL 0x38C034 // Host up general purpose control | ||
521 | #define MO_GPHST_DMACNTRL 0x38C040 // Host DMA control | ||
522 | #define MO_GPHST_XFR_STAT 0x38C044 // Host transfer status | ||
523 | #define MO_GPHST_SOFT_RST 0x38C06C // Host software reset | ||
524 | |||
525 | |||
526 | /* ---------------------------------------------------------------------- */ | ||
527 | /* RISC instructions */ | ||
528 | |||
529 | #define RISC_SYNC 0x80000000 | ||
530 | #define RISC_SYNC_ODD 0x80000000 | ||
531 | #define RISC_SYNC_EVEN 0x80000200 | ||
532 | #define RISC_RESYNC 0x80008000 | ||
533 | #define RISC_RESYNC_ODD 0x80008000 | ||
534 | #define RISC_RESYNC_EVEN 0x80008200 | ||
535 | #define RISC_WRITE 0x10000000 | ||
536 | #define RISC_WRITEC 0x50000000 | ||
537 | #define RISC_READ 0x90000000 | ||
538 | #define RISC_READC 0xA0000000 | ||
539 | #define RISC_JUMP 0x70000000 | ||
540 | #define RISC_SKIP 0x20000000 | ||
541 | #define RISC_WRITERM 0xB0000000 | ||
542 | #define RISC_WRITECM 0xC0000000 | ||
543 | #define RISC_WRITECR 0xD0000000 | ||
544 | #define RISC_IMM 0x00000001 | ||
545 | |||
546 | #define RISC_SOL 0x08000000 | ||
547 | #define RISC_EOL 0x04000000 | ||
548 | |||
549 | #define RISC_IRQ2 0x02000000 | ||
550 | #define RISC_IRQ1 0x01000000 | ||
551 | |||
552 | #define RISC_CNT_NONE 0x00000000 | ||
553 | #define RISC_CNT_INC 0x00010000 | ||
554 | #define RISC_CNT_RSVR 0x00020000 | ||
555 | #define RISC_CNT_RESET 0x00030000 | ||
556 | #define RISC_JMP_SRP 0x01 | ||
557 | |||
558 | |||
559 | /* ---------------------------------------------------------------------- */ | ||
560 | /* various constants */ | ||
561 | |||
562 | #define SEL_BTSC 0x01 | ||
563 | #define SEL_EIAJ 0x02 | ||
564 | #define SEL_A2 0x04 | ||
565 | #define SEL_SAP 0x08 | ||
566 | #define SEL_NICAM 0x10 | ||
567 | #define SEL_FMRADIO 0x20 | ||
568 | |||
569 | // AUD_CTL | ||
570 | #define EN_BTSC_FORCE_MONO 0 | ||
571 | #define EN_BTSC_FORCE_STEREO 1 | ||
572 | #define EN_BTSC_FORCE_SAP 2 | ||
573 | #define EN_BTSC_AUTO_STEREO 3 | ||
574 | #define EN_BTSC_AUTO_SAP 4 | ||
575 | |||
576 | #define EN_A2_FORCE_MONO1 8 | ||
577 | #define EN_A2_FORCE_MONO2 9 | ||
578 | #define EN_A2_FORCE_STEREO 10 | ||
579 | #define EN_A2_AUTO_MONO2 11 | ||
580 | #define EN_A2_AUTO_STEREO 12 | ||
581 | |||
582 | #define EN_EIAJ_FORCE_MONO1 16 | ||
583 | #define EN_EIAJ_FORCE_MONO2 17 | ||
584 | #define EN_EIAJ_FORCE_STEREO 18 | ||
585 | #define EN_EIAJ_AUTO_MONO2 19 | ||
586 | #define EN_EIAJ_AUTO_STEREO 20 | ||
587 | |||
588 | #define EN_NICAM_FORCE_MONO1 32 | ||
589 | #define EN_NICAM_FORCE_MONO2 33 | ||
590 | #define EN_NICAM_FORCE_STEREO 34 | ||
591 | #define EN_NICAM_AUTO_MONO2 35 | ||
592 | #define EN_NICAM_AUTO_STEREO 36 | ||
593 | |||
594 | #define EN_FMRADIO_FORCE_MONO 24 | ||
595 | #define EN_FMRADIO_FORCE_STEREO 25 | ||
596 | #define EN_FMRADIO_AUTO_STEREO 26 | ||
597 | |||
598 | #define EN_NICAM_AUTO_FALLBACK 0x00000040 | ||
599 | #define EN_FMRADIO_EN_RDS 0x00000200 | ||
600 | #define EN_NICAM_TRY_AGAIN_BIT 0x00000400 | ||
601 | #define EN_DAC_ENABLE 0x00001000 | ||
602 | #define EN_I2SOUT_ENABLE 0x00002000 | ||
603 | #define EN_I2SIN_STR2DAC 0x00004000 | ||
604 | #define EN_I2SIN_ENABLE 0x00008000 | ||
605 | |||
606 | #if 0 | ||
607 | /* old */ | ||
608 | #define EN_DMTRX_SUMDIFF 0x00000800 | ||
609 | #define EN_DMTRX_SUMR 0x00000880 | ||
610 | #define EN_DMTRX_LR 0x00000900 | ||
611 | #define EN_DMTRX_MONO 0x00000980 | ||
612 | #else | ||
613 | /* dscaler cvs */ | ||
614 | #define EN_DMTRX_SUMDIFF (0 << 7) | ||
615 | #define EN_DMTRX_SUMR (1 << 7) | ||
616 | #define EN_DMTRX_LR (2 << 7) | ||
617 | #define EN_DMTRX_MONO (3 << 7) | ||
618 | #define EN_DMTRX_BYPASS (1 << 11) | ||
619 | #endif | ||
620 | |||
621 | // Video | ||
622 | #define VID_CAPTURE_CONTROL 0x310180 | ||
623 | |||
624 | #define CX23880_CAP_CTL_CAPTURE_VBI_ODD (1<<3) | ||
625 | #define CX23880_CAP_CTL_CAPTURE_VBI_EVEN (1<<2) | ||
626 | #define CX23880_CAP_CTL_CAPTURE_ODD (1<<1) | ||
627 | #define CX23880_CAP_CTL_CAPTURE_EVEN (1<<0) | ||
628 | |||
629 | #define VideoInputMux0 0x0 | ||
630 | #define VideoInputMux1 0x1 | ||
631 | #define VideoInputMux2 0x2 | ||
632 | #define VideoInputMux3 0x3 | ||
633 | #define VideoInputTuner 0x0 | ||
634 | #define VideoInputComposite 0x1 | ||
635 | #define VideoInputSVideo 0x2 | ||
636 | #define VideoInputOther 0x3 | ||
637 | |||
638 | #define Xtal0 0x1 | ||
639 | #define Xtal1 0x2 | ||
640 | #define XtalAuto 0x3 | ||
641 | |||
642 | #define VideoFormatAuto 0x0 | ||
643 | #define VideoFormatNTSC 0x1 | ||
644 | #define VideoFormatNTSCJapan 0x2 | ||
645 | #define VideoFormatNTSC443 0x3 | ||
646 | #define VideoFormatPAL 0x4 | ||
647 | #define VideoFormatPALB 0x4 | ||
648 | #define VideoFormatPALD 0x4 | ||
649 | #define VideoFormatPALG 0x4 | ||
650 | #define VideoFormatPALH 0x4 | ||
651 | #define VideoFormatPALI 0x4 | ||
652 | #define VideoFormatPALBDGHI 0x4 | ||
653 | #define VideoFormatPALM 0x5 | ||
654 | #define VideoFormatPALN 0x6 | ||
655 | #define VideoFormatPALNC 0x7 | ||
656 | #define VideoFormatPAL60 0x8 | ||
657 | #define VideoFormatSECAM 0x9 | ||
658 | |||
659 | #define VideoFormatAuto27MHz 0x10 | ||
660 | #define VideoFormatNTSC27MHz 0x11 | ||
661 | #define VideoFormatNTSCJapan27MHz 0x12 | ||
662 | #define VideoFormatNTSC44327MHz 0x13 | ||
663 | #define VideoFormatPAL27MHz 0x14 | ||
664 | #define VideoFormatPALB27MHz 0x14 | ||
665 | #define VideoFormatPALD27MHz 0x14 | ||
666 | #define VideoFormatPALG27MHz 0x14 | ||
667 | #define VideoFormatPALH27MHz 0x14 | ||
668 | #define VideoFormatPALI27MHz 0x14 | ||
669 | #define VideoFormatPALBDGHI27MHz 0x14 | ||
670 | #define VideoFormatPALM27MHz 0x15 | ||
671 | #define VideoFormatPALN27MHz 0x16 | ||
672 | #define VideoFormatPALNC27MHz 0x17 | ||
673 | #define VideoFormatPAL6027MHz 0x18 | ||
674 | #define VideoFormatSECAM27MHz 0x19 | ||
675 | |||
676 | #define NominalUSECAM 0x87 | ||
677 | #define NominalVSECAM 0x85 | ||
678 | #define NominalUNTSC 0xFE | ||
679 | #define NominalVNTSC 0xB4 | ||
680 | |||
681 | #define NominalContrast 0xD8 | ||
682 | |||
683 | #define HFilterAutoFormat 0x0 | ||
684 | #define HFilterCIF 0x1 | ||
685 | #define HFilterQCIF 0x2 | ||
686 | #define HFilterICON 0x3 | ||
687 | |||
688 | #define VFilter2TapInterpolate 0 | ||
689 | #define VFilter3TapInterpolate 1 | ||
690 | #define VFilter4TapInterpolate 2 | ||
691 | #define VFilter5TapInterpolate 3 | ||
692 | #define VFilter2TapNoInterpolate 4 | ||
693 | #define VFilter3TapNoInterpolate 5 | ||
694 | #define VFilter4TapNoInterpolate 6 | ||
695 | #define VFilter5TapNoInterpolate 7 | ||
696 | |||
697 | #define ColorFormatRGB32 0x0000 | ||
698 | #define ColorFormatRGB24 0x0011 | ||
699 | #define ColorFormatRGB16 0x0022 | ||
700 | #define ColorFormatRGB15 0x0033 | ||
701 | #define ColorFormatYUY2 0x0044 | ||
702 | #define ColorFormatBTYUV 0x0055 | ||
703 | #define ColorFormatY8 0x0066 | ||
704 | #define ColorFormatRGB8 0x0077 | ||
705 | #define ColorFormatPL422 0x0088 | ||
706 | #define ColorFormatPL411 0x0099 | ||
707 | #define ColorFormatYUV12 0x00AA | ||
708 | #define ColorFormatYUV9 0x00BB | ||
709 | #define ColorFormatRAW 0x00EE | ||
710 | #define ColorFormatBSWAP 0x0300 | ||
711 | #define ColorFormatWSWAP 0x0c00 | ||
712 | #define ColorFormatEvenMask 0x050f | ||
713 | #define ColorFormatOddMask 0x0af0 | ||
714 | #define ColorFormatGamma 0x1000 | ||
715 | |||
716 | #define Interlaced 0x1 | ||
717 | #define NonInterlaced 0x0 | ||
718 | |||
719 | #define FieldEven 0x1 | ||
720 | #define FieldOdd 0x0 | ||
721 | |||
722 | #define TGReadWriteMode 0x0 | ||
723 | #define TGEnableMode 0x1 | ||
724 | |||
725 | #define DV_CbAlign 0x0 | ||
726 | #define DV_Y0Align 0x1 | ||
727 | #define DV_CrAlign 0x2 | ||
728 | #define DV_Y1Align 0x3 | ||
729 | |||
730 | #define DVF_Analog 0x0 | ||
731 | #define DVF_CCIR656 0x1 | ||
732 | #define DVF_ByteStream 0x2 | ||
733 | #define DVF_ExtVSYNC 0x4 | ||
734 | #define DVF_ExtField 0x5 | ||
735 | |||
736 | #define CHANNEL_VID_Y 0x1 | ||
737 | #define CHANNEL_VID_U 0x2 | ||
738 | #define CHANNEL_VID_V 0x3 | ||
739 | #define CHANNEL_VID_VBI 0x4 | ||
740 | #define CHANNEL_AUD_DN 0x5 | ||
741 | #define CHANNEL_AUD_UP 0x6 | ||
742 | #define CHANNEL_AUD_RDS_DN 0x7 | ||
743 | #define CHANNEL_MPEG_DN 0x8 | ||
744 | #define CHANNEL_VIP_DN 0x9 | ||
745 | #define CHANNEL_VIP_UP 0xA | ||
746 | #define CHANNEL_HOST_DN 0xB | ||
747 | #define CHANNEL_HOST_UP 0xC | ||
748 | #define CHANNEL_FIRST 0x1 | ||
749 | #define CHANNEL_LAST 0xC | ||
750 | |||
751 | #define GP_COUNT_CONTROL_NONE 0x0 | ||
752 | #define GP_COUNT_CONTROL_INC 0x1 | ||
753 | #define GP_COUNT_CONTROL_RESERVED 0x2 | ||
754 | #define GP_COUNT_CONTROL_RESET 0x3 | ||
755 | |||
756 | #define PLL_PRESCALE_BY_2 2 | ||
757 | #define PLL_PRESCALE_BY_3 3 | ||
758 | #define PLL_PRESCALE_BY_4 4 | ||
759 | #define PLL_PRESCALE_BY_5 5 | ||
760 | |||
761 | #define HLNotchFilter4xFsc 0 | ||
762 | #define HLNotchFilterSquare 1 | ||
763 | #define HLNotchFilter135NTSC 2 | ||
764 | #define HLNotchFilter135PAL 3 | ||
765 | |||
766 | #define NTSC_8x_SUB_CARRIER 28.63636E6 | ||
767 | #define PAL_8x_SUB_CARRIER 35.46895E6 | ||
768 | |||
769 | // Default analog settings | ||
770 | #define DEFAULT_HUE_NTSC 0x00 | ||
771 | #define DEFAULT_BRIGHTNESS_NTSC 0x00 | ||
772 | #define DEFAULT_CONTRAST_NTSC 0x39 | ||
773 | #define DEFAULT_SAT_U_NTSC 0x7F | ||
774 | #define DEFAULT_SAT_V_NTSC 0x5A | ||
775 | |||
776 | typedef enum | ||
777 | { | ||
778 | SOURCE_TUNER = 0, | ||
779 | SOURCE_COMPOSITE, | ||
780 | SOURCE_SVIDEO, | ||
781 | SOURCE_OTHER1, | ||
782 | SOURCE_OTHER2, | ||
783 | SOURCE_COMPVIASVIDEO, | ||
784 | SOURCE_CCIR656 | ||
785 | } VIDEOSOURCETYPE; | ||
786 | |||
787 | #endif /* _CX88_REG_H_ */ | ||
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c new file mode 100644 index 000000000000..f2a9475a2fee --- /dev/null +++ b/drivers/media/video/cx88/cx88-tvaudio.c | |||
@@ -0,0 +1,1032 @@ | |||
1 | /* | ||
2 | $Id: cx88-tvaudio.c,v 1.34 2005/03/07 16:10:51 kraxel Exp $ | ||
3 | |||
4 | cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver | ||
5 | |||
6 | (c) 2001 Michael Eskin, Tom Zakrajsek [Windows version] | ||
7 | (c) 2002 Yurij Sysoev <yurij@naturesoft.net> | ||
8 | (c) 2003 Gerd Knorr <kraxel@bytesex.org> | ||
9 | |||
10 | ----------------------------------------------------------------------- | ||
11 | |||
12 | Lot of voodoo here. Even the data sheet doesn't help to | ||
13 | understand what is going on here, the documentation for the audio | ||
14 | part of the cx2388x chip is *very* bad. | ||
15 | |||
16 | Some of this comes from party done linux driver sources I got from | ||
17 | [undocumented]. | ||
18 | |||
19 | Some comes from the dscaler sources, one of the dscaler driver guy works | ||
20 | for Conexant ... | ||
21 | |||
22 | ----------------------------------------------------------------------- | ||
23 | |||
24 | This program is free software; you can redistribute it and/or modify | ||
25 | it under the terms of the GNU General Public License as published by | ||
26 | the Free Software Foundation; either version 2 of the License, or | ||
27 | (at your option) any later version. | ||
28 | |||
29 | This program is distributed in the hope that it will be useful, | ||
30 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
31 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
32 | GNU General Public License for more details. | ||
33 | |||
34 | You should have received a copy of the GNU General Public License | ||
35 | along with this program; if not, write to the Free Software | ||
36 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
37 | */ | ||
38 | |||
39 | #include <linux/module.h> | ||
40 | #include <linux/moduleparam.h> | ||
41 | #include <linux/errno.h> | ||
42 | #include <linux/kernel.h> | ||
43 | #include <linux/slab.h> | ||
44 | #include <linux/mm.h> | ||
45 | #include <linux/poll.h> | ||
46 | #include <linux/pci.h> | ||
47 | #include <linux/signal.h> | ||
48 | #include <linux/ioport.h> | ||
49 | #include <linux/sched.h> | ||
50 | #include <linux/types.h> | ||
51 | #include <linux/interrupt.h> | ||
52 | #include <linux/vmalloc.h> | ||
53 | #include <linux/init.h> | ||
54 | #include <linux/smp_lock.h> | ||
55 | #include <linux/delay.h> | ||
56 | #include <linux/kthread.h> | ||
57 | |||
58 | #include "cx88.h" | ||
59 | |||
60 | static unsigned int audio_debug = 0; | ||
61 | module_param(audio_debug,int,0644); | ||
62 | MODULE_PARM_DESC(audio_debug,"enable debug messages [audio]"); | ||
63 | |||
64 | #define dprintk(fmt, arg...) if (audio_debug) \ | ||
65 | printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg) | ||
66 | |||
67 | /* ----------------------------------------------------------- */ | ||
68 | |||
69 | static char *aud_ctl_names[64] = | ||
70 | { | ||
71 | [ EN_BTSC_FORCE_MONO ] = "BTSC_FORCE_MONO", | ||
72 | [ EN_BTSC_FORCE_STEREO ] = "BTSC_FORCE_STEREO", | ||
73 | [ EN_BTSC_FORCE_SAP ] = "BTSC_FORCE_SAP", | ||
74 | [ EN_BTSC_AUTO_STEREO ] = "BTSC_AUTO_STEREO", | ||
75 | [ EN_BTSC_AUTO_SAP ] = "BTSC_AUTO_SAP", | ||
76 | [ EN_A2_FORCE_MONO1 ] = "A2_FORCE_MONO1", | ||
77 | [ EN_A2_FORCE_MONO2 ] = "A2_FORCE_MONO2", | ||
78 | [ EN_A2_FORCE_STEREO ] = "A2_FORCE_STEREO", | ||
79 | [ EN_A2_AUTO_MONO2 ] = "A2_AUTO_MONO2", | ||
80 | [ EN_A2_AUTO_STEREO ] = "A2_AUTO_STEREO", | ||
81 | [ EN_EIAJ_FORCE_MONO1 ] = "EIAJ_FORCE_MONO1", | ||
82 | [ EN_EIAJ_FORCE_MONO2 ] = "EIAJ_FORCE_MONO2", | ||
83 | [ EN_EIAJ_FORCE_STEREO ] = "EIAJ_FORCE_STEREO", | ||
84 | [ EN_EIAJ_AUTO_MONO2 ] = "EIAJ_AUTO_MONO2", | ||
85 | [ EN_EIAJ_AUTO_STEREO ] = "EIAJ_AUTO_STEREO", | ||
86 | [ EN_NICAM_FORCE_MONO1 ] = "NICAM_FORCE_MONO1", | ||
87 | [ EN_NICAM_FORCE_MONO2 ] = "NICAM_FORCE_MONO2", | ||
88 | [ EN_NICAM_FORCE_STEREO ] = "NICAM_FORCE_STEREO", | ||
89 | [ EN_NICAM_AUTO_MONO2 ] = "NICAM_AUTO_MONO2", | ||
90 | [ EN_NICAM_AUTO_STEREO ] = "NICAM_AUTO_STEREO", | ||
91 | [ EN_FMRADIO_FORCE_MONO ] = "FMRADIO_FORCE_MONO", | ||
92 | [ EN_FMRADIO_FORCE_STEREO ] = "FMRADIO_FORCE_STEREO", | ||
93 | [ EN_FMRADIO_AUTO_STEREO ] = "FMRADIO_AUTO_STEREO", | ||
94 | }; | ||
95 | |||
96 | struct rlist { | ||
97 | u32 reg; | ||
98 | u32 val; | ||
99 | }; | ||
100 | |||
101 | static void set_audio_registers(struct cx88_core *core, | ||
102 | const struct rlist *l) | ||
103 | { | ||
104 | int i; | ||
105 | |||
106 | for (i = 0; l[i].reg; i++) { | ||
107 | switch (l[i].reg) { | ||
108 | case AUD_PDF_DDS_CNST_BYTE2: | ||
109 | case AUD_PDF_DDS_CNST_BYTE1: | ||
110 | case AUD_PDF_DDS_CNST_BYTE0: | ||
111 | case AUD_QAM_MODE: | ||
112 | case AUD_PHACC_FREQ_8MSB: | ||
113 | case AUD_PHACC_FREQ_8LSB: | ||
114 | cx_writeb(l[i].reg, l[i].val); | ||
115 | break; | ||
116 | default: | ||
117 | cx_write(l[i].reg, l[i].val); | ||
118 | break; | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | |||
123 | static void set_audio_start(struct cx88_core *core, | ||
124 | u32 mode, u32 ctl) | ||
125 | { | ||
126 | // mute | ||
127 | cx_write(AUD_VOL_CTL, (1 << 6)); | ||
128 | |||
129 | // increase level of input by 12dB | ||
130 | cx_write(AUD_AFE_12DB_EN, 0x0001); | ||
131 | |||
132 | // start programming | ||
133 | cx_write(AUD_CTL, 0x0000); | ||
134 | cx_write(AUD_INIT, mode); | ||
135 | cx_write(AUD_INIT_LD, 0x0001); | ||
136 | cx_write(AUD_SOFT_RESET, 0x0001); | ||
137 | |||
138 | cx_write(AUD_CTL, ctl); | ||
139 | } | ||
140 | |||
141 | static void set_audio_finish(struct cx88_core *core) | ||
142 | { | ||
143 | u32 volume; | ||
144 | |||
145 | if (cx88_boards[core->board].blackbird) { | ||
146 | // 'pass-thru mode': this enables the i2s output to the mpeg encoder | ||
147 | cx_set(AUD_CTL, 0x2000); | ||
148 | cx_write(AUD_I2SOUTPUTCNTL, 1); | ||
149 | //cx_write(AUD_APB_IN_RATE_ADJ, 0); | ||
150 | } | ||
151 | |||
152 | // finish programming | ||
153 | cx_write(AUD_SOFT_RESET, 0x0000); | ||
154 | |||
155 | // start audio processing | ||
156 | cx_set(AUD_CTL, EN_DAC_ENABLE); | ||
157 | |||
158 | // unmute | ||
159 | volume = cx_sread(SHADOW_AUD_VOL_CTL); | ||
160 | cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume); | ||
161 | } | ||
162 | |||
163 | /* ----------------------------------------------------------- */ | ||
164 | |||
165 | static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap) | ||
166 | { | ||
167 | static const struct rlist btsc[] = { | ||
168 | /* from dscaler */ | ||
169 | { AUD_OUT1_SEL, 0x00000013 }, | ||
170 | { AUD_OUT1_SHIFT, 0x00000000 }, | ||
171 | { AUD_POLY0_DDS_CONSTANT, 0x0012010c }, | ||
172 | { AUD_DMD_RA_DDS, 0x00c3e7aa }, | ||
173 | { AUD_DBX_IN_GAIN, 0x00004734 }, | ||
174 | { AUD_DBX_WBE_GAIN, 0x00004640 }, | ||
175 | { AUD_DBX_SE_GAIN, 0x00008d31 }, | ||
176 | { AUD_DCOC_0_SRC, 0x0000001a }, | ||
177 | { AUD_IIR1_4_SEL, 0x00000021 }, | ||
178 | { AUD_DCOC_PASS_IN, 0x00000003 }, | ||
179 | { AUD_DCOC_0_SHIFT_IN0, 0x0000000a }, | ||
180 | { AUD_DCOC_0_SHIFT_IN1, 0x00000008 }, | ||
181 | { AUD_DCOC_1_SHIFT_IN0, 0x0000000a }, | ||
182 | { AUD_DCOC_1_SHIFT_IN1, 0x00000008 }, | ||
183 | { AUD_DN0_FREQ, 0x0000283b }, | ||
184 | { AUD_DN2_SRC_SEL, 0x00000008 }, | ||
185 | { AUD_DN2_FREQ, 0x00003000 }, | ||
186 | { AUD_DN2_AFC, 0x00000002 }, | ||
187 | { AUD_DN2_SHFT, 0x00000000 }, | ||
188 | { AUD_IIR2_2_SEL, 0x00000020 }, | ||
189 | { AUD_IIR2_2_SHIFT, 0x00000000 }, | ||
190 | { AUD_IIR2_3_SEL, 0x0000001f }, | ||
191 | { AUD_IIR2_3_SHIFT, 0x00000000 }, | ||
192 | { AUD_CRDC1_SRC_SEL, 0x000003ce }, | ||
193 | { AUD_CRDC1_SHIFT, 0x00000000 }, | ||
194 | { AUD_CORDIC_SHIFT_1, 0x00000007 }, | ||
195 | { AUD_DCOC_1_SRC, 0x0000001b }, | ||
196 | { AUD_DCOC1_SHIFT, 0x00000000 }, | ||
197 | { AUD_RDSI_SEL, 0x00000008 }, | ||
198 | { AUD_RDSQ_SEL, 0x00000008 }, | ||
199 | { AUD_RDSI_SHIFT, 0x00000000 }, | ||
200 | { AUD_RDSQ_SHIFT, 0x00000000 }, | ||
201 | { AUD_POLYPH80SCALEFAC, 0x00000003 }, | ||
202 | { /* end of list */ }, | ||
203 | }; | ||
204 | static const struct rlist btsc_sap[] = { | ||
205 | { AUD_DBX_IN_GAIN, 0x00007200 }, | ||
206 | { AUD_DBX_WBE_GAIN, 0x00006200 }, | ||
207 | { AUD_DBX_SE_GAIN, 0x00006200 }, | ||
208 | { AUD_IIR1_1_SEL, 0x00000000 }, | ||
209 | { AUD_IIR1_3_SEL, 0x00000001 }, | ||
210 | { AUD_DN1_SRC_SEL, 0x00000007 }, | ||
211 | { AUD_IIR1_4_SHIFT, 0x00000006 }, | ||
212 | { AUD_IIR2_1_SHIFT, 0x00000000 }, | ||
213 | { AUD_IIR2_2_SHIFT, 0x00000000 }, | ||
214 | { AUD_IIR3_0_SHIFT, 0x00000000 }, | ||
215 | { AUD_IIR3_1_SHIFT, 0x00000000 }, | ||
216 | { AUD_IIR3_0_SEL, 0x0000000d }, | ||
217 | { AUD_IIR3_1_SEL, 0x0000000e }, | ||
218 | { AUD_DEEMPH1_SRC_SEL, 0x00000014 }, | ||
219 | { AUD_DEEMPH1_SHIFT, 0x00000000 }, | ||
220 | { AUD_DEEMPH1_G0, 0x00004000 }, | ||
221 | { AUD_DEEMPH1_A0, 0x00000000 }, | ||
222 | { AUD_DEEMPH1_B0, 0x00000000 }, | ||
223 | { AUD_DEEMPH1_A1, 0x00000000 }, | ||
224 | { AUD_DEEMPH1_B1, 0x00000000 }, | ||
225 | { AUD_OUT0_SEL, 0x0000003f }, | ||
226 | { AUD_OUT1_SEL, 0x0000003f }, | ||
227 | { AUD_DN1_AFC, 0x00000002 }, | ||
228 | { AUD_DCOC_0_SHIFT_IN0, 0x0000000a }, | ||
229 | { AUD_DCOC_0_SHIFT_IN1, 0x00000008 }, | ||
230 | { AUD_DCOC_1_SHIFT_IN0, 0x0000000a }, | ||
231 | { AUD_DCOC_1_SHIFT_IN1, 0x00000008 }, | ||
232 | { AUD_IIR1_0_SEL, 0x0000001d }, | ||
233 | { AUD_IIR1_2_SEL, 0x0000001e }, | ||
234 | { AUD_IIR2_1_SEL, 0x00000002 }, | ||
235 | { AUD_IIR2_2_SEL, 0x00000004 }, | ||
236 | { AUD_IIR3_2_SEL, 0x0000000f }, | ||
237 | { AUD_DCOC2_SHIFT, 0x00000001 }, | ||
238 | { AUD_IIR3_2_SHIFT, 0x00000001 }, | ||
239 | { AUD_DEEMPH0_SRC_SEL, 0x00000014 }, | ||
240 | { AUD_CORDIC_SHIFT_1, 0x00000006 }, | ||
241 | { AUD_POLY0_DDS_CONSTANT, 0x000e4db2 }, | ||
242 | { AUD_DMD_RA_DDS, 0x00f696e6 }, | ||
243 | { AUD_IIR2_3_SEL, 0x00000025 }, | ||
244 | { AUD_IIR1_4_SEL, 0x00000021 }, | ||
245 | { AUD_DN1_FREQ, 0x0000c965 }, | ||
246 | { AUD_DCOC_PASS_IN, 0x00000003 }, | ||
247 | { AUD_DCOC_0_SRC, 0x0000001a }, | ||
248 | { AUD_DCOC_1_SRC, 0x0000001b }, | ||
249 | { AUD_DCOC1_SHIFT, 0x00000000 }, | ||
250 | { AUD_RDSI_SEL, 0x00000009 }, | ||
251 | { AUD_RDSQ_SEL, 0x00000009 }, | ||
252 | { AUD_RDSI_SHIFT, 0x00000000 }, | ||
253 | { AUD_RDSQ_SHIFT, 0x00000000 }, | ||
254 | { AUD_POLYPH80SCALEFAC, 0x00000003 }, | ||
255 | { /* end of list */ }, | ||
256 | }; | ||
257 | |||
258 | // dscaler: exactly taken from driver, | ||
259 | // dscaler: don't know why to set EN_FMRADIO_EN_RDS | ||
260 | if (sap) { | ||
261 | dprintk("%s SAP (status: unknown)\n",__FUNCTION__); | ||
262 | set_audio_start(core, 0x0001, | ||
263 | EN_FMRADIO_EN_RDS | EN_BTSC_FORCE_SAP); | ||
264 | set_audio_registers(core, btsc_sap); | ||
265 | } else { | ||
266 | dprintk("%s (status: known-good)\n",__FUNCTION__); | ||
267 | set_audio_start(core, 0x0001, | ||
268 | EN_FMRADIO_EN_RDS | EN_BTSC_AUTO_STEREO); | ||
269 | set_audio_registers(core, btsc); | ||
270 | } | ||
271 | set_audio_finish(core); | ||
272 | } | ||
273 | |||
274 | #if 0 | ||
275 | static void set_audio_standard_NICAM(struct cx88_core *core) | ||
276 | { | ||
277 | static const struct rlist nicam_common[] = { | ||
278 | /* from dscaler */ | ||
279 | { AUD_RATE_ADJ1, 0x00000010 }, | ||
280 | { AUD_RATE_ADJ2, 0x00000040 }, | ||
281 | { AUD_RATE_ADJ3, 0x00000100 }, | ||
282 | { AUD_RATE_ADJ4, 0x00000400 }, | ||
283 | { AUD_RATE_ADJ5, 0x00001000 }, | ||
284 | // { AUD_DMD_RA_DDS, 0x00c0d5ce }, | ||
285 | |||
286 | // Deemphasis 1: | ||
287 | { AUD_DEEMPHGAIN_R, 0x000023c2 }, | ||
288 | { AUD_DEEMPHNUMER1_R, 0x0002a7bc }, | ||
289 | { AUD_DEEMPHNUMER2_R, 0x0003023e }, | ||
290 | { AUD_DEEMPHDENOM1_R, 0x0000f3d0 }, | ||
291 | { AUD_DEEMPHDENOM2_R, 0x00000000 }, | ||
292 | |||
293 | #if 0 | ||
294 | // Deemphasis 2: (other tv norm?) | ||
295 | { AUD_DEEMPHGAIN_R, 0x0000c600 }, | ||
296 | { AUD_DEEMPHNUMER1_R, 0x00066738 }, | ||
297 | { AUD_DEEMPHNUMER2_R, 0x00066739 }, | ||
298 | { AUD_DEEMPHDENOM1_R, 0x0001e88c }, | ||
299 | { AUD_DEEMPHDENOM2_R, 0x0001e88c }, | ||
300 | #endif | ||
301 | |||
302 | { AUD_DEEMPHDENOM2_R, 0x00000000 }, | ||
303 | { AUD_ERRLOGPERIOD_R, 0x00000fff }, | ||
304 | { AUD_ERRINTRPTTHSHLD1_R, 0x000003ff }, | ||
305 | { AUD_ERRINTRPTTHSHLD2_R, 0x000000ff }, | ||
306 | { AUD_ERRINTRPTTHSHLD3_R, 0x0000003f }, | ||
307 | { AUD_POLYPH80SCALEFAC, 0x00000003 }, | ||
308 | |||
309 | // setup QAM registers | ||
310 | { AUD_PDF_DDS_CNST_BYTE2, 0x06 }, | ||
311 | { AUD_PDF_DDS_CNST_BYTE1, 0x82 }, | ||
312 | { AUD_PDF_DDS_CNST_BYTE0, 0x16 }, | ||
313 | { AUD_QAM_MODE, 0x05 }, | ||
314 | |||
315 | { /* end of list */ }, | ||
316 | }; | ||
317 | static const struct rlist nicam_pal_i[] = { | ||
318 | { AUD_PDF_DDS_CNST_BYTE0, 0x12 }, | ||
319 | { AUD_PHACC_FREQ_8MSB, 0x3a }, | ||
320 | { AUD_PHACC_FREQ_8LSB, 0x93 }, | ||
321 | |||
322 | { /* end of list */ }, | ||
323 | }; | ||
324 | static const struct rlist nicam_default[] = { | ||
325 | { AUD_PDF_DDS_CNST_BYTE0, 0x16 }, | ||
326 | { AUD_PHACC_FREQ_8MSB, 0x34 }, | ||
327 | { AUD_PHACC_FREQ_8LSB, 0x4c }, | ||
328 | |||
329 | { /* end of list */ }, | ||
330 | }; | ||
331 | |||
332 | set_audio_start(core, 0x0010, | ||
333 | EN_DMTRX_LR | EN_DMTRX_BYPASS | EN_NICAM_AUTO_STEREO); | ||
334 | set_audio_registers(core, nicam_common); | ||
335 | switch (core->tvaudio) { | ||
336 | case WW_NICAM_I: | ||
337 | dprintk("%s PAL-I NICAM (status: unknown)\n",__FUNCTION__); | ||
338 | set_audio_registers(core, nicam_pal_i); | ||
339 | break; | ||
340 | case WW_NICAM_BGDKL: | ||
341 | dprintk("%s PAL-BGDK NICAM (status: unknown)\n",__FUNCTION__); | ||
342 | set_audio_registers(core, nicam_default); | ||
343 | break; | ||
344 | }; | ||
345 | set_audio_finish(core); | ||
346 | } | ||
347 | #endif | ||
348 | |||
349 | static void set_audio_standard_NICAM_L(struct cx88_core *core, int stereo) | ||
350 | { | ||
351 | /* This is probably weird.. | ||
352 | * Let's operate and find out. */ | ||
353 | |||
354 | static const struct rlist nicam_l_mono[] = { | ||
355 | { AUD_ERRLOGPERIOD_R, 0x00000064 }, | ||
356 | { AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF }, | ||
357 | { AUD_ERRINTRPTTHSHLD2_R, 0x0000001F }, | ||
358 | { AUD_ERRINTRPTTHSHLD3_R, 0x0000000F }, | ||
359 | |||
360 | { AUD_PDF_DDS_CNST_BYTE2, 0x48 }, | ||
361 | { AUD_PDF_DDS_CNST_BYTE1, 0x3D }, | ||
362 | { AUD_QAM_MODE, 0x00 }, | ||
363 | { AUD_PDF_DDS_CNST_BYTE0, 0xf5 }, | ||
364 | { AUD_PHACC_FREQ_8MSB, 0x3a }, | ||
365 | { AUD_PHACC_FREQ_8LSB, 0x4a }, | ||
366 | |||
367 | { AUD_DEEMPHGAIN_R, 0x6680 }, | ||
368 | { AUD_DEEMPHNUMER1_R, 0x353DE }, | ||
369 | { AUD_DEEMPHNUMER2_R, 0x1B1 }, | ||
370 | { AUD_DEEMPHDENOM1_R, 0x0F3D0 }, | ||
371 | { AUD_DEEMPHDENOM2_R, 0x0 }, | ||
372 | { AUD_FM_MODE_ENABLE, 0x7 }, | ||
373 | { AUD_POLYPH80SCALEFAC, 0x3 }, | ||
374 | { AUD_AFE_12DB_EN, 0x1 }, | ||
375 | { AAGC_GAIN, 0x0 }, | ||
376 | { AAGC_HYST, 0x18 }, | ||
377 | { AAGC_DEF, 0x20 }, | ||
378 | { AUD_DN0_FREQ, 0x0 }, | ||
379 | { AUD_POLY0_DDS_CONSTANT, 0x0E4DB2 }, | ||
380 | { AUD_DCOC_0_SRC, 0x21 }, | ||
381 | { AUD_IIR1_0_SEL, 0x0 }, | ||
382 | { AUD_IIR1_0_SHIFT, 0x7 }, | ||
383 | { AUD_IIR1_1_SEL, 0x2 }, | ||
384 | { AUD_IIR1_1_SHIFT, 0x0 }, | ||
385 | { AUD_DCOC_1_SRC, 0x3 }, | ||
386 | { AUD_DCOC1_SHIFT, 0x0 }, | ||
387 | { AUD_DCOC_PASS_IN, 0x0 }, | ||
388 | { AUD_IIR1_2_SEL, 0x23 }, | ||
389 | { AUD_IIR1_2_SHIFT, 0x0 }, | ||
390 | { AUD_IIR1_3_SEL, 0x4 }, | ||
391 | { AUD_IIR1_3_SHIFT, 0x7 }, | ||
392 | { AUD_IIR1_4_SEL, 0x5 }, | ||
393 | { AUD_IIR1_4_SHIFT, 0x7 }, | ||
394 | { AUD_IIR3_0_SEL, 0x7 }, | ||
395 | { AUD_IIR3_0_SHIFT, 0x0 }, | ||
396 | { AUD_DEEMPH0_SRC_SEL, 0x11 }, | ||
397 | { AUD_DEEMPH0_SHIFT, 0x0 }, | ||
398 | { AUD_DEEMPH0_G0, 0x7000 }, | ||
399 | { AUD_DEEMPH0_A0, 0x0 }, | ||
400 | { AUD_DEEMPH0_B0, 0x0 }, | ||
401 | { AUD_DEEMPH0_A1, 0x0 }, | ||
402 | { AUD_DEEMPH0_B1, 0x0 }, | ||
403 | { AUD_DEEMPH1_SRC_SEL, 0x11 }, | ||
404 | { AUD_DEEMPH1_SHIFT, 0x0 }, | ||
405 | { AUD_DEEMPH1_G0, 0x7000 }, | ||
406 | { AUD_DEEMPH1_A0, 0x0 }, | ||
407 | { AUD_DEEMPH1_B0, 0x0 }, | ||
408 | { AUD_DEEMPH1_A1, 0x0 }, | ||
409 | { AUD_DEEMPH1_B1, 0x0 }, | ||
410 | { AUD_OUT0_SEL, 0x3F }, | ||
411 | { AUD_OUT1_SEL, 0x3F }, | ||
412 | { AUD_DMD_RA_DDS, 0x0F5C285 }, | ||
413 | { AUD_PLL_INT, 0x1E }, | ||
414 | { AUD_PLL_DDS, 0x0 }, | ||
415 | { AUD_PLL_FRAC, 0x0E542 }, | ||
416 | |||
417 | // setup QAM registers | ||
418 | { AUD_RATE_ADJ1, 0x00000100 }, | ||
419 | { AUD_RATE_ADJ2, 0x00000200 }, | ||
420 | { AUD_RATE_ADJ3, 0x00000300 }, | ||
421 | { AUD_RATE_ADJ4, 0x00000400 }, | ||
422 | { AUD_RATE_ADJ5, 0x00000500 }, | ||
423 | { AUD_RATE_THRES_DMD, 0x000000C0 }, | ||
424 | { /* end of list */ }, | ||
425 | }; | ||
426 | |||
427 | static const struct rlist nicam_l[] = { | ||
428 | // setup QAM registers | ||
429 | { AUD_RATE_ADJ1, 0x00000060 }, | ||
430 | { AUD_RATE_ADJ2, 0x000000F9 }, | ||
431 | { AUD_RATE_ADJ3, 0x000001CC }, | ||
432 | { AUD_RATE_ADJ4, 0x000002B3 }, | ||
433 | { AUD_RATE_ADJ5, 0x00000726 }, | ||
434 | { AUD_DEEMPHDENOM1_R, 0x0000F3D0 }, | ||
435 | { AUD_DEEMPHDENOM2_R, 0x00000000 }, | ||
436 | { AUD_ERRLOGPERIOD_R, 0x00000064 }, | ||
437 | { AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF }, | ||
438 | { AUD_ERRINTRPTTHSHLD2_R, 0x0000001F }, | ||
439 | { AUD_ERRINTRPTTHSHLD3_R, 0x0000000F }, | ||
440 | { AUD_POLYPH80SCALEFAC, 0x00000003 }, | ||
441 | { AUD_DMD_RA_DDS, 0x00C00000 }, | ||
442 | { AUD_PLL_INT, 0x0000001E }, | ||
443 | { AUD_PLL_DDS, 0x00000000 }, | ||
444 | { AUD_PLL_FRAC, 0x0000E542 }, | ||
445 | { AUD_START_TIMER, 0x00000000 }, | ||
446 | { AUD_DEEMPHNUMER1_R, 0x000353DE }, | ||
447 | { AUD_DEEMPHNUMER2_R, 0x000001B1 }, | ||
448 | { AUD_PDF_DDS_CNST_BYTE2, 0x06 }, | ||
449 | { AUD_PDF_DDS_CNST_BYTE1, 0x82 }, | ||
450 | { AUD_QAM_MODE, 0x05 }, | ||
451 | { AUD_PDF_DDS_CNST_BYTE0, 0x12 }, | ||
452 | { AUD_PHACC_FREQ_8MSB, 0x34 }, | ||
453 | { AUD_PHACC_FREQ_8LSB, 0x4C }, | ||
454 | { AUD_DEEMPHGAIN_R, 0x00006680 }, | ||
455 | { AUD_RATE_THRES_DMD, 0x000000C0 }, | ||
456 | { /* end of list */ }, | ||
457 | } ; | ||
458 | dprintk("%s (status: devel), stereo : %d\n",__FUNCTION__,stereo); | ||
459 | |||
460 | if (!stereo) { | ||
461 | /* AM mono sound */ | ||
462 | set_audio_start(core, 0x0004, | ||
463 | 0x100c /* FIXME again */); | ||
464 | set_audio_registers(core, nicam_l_mono); | ||
465 | } else { | ||
466 | set_audio_start(core, 0x0010, | ||
467 | 0x1924 /* FIXME again */); | ||
468 | set_audio_registers(core, nicam_l); | ||
469 | } | ||
470 | set_audio_finish(core); | ||
471 | |||
472 | } | ||
473 | |||
474 | static void set_audio_standard_PAL_I(struct cx88_core *core, int stereo) | ||
475 | { | ||
476 | static const struct rlist pal_i_fm_mono[] = { | ||
477 | {AUD_ERRLOGPERIOD_R, 0x00000064}, | ||
478 | {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff}, | ||
479 | {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f}, | ||
480 | {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f}, | ||
481 | {AUD_PDF_DDS_CNST_BYTE2, 0x06}, | ||
482 | {AUD_PDF_DDS_CNST_BYTE1, 0x82}, | ||
483 | {AUD_PDF_DDS_CNST_BYTE0, 0x12}, | ||
484 | {AUD_QAM_MODE, 0x05}, | ||
485 | {AUD_PHACC_FREQ_8MSB, 0x3a}, | ||
486 | {AUD_PHACC_FREQ_8LSB, 0x93}, | ||
487 | {AUD_DMD_RA_DDS, 0x002a4f2f}, | ||
488 | {AUD_PLL_INT, 0x0000001e}, | ||
489 | {AUD_PLL_DDS, 0x00000004}, | ||
490 | {AUD_PLL_FRAC, 0x0000e542}, | ||
491 | {AUD_RATE_ADJ1, 0x00000100}, | ||
492 | {AUD_RATE_ADJ2, 0x00000200}, | ||
493 | {AUD_RATE_ADJ3, 0x00000300}, | ||
494 | {AUD_RATE_ADJ4, 0x00000400}, | ||
495 | {AUD_RATE_ADJ5, 0x00000500}, | ||
496 | {AUD_THR_FR, 0x00000000}, | ||
497 | {AUD_PILOT_BQD_1_K0, 0x0000755b}, | ||
498 | {AUD_PILOT_BQD_1_K1, 0x00551340}, | ||
499 | {AUD_PILOT_BQD_1_K2, 0x006d30be}, | ||
500 | {AUD_PILOT_BQD_1_K3, 0xffd394af}, | ||
501 | {AUD_PILOT_BQD_1_K4, 0x00400000}, | ||
502 | {AUD_PILOT_BQD_2_K0, 0x00040000}, | ||
503 | {AUD_PILOT_BQD_2_K1, 0x002a4841}, | ||
504 | {AUD_PILOT_BQD_2_K2, 0x00400000}, | ||
505 | {AUD_PILOT_BQD_2_K3, 0x00000000}, | ||
506 | {AUD_PILOT_BQD_2_K4, 0x00000000}, | ||
507 | {AUD_MODE_CHG_TIMER, 0x00000060}, | ||
508 | {AUD_AFE_12DB_EN, 0x00000001}, | ||
509 | {AAGC_HYST, 0x0000000a}, | ||
510 | {AUD_CORDIC_SHIFT_0, 0x00000007}, | ||
511 | {AUD_CORDIC_SHIFT_1, 0x00000007}, | ||
512 | {AUD_C1_UP_THR, 0x00007000}, | ||
513 | {AUD_C1_LO_THR, 0x00005400}, | ||
514 | {AUD_C2_UP_THR, 0x00005400}, | ||
515 | {AUD_C2_LO_THR, 0x00003000}, | ||
516 | {AUD_DCOC_0_SRC, 0x0000001a}, | ||
517 | {AUD_DCOC0_SHIFT, 0x00000000}, | ||
518 | {AUD_DCOC_0_SHIFT_IN0, 0x0000000a}, | ||
519 | {AUD_DCOC_0_SHIFT_IN1, 0x00000008}, | ||
520 | {AUD_DCOC_PASS_IN, 0x00000003}, | ||
521 | {AUD_IIR3_0_SEL, 0x00000021}, | ||
522 | {AUD_DN2_AFC, 0x00000002}, | ||
523 | {AUD_DCOC_1_SRC, 0x0000001b}, | ||
524 | {AUD_DCOC1_SHIFT, 0x00000000}, | ||
525 | {AUD_DCOC_1_SHIFT_IN0, 0x0000000a}, | ||
526 | {AUD_DCOC_1_SHIFT_IN1, 0x00000008}, | ||
527 | {AUD_IIR3_1_SEL, 0x00000023}, | ||
528 | {AUD_DN0_FREQ, 0x000035a3}, | ||
529 | {AUD_DN2_FREQ, 0x000029c7}, | ||
530 | {AUD_CRDC0_SRC_SEL, 0x00000511}, | ||
531 | {AUD_IIR1_0_SEL, 0x00000001}, | ||
532 | {AUD_IIR1_1_SEL, 0x00000000}, | ||
533 | {AUD_IIR3_2_SEL, 0x00000003}, | ||
534 | {AUD_IIR3_2_SHIFT, 0x00000000}, | ||
535 | {AUD_IIR3_0_SEL, 0x00000002}, | ||
536 | {AUD_IIR2_0_SEL, 0x00000021}, | ||
537 | {AUD_IIR2_0_SHIFT, 0x00000002}, | ||
538 | {AUD_DEEMPH0_SRC_SEL, 0x0000000b}, | ||
539 | {AUD_DEEMPH1_SRC_SEL, 0x0000000b}, | ||
540 | {AUD_POLYPH80SCALEFAC, 0x00000001}, | ||
541 | {AUD_START_TIMER, 0x00000000}, | ||
542 | { /* end of list */ }, | ||
543 | }; | ||
544 | |||
545 | static const struct rlist pal_i_nicam[] = { | ||
546 | { AUD_RATE_ADJ1, 0x00000010 }, | ||
547 | { AUD_RATE_ADJ2, 0x00000040 }, | ||
548 | { AUD_RATE_ADJ3, 0x00000100 }, | ||
549 | { AUD_RATE_ADJ4, 0x00000400 }, | ||
550 | { AUD_RATE_ADJ5, 0x00001000 }, | ||
551 | // { AUD_DMD_RA_DDS, 0x00c0d5ce }, | ||
552 | { AUD_DEEMPHGAIN_R, 0x000023c2 }, | ||
553 | { AUD_DEEMPHNUMER1_R, 0x0002a7bc }, | ||
554 | { AUD_DEEMPHNUMER2_R, 0x0003023e }, | ||
555 | { AUD_DEEMPHDENOM1_R, 0x0000f3d0 }, | ||
556 | { AUD_DEEMPHDENOM2_R, 0x00000000 }, | ||
557 | { AUD_DEEMPHDENOM2_R, 0x00000000 }, | ||
558 | { AUD_ERRLOGPERIOD_R, 0x00000fff }, | ||
559 | { AUD_ERRINTRPTTHSHLD1_R, 0x000003ff }, | ||
560 | { AUD_ERRINTRPTTHSHLD2_R, 0x000000ff }, | ||
561 | { AUD_ERRINTRPTTHSHLD3_R, 0x0000003f }, | ||
562 | { AUD_POLYPH80SCALEFAC, 0x00000003 }, | ||
563 | { AUD_PDF_DDS_CNST_BYTE2, 0x06 }, | ||
564 | { AUD_PDF_DDS_CNST_BYTE1, 0x82 }, | ||
565 | { AUD_PDF_DDS_CNST_BYTE0, 0x16 }, | ||
566 | { AUD_QAM_MODE, 0x05 }, | ||
567 | { AUD_PDF_DDS_CNST_BYTE0, 0x12 }, | ||
568 | { AUD_PHACC_FREQ_8MSB, 0x3a }, | ||
569 | { AUD_PHACC_FREQ_8LSB, 0x93 }, | ||
570 | { /* end of list */ }, | ||
571 | }; | ||
572 | |||
573 | dprintk("%s (status: devel), stereo : %d\n",__FUNCTION__,stereo); | ||
574 | |||
575 | if (!stereo) { | ||
576 | // FM mono | ||
577 | set_audio_start(core, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_FORCE_MONO1); | ||
578 | set_audio_registers(core, pal_i_fm_mono); | ||
579 | } else { | ||
580 | // Nicam Stereo | ||
581 | set_audio_start(core, 0x0010, EN_DMTRX_LR | EN_DMTRX_BYPASS | EN_NICAM_AUTO_STEREO); | ||
582 | set_audio_registers(core, pal_i_nicam); | ||
583 | } | ||
584 | set_audio_finish(core); | ||
585 | } | ||
586 | |||
587 | static void set_audio_standard_A2(struct cx88_core *core) | ||
588 | { | ||
589 | /* from dscaler cvs */ | ||
590 | static const struct rlist a2_common[] = { | ||
591 | { AUD_PDF_DDS_CNST_BYTE2, 0x06 }, | ||
592 | { AUD_PDF_DDS_CNST_BYTE1, 0x82 }, | ||
593 | { AUD_PDF_DDS_CNST_BYTE0, 0x12 }, | ||
594 | { AUD_QAM_MODE, 0x05 }, | ||
595 | { AUD_PHACC_FREQ_8MSB, 0x34 }, | ||
596 | { AUD_PHACC_FREQ_8LSB, 0x4c }, | ||
597 | |||
598 | { AUD_RATE_ADJ1, 0x00001000 }, | ||
599 | { AUD_RATE_ADJ2, 0x00002000 }, | ||
600 | { AUD_RATE_ADJ3, 0x00003000 }, | ||
601 | { AUD_RATE_ADJ4, 0x00004000 }, | ||
602 | { AUD_RATE_ADJ5, 0x00005000 }, | ||
603 | { AUD_THR_FR, 0x00000000 }, | ||
604 | { AAGC_HYST, 0x0000001a }, | ||
605 | { AUD_PILOT_BQD_1_K0, 0x0000755b }, | ||
606 | { AUD_PILOT_BQD_1_K1, 0x00551340 }, | ||
607 | { AUD_PILOT_BQD_1_K2, 0x006d30be }, | ||
608 | { AUD_PILOT_BQD_1_K3, 0xffd394af }, | ||
609 | { AUD_PILOT_BQD_1_K4, 0x00400000 }, | ||
610 | { AUD_PILOT_BQD_2_K0, 0x00040000 }, | ||
611 | { AUD_PILOT_BQD_2_K1, 0x002a4841 }, | ||
612 | { AUD_PILOT_BQD_2_K2, 0x00400000 }, | ||
613 | { AUD_PILOT_BQD_2_K3, 0x00000000 }, | ||
614 | { AUD_PILOT_BQD_2_K4, 0x00000000 }, | ||
615 | { AUD_MODE_CHG_TIMER, 0x00000040 }, | ||
616 | { AUD_START_TIMER, 0x00000200 }, | ||
617 | { AUD_AFE_12DB_EN, 0x00000000 }, | ||
618 | { AUD_CORDIC_SHIFT_0, 0x00000007 }, | ||
619 | { AUD_CORDIC_SHIFT_1, 0x00000007 }, | ||
620 | { AUD_DEEMPH0_G0, 0x00000380 }, | ||
621 | { AUD_DEEMPH1_G0, 0x00000380 }, | ||
622 | { AUD_DCOC_0_SRC, 0x0000001a }, | ||
623 | { AUD_DCOC0_SHIFT, 0x00000000 }, | ||
624 | { AUD_DCOC_0_SHIFT_IN0, 0x0000000a }, | ||
625 | { AUD_DCOC_0_SHIFT_IN1, 0x00000008 }, | ||
626 | { AUD_DCOC_PASS_IN, 0x00000003 }, | ||
627 | { AUD_IIR3_0_SEL, 0x00000021 }, | ||
628 | { AUD_DN2_AFC, 0x00000002 }, | ||
629 | { AUD_DCOC_1_SRC, 0x0000001b }, | ||
630 | { AUD_DCOC1_SHIFT, 0x00000000 }, | ||
631 | { AUD_DCOC_1_SHIFT_IN0, 0x0000000a }, | ||
632 | { AUD_DCOC_1_SHIFT_IN1, 0x00000008 }, | ||
633 | { AUD_IIR3_1_SEL, 0x00000023 }, | ||
634 | { AUD_RDSI_SEL, 0x00000017 }, | ||
635 | { AUD_RDSI_SHIFT, 0x00000000 }, | ||
636 | { AUD_RDSQ_SEL, 0x00000017 }, | ||
637 | { AUD_RDSQ_SHIFT, 0x00000000 }, | ||
638 | { AUD_POLYPH80SCALEFAC, 0x00000001 }, | ||
639 | |||
640 | { /* end of list */ }, | ||
641 | }; | ||
642 | |||
643 | static const struct rlist a2_table1[] = { | ||
644 | // PAL-BG | ||
645 | { AUD_DMD_RA_DDS, 0x002a73bd }, | ||
646 | { AUD_C1_UP_THR, 0x00007000 }, | ||
647 | { AUD_C1_LO_THR, 0x00005400 }, | ||
648 | { AUD_C2_UP_THR, 0x00005400 }, | ||
649 | { AUD_C2_LO_THR, 0x00003000 }, | ||
650 | { /* end of list */ }, | ||
651 | }; | ||
652 | static const struct rlist a2_table2[] = { | ||
653 | // PAL-DK | ||
654 | { AUD_DMD_RA_DDS, 0x002a73bd }, | ||
655 | { AUD_C1_UP_THR, 0x00007000 }, | ||
656 | { AUD_C1_LO_THR, 0x00005400 }, | ||
657 | { AUD_C2_UP_THR, 0x00005400 }, | ||
658 | { AUD_C2_LO_THR, 0x00003000 }, | ||
659 | { AUD_DN0_FREQ, 0x00003a1c }, | ||
660 | { AUD_DN2_FREQ, 0x0000d2e0 }, | ||
661 | { /* end of list */ }, | ||
662 | }; | ||
663 | static const struct rlist a2_table3[] = { | ||
664 | // unknown, probably NTSC-M | ||
665 | { AUD_DMD_RA_DDS, 0x002a2873 }, | ||
666 | { AUD_C1_UP_THR, 0x00003c00 }, | ||
667 | { AUD_C1_LO_THR, 0x00003000 }, | ||
668 | { AUD_C2_UP_THR, 0x00006000 }, | ||
669 | { AUD_C2_LO_THR, 0x00003c00 }, | ||
670 | { AUD_DN0_FREQ, 0x00002836 }, | ||
671 | { AUD_DN1_FREQ, 0x00003418 }, | ||
672 | { AUD_DN2_FREQ, 0x000029c7 }, | ||
673 | { AUD_POLY0_DDS_CONSTANT, 0x000a7540 }, | ||
674 | { /* end of list */ }, | ||
675 | }; | ||
676 | |||
677 | set_audio_start(core, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_AUTO_STEREO); | ||
678 | set_audio_registers(core, a2_common); | ||
679 | switch (core->tvaudio) { | ||
680 | case WW_A2_BG: | ||
681 | dprintk("%s PAL-BG A2 (status: known-good)\n",__FUNCTION__); | ||
682 | set_audio_registers(core, a2_table1); | ||
683 | break; | ||
684 | case WW_A2_DK: | ||
685 | dprintk("%s PAL-DK A2 (status: known-good)\n",__FUNCTION__); | ||
686 | set_audio_registers(core, a2_table2); | ||
687 | break; | ||
688 | case WW_A2_M: | ||
689 | dprintk("%s NTSC-M A2 (status: unknown)\n",__FUNCTION__); | ||
690 | set_audio_registers(core, a2_table3); | ||
691 | break; | ||
692 | }; | ||
693 | set_audio_finish(core); | ||
694 | } | ||
695 | |||
696 | static void set_audio_standard_EIAJ(struct cx88_core *core) | ||
697 | { | ||
698 | static const struct rlist eiaj[] = { | ||
699 | /* TODO: eiaj register settings are not there yet ... */ | ||
700 | |||
701 | { /* end of list */ }, | ||
702 | }; | ||
703 | dprintk("%s (status: unknown)\n",__FUNCTION__); | ||
704 | |||
705 | set_audio_start(core, 0x0002, EN_EIAJ_AUTO_STEREO); | ||
706 | set_audio_registers(core, eiaj); | ||
707 | set_audio_finish(core); | ||
708 | } | ||
709 | |||
710 | static void set_audio_standard_FM(struct cx88_core *core) | ||
711 | { | ||
712 | #if 0 /* FIXME */ | ||
713 | switch (dev->audio_properties.FM_deemphasis) | ||
714 | { | ||
715 | case WW_FM_DEEMPH_50: | ||
716 | //Set De-emphasis filter coefficients for 50 usec | ||
717 | cx_write(AUD_DEEMPH0_G0, 0x0C45); | ||
718 | cx_write(AUD_DEEMPH0_A0, 0x6262); | ||
719 | cx_write(AUD_DEEMPH0_B0, 0x1C29); | ||
720 | cx_write(AUD_DEEMPH0_A1, 0x3FC66); | ||
721 | cx_write(AUD_DEEMPH0_B1, 0x399A); | ||
722 | |||
723 | cx_write(AUD_DEEMPH1_G0, 0x0D80); | ||
724 | cx_write(AUD_DEEMPH1_A0, 0x6262); | ||
725 | cx_write(AUD_DEEMPH1_B0, 0x1C29); | ||
726 | cx_write(AUD_DEEMPH1_A1, 0x3FC66); | ||
727 | cx_write(AUD_DEEMPH1_B1, 0x399A); | ||
728 | |||
729 | break; | ||
730 | |||
731 | case WW_FM_DEEMPH_75: | ||
732 | //Set De-emphasis filter coefficients for 75 usec | ||
733 | cx_write(AUD_DEEMPH0_G0, 0x91B ); | ||
734 | cx_write(AUD_DEEMPH0_A0, 0x6B68); | ||
735 | cx_write(AUD_DEEMPH0_B0, 0x11EC); | ||
736 | cx_write(AUD_DEEMPH0_A1, 0x3FC66); | ||
737 | cx_write(AUD_DEEMPH0_B1, 0x399A); | ||
738 | |||
739 | cx_write(AUD_DEEMPH1_G0, 0xAA0 ); | ||
740 | cx_write(AUD_DEEMPH1_A0, 0x6B68); | ||
741 | cx_write(AUD_DEEMPH1_B0, 0x11EC); | ||
742 | cx_write(AUD_DEEMPH1_A1, 0x3FC66); | ||
743 | cx_write(AUD_DEEMPH1_B1, 0x399A); | ||
744 | |||
745 | break; | ||
746 | } | ||
747 | #endif | ||
748 | |||
749 | dprintk("%s (status: unknown)\n",__FUNCTION__); | ||
750 | set_audio_start(core, 0x0020, EN_FMRADIO_AUTO_STEREO); | ||
751 | |||
752 | // AB: 10/2/01: this register is not being reset appropriately on occasion. | ||
753 | cx_write(AUD_POLYPH80SCALEFAC,3); | ||
754 | |||
755 | set_audio_finish(core); | ||
756 | } | ||
757 | |||
758 | /* ----------------------------------------------------------- */ | ||
759 | |||
760 | void cx88_set_tvaudio(struct cx88_core *core) | ||
761 | { | ||
762 | switch (core->tvaudio) { | ||
763 | case WW_BTSC: | ||
764 | set_audio_standard_BTSC(core,0); | ||
765 | break; | ||
766 | case WW_NICAM_BGDKL: | ||
767 | set_audio_standard_NICAM_L(core,0); | ||
768 | break; | ||
769 | case WW_NICAM_I: | ||
770 | set_audio_standard_PAL_I(core,0); | ||
771 | break; | ||
772 | case WW_A2_BG: | ||
773 | case WW_A2_DK: | ||
774 | case WW_A2_M: | ||
775 | set_audio_standard_A2(core); | ||
776 | break; | ||
777 | case WW_EIAJ: | ||
778 | set_audio_standard_EIAJ(core); | ||
779 | break; | ||
780 | case WW_FM: | ||
781 | set_audio_standard_FM(core); | ||
782 | break; | ||
783 | case WW_SYSTEM_L_AM: | ||
784 | set_audio_standard_NICAM_L(core, 1); | ||
785 | break; | ||
786 | case WW_NONE: | ||
787 | default: | ||
788 | printk("%s/0: unknown tv audio mode [%d]\n", | ||
789 | core->name, core->tvaudio); | ||
790 | break; | ||
791 | } | ||
792 | return; | ||
793 | } | ||
794 | |||
795 | void cx88_newstation(struct cx88_core *core) | ||
796 | { | ||
797 | core->audiomode_manual = UNSET; | ||
798 | |||
799 | switch (core->tvaudio) { | ||
800 | case WW_SYSTEM_L_AM: | ||
801 | /* try nicam ... */ | ||
802 | core->audiomode_current = V4L2_TUNER_MODE_STEREO; | ||
803 | set_audio_standard_NICAM_L(core, 1); | ||
804 | break; | ||
805 | } | ||
806 | } | ||
807 | |||
808 | void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t) | ||
809 | { | ||
810 | static char *m[] = {"stereo", "dual mono", "mono", "sap"}; | ||
811 | static char *p[] = {"no pilot", "pilot c1", "pilot c2", "?"}; | ||
812 | u32 reg,mode,pilot; | ||
813 | |||
814 | reg = cx_read(AUD_STATUS); | ||
815 | mode = reg & 0x03; | ||
816 | pilot = (reg >> 2) & 0x03; | ||
817 | |||
818 | if (core->astat != reg) | ||
819 | dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n", | ||
820 | reg, m[mode], p[pilot], | ||
821 | aud_ctl_names[cx_read(AUD_CTL) & 63]); | ||
822 | core->astat = reg; | ||
823 | |||
824 | t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP | | ||
825 | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; | ||
826 | t->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
827 | t->audmode = V4L2_TUNER_MODE_MONO; | ||
828 | |||
829 | switch (core->tvaudio) { | ||
830 | case WW_BTSC: | ||
831 | t->capability = V4L2_TUNER_CAP_STEREO | | ||
832 | V4L2_TUNER_CAP_SAP; | ||
833 | t->rxsubchans = V4L2_TUNER_SUB_STEREO; | ||
834 | if (1 == pilot) { | ||
835 | /* SAP */ | ||
836 | t->rxsubchans |= V4L2_TUNER_SUB_SAP; | ||
837 | } | ||
838 | break; | ||
839 | case WW_A2_BG: | ||
840 | case WW_A2_DK: | ||
841 | case WW_A2_M: | ||
842 | if (1 == pilot) { | ||
843 | /* stereo */ | ||
844 | t->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; | ||
845 | if (0 == mode) | ||
846 | t->audmode = V4L2_TUNER_MODE_STEREO; | ||
847 | } | ||
848 | if (2 == pilot) { | ||
849 | /* dual language -- FIXME */ | ||
850 | t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; | ||
851 | t->audmode = V4L2_TUNER_MODE_LANG1; | ||
852 | } | ||
853 | break; | ||
854 | case WW_NICAM_BGDKL: | ||
855 | if (0 == mode) { | ||
856 | t->audmode = V4L2_TUNER_MODE_STEREO; | ||
857 | t->rxsubchans |= V4L2_TUNER_SUB_STEREO; | ||
858 | } | ||
859 | break; | ||
860 | case WW_SYSTEM_L_AM: | ||
861 | if (0x0 == mode && !(cx_read(AUD_INIT) & 0x04)) { | ||
862 | t->audmode = V4L2_TUNER_MODE_STEREO; | ||
863 | t->rxsubchans |= V4L2_TUNER_SUB_STEREO; | ||
864 | } | ||
865 | break ; | ||
866 | default: | ||
867 | /* nothing */ | ||
868 | break; | ||
869 | } | ||
870 | return; | ||
871 | } | ||
872 | |||
873 | void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual) | ||
874 | { | ||
875 | u32 ctl = UNSET; | ||
876 | u32 mask = UNSET; | ||
877 | |||
878 | if (manual) { | ||
879 | core->audiomode_manual = mode; | ||
880 | } else { | ||
881 | if (UNSET != core->audiomode_manual) | ||
882 | return; | ||
883 | } | ||
884 | core->audiomode_current = mode; | ||
885 | |||
886 | switch (core->tvaudio) { | ||
887 | case WW_BTSC: | ||
888 | switch (mode) { | ||
889 | case V4L2_TUNER_MODE_MONO: | ||
890 | ctl = EN_BTSC_FORCE_MONO; | ||
891 | mask = 0x3f; | ||
892 | break; | ||
893 | case V4L2_TUNER_MODE_SAP: | ||
894 | ctl = EN_BTSC_FORCE_SAP; | ||
895 | mask = 0x3f; | ||
896 | break; | ||
897 | case V4L2_TUNER_MODE_STEREO: | ||
898 | ctl = EN_BTSC_AUTO_STEREO; | ||
899 | mask = 0x3f; | ||
900 | break; | ||
901 | } | ||
902 | break; | ||
903 | case WW_A2_BG: | ||
904 | case WW_A2_DK: | ||
905 | case WW_A2_M: | ||
906 | switch (mode) { | ||
907 | case V4L2_TUNER_MODE_MONO: | ||
908 | case V4L2_TUNER_MODE_LANG1: | ||
909 | ctl = EN_A2_FORCE_MONO1; | ||
910 | mask = 0x3f; | ||
911 | break; | ||
912 | case V4L2_TUNER_MODE_LANG2: | ||
913 | ctl = EN_A2_AUTO_MONO2; | ||
914 | mask = 0x3f; | ||
915 | break; | ||
916 | case V4L2_TUNER_MODE_STEREO: | ||
917 | ctl = EN_A2_AUTO_STEREO | EN_DMTRX_SUMR; | ||
918 | mask = 0x8bf; | ||
919 | break; | ||
920 | } | ||
921 | break; | ||
922 | case WW_NICAM_BGDKL: | ||
923 | switch (mode) { | ||
924 | case V4L2_TUNER_MODE_MONO: | ||
925 | ctl = EN_NICAM_FORCE_MONO1; | ||
926 | mask = 0x3f; | ||
927 | break; | ||
928 | case V4L2_TUNER_MODE_LANG1: | ||
929 | ctl = EN_NICAM_AUTO_MONO2; | ||
930 | mask = 0x3f; | ||
931 | break; | ||
932 | case V4L2_TUNER_MODE_STEREO: | ||
933 | ctl = EN_NICAM_FORCE_STEREO | EN_DMTRX_LR; | ||
934 | mask = 0x93f; | ||
935 | break; | ||
936 | } | ||
937 | break; | ||
938 | case WW_SYSTEM_L_AM: | ||
939 | switch (mode) { | ||
940 | case V4L2_TUNER_MODE_MONO: | ||
941 | case V4L2_TUNER_MODE_LANG1: /* FIXME */ | ||
942 | set_audio_standard_NICAM_L(core, 0); | ||
943 | break; | ||
944 | case V4L2_TUNER_MODE_STEREO: | ||
945 | set_audio_standard_NICAM_L(core, 1); | ||
946 | break; | ||
947 | } | ||
948 | break; | ||
949 | case WW_NICAM_I: | ||
950 | switch (mode) { | ||
951 | case V4L2_TUNER_MODE_MONO: | ||
952 | case V4L2_TUNER_MODE_LANG1: | ||
953 | set_audio_standard_PAL_I(core, 0); | ||
954 | break; | ||
955 | case V4L2_TUNER_MODE_STEREO: | ||
956 | set_audio_standard_PAL_I(core, 1); | ||
957 | break; | ||
958 | } | ||
959 | break; | ||
960 | case WW_FM: | ||
961 | switch (mode) { | ||
962 | case V4L2_TUNER_MODE_MONO: | ||
963 | ctl = EN_FMRADIO_FORCE_MONO; | ||
964 | mask = 0x3f; | ||
965 | break; | ||
966 | case V4L2_TUNER_MODE_STEREO: | ||
967 | ctl = EN_FMRADIO_AUTO_STEREO; | ||
968 | mask = 0x3f; | ||
969 | break; | ||
970 | } | ||
971 | break; | ||
972 | } | ||
973 | |||
974 | if (UNSET != ctl) { | ||
975 | dprintk("cx88_set_stereo: mask 0x%x, ctl 0x%x " | ||
976 | "[status=0x%x,ctl=0x%x,vol=0x%x]\n", | ||
977 | mask, ctl, cx_read(AUD_STATUS), | ||
978 | cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL)); | ||
979 | cx_andor(AUD_CTL, mask, ctl); | ||
980 | } | ||
981 | return; | ||
982 | } | ||
983 | |||
984 | int cx88_audio_thread(void *data) | ||
985 | { | ||
986 | struct cx88_core *core = data; | ||
987 | struct v4l2_tuner t; | ||
988 | u32 mode = 0; | ||
989 | |||
990 | dprintk("cx88: tvaudio thread started\n"); | ||
991 | for (;;) { | ||
992 | msleep_interruptible(1000); | ||
993 | if (kthread_should_stop()) | ||
994 | break; | ||
995 | |||
996 | /* just monitor the audio status for now ... */ | ||
997 | memset(&t,0,sizeof(t)); | ||
998 | cx88_get_stereo(core,&t); | ||
999 | |||
1000 | if (UNSET != core->audiomode_manual) | ||
1001 | /* manually set, don't do anything. */ | ||
1002 | continue; | ||
1003 | |||
1004 | /* monitor signal */ | ||
1005 | if (t.rxsubchans & V4L2_TUNER_SUB_STEREO) | ||
1006 | mode = V4L2_TUNER_MODE_STEREO; | ||
1007 | else | ||
1008 | mode = V4L2_TUNER_MODE_MONO; | ||
1009 | if (mode == core->audiomode_current) | ||
1010 | continue; | ||
1011 | |||
1012 | /* automatically switch to best available mode */ | ||
1013 | cx88_set_stereo(core, mode, 0); | ||
1014 | } | ||
1015 | |||
1016 | dprintk("cx88: tvaudio thread exiting\n"); | ||
1017 | return 0; | ||
1018 | } | ||
1019 | |||
1020 | /* ----------------------------------------------------------- */ | ||
1021 | |||
1022 | EXPORT_SYMBOL(cx88_set_tvaudio); | ||
1023 | EXPORT_SYMBOL(cx88_newstation); | ||
1024 | EXPORT_SYMBOL(cx88_set_stereo); | ||
1025 | EXPORT_SYMBOL(cx88_get_stereo); | ||
1026 | EXPORT_SYMBOL(cx88_audio_thread); | ||
1027 | |||
1028 | /* | ||
1029 | * Local variables: | ||
1030 | * c-basic-offset: 8 | ||
1031 | * End: | ||
1032 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-vbi.c b/drivers/media/video/cx88/cx88-vbi.c new file mode 100644 index 000000000000..471e508b0746 --- /dev/null +++ b/drivers/media/video/cx88/cx88-vbi.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | * $Id: cx88-vbi.c,v 1.16 2004/12/10 12:33:39 kraxel Exp $ | ||
3 | */ | ||
4 | #include <linux/kernel.h> | ||
5 | #include <linux/module.h> | ||
6 | #include <linux/moduleparam.h> | ||
7 | #include <linux/init.h> | ||
8 | #include <linux/slab.h> | ||
9 | |||
10 | #include "cx88.h" | ||
11 | |||
12 | static unsigned int vbibufs = 4; | ||
13 | module_param(vbibufs,int,0644); | ||
14 | MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32"); | ||
15 | |||
16 | static unsigned int vbi_debug = 0; | ||
17 | module_param(vbi_debug,int,0644); | ||
18 | MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]"); | ||
19 | |||
20 | #define dprintk(level,fmt, arg...) if (vbi_debug >= level) \ | ||
21 | printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) | ||
22 | |||
23 | /* ------------------------------------------------------------------ */ | ||
24 | |||
25 | void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f) | ||
26 | { | ||
27 | memset(&f->fmt.vbi,0,sizeof(f->fmt.vbi)); | ||
28 | |||
29 | f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; | ||
30 | f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; | ||
31 | f->fmt.vbi.offset = 244; | ||
32 | f->fmt.vbi.count[0] = VBI_LINE_COUNT; | ||
33 | f->fmt.vbi.count[1] = VBI_LINE_COUNT; | ||
34 | |||
35 | if (dev->core->tvnorm->id & V4L2_STD_525_60) { | ||
36 | /* ntsc */ | ||
37 | f->fmt.vbi.sampling_rate = 28636363; | ||
38 | f->fmt.vbi.start[0] = 10 -1; | ||
39 | f->fmt.vbi.start[1] = 273 -1; | ||
40 | |||
41 | } else if (dev->core->tvnorm->id & V4L2_STD_625_50) { | ||
42 | /* pal */ | ||
43 | f->fmt.vbi.sampling_rate = 35468950; | ||
44 | f->fmt.vbi.start[0] = 7 -1; | ||
45 | f->fmt.vbi.start[1] = 319 -1; | ||
46 | } | ||
47 | } | ||
48 | |||
49 | int cx8800_start_vbi_dma(struct cx8800_dev *dev, | ||
50 | struct cx88_dmaqueue *q, | ||
51 | struct cx88_buffer *buf) | ||
52 | { | ||
53 | struct cx88_core *core = dev->core; | ||
54 | |||
55 | /* setup fifo + format */ | ||
56 | cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24], | ||
57 | buf->vb.width, buf->risc.dma); | ||
58 | |||
59 | cx_write(MO_VBOS_CONTROL, ( (1 << 18) | // comb filter delay fixup | ||
60 | (1 << 15) | // enable vbi capture | ||
61 | (1 << 11) )); | ||
62 | |||
63 | /* reset counter */ | ||
64 | cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET); | ||
65 | q->count = 1; | ||
66 | |||
67 | /* enable irqs */ | ||
68 | cx_set(MO_PCI_INTMSK, core->pci_irqmask | 0x01); | ||
69 | cx_set(MO_VID_INTMSK, 0x0f0088); | ||
70 | |||
71 | /* enable capture */ | ||
72 | cx_set(VID_CAPTURE_CONTROL,0x18); | ||
73 | |||
74 | /* start dma */ | ||
75 | cx_set(MO_DEV_CNTRL2, (1<<5)); | ||
76 | cx_set(MO_VID_DMACNTRL, 0x88); | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | int cx8800_stop_vbi_dma(struct cx8800_dev *dev) | ||
82 | { | ||
83 | struct cx88_core *core = dev->core; | ||
84 | |||
85 | /* stop dma */ | ||
86 | cx_clear(MO_VID_DMACNTRL, 0x88); | ||
87 | |||
88 | /* disable capture */ | ||
89 | cx_clear(VID_CAPTURE_CONTROL,0x18); | ||
90 | |||
91 | /* disable irqs */ | ||
92 | cx_clear(MO_PCI_INTMSK, 0x000001); | ||
93 | cx_clear(MO_VID_INTMSK, 0x0f0088); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | int cx8800_restart_vbi_queue(struct cx8800_dev *dev, | ||
98 | struct cx88_dmaqueue *q) | ||
99 | { | ||
100 | struct cx88_buffer *buf; | ||
101 | struct list_head *item; | ||
102 | |||
103 | if (list_empty(&q->active)) | ||
104 | return 0; | ||
105 | |||
106 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
107 | dprintk(2,"restart_queue [%p/%d]: restart dma\n", | ||
108 | buf, buf->vb.i); | ||
109 | cx8800_start_vbi_dma(dev, q, buf); | ||
110 | list_for_each(item,&q->active) { | ||
111 | buf = list_entry(item, struct cx88_buffer, vb.queue); | ||
112 | buf->count = q->count++; | ||
113 | } | ||
114 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | void cx8800_vbi_timeout(unsigned long data) | ||
119 | { | ||
120 | struct cx8800_dev *dev = (struct cx8800_dev*)data; | ||
121 | struct cx88_core *core = dev->core; | ||
122 | struct cx88_dmaqueue *q = &dev->vbiq; | ||
123 | struct cx88_buffer *buf; | ||
124 | unsigned long flags; | ||
125 | |||
126 | cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH24]); | ||
127 | |||
128 | cx_clear(MO_VID_DMACNTRL, 0x88); | ||
129 | cx_clear(VID_CAPTURE_CONTROL, 0x18); | ||
130 | |||
131 | spin_lock_irqsave(&dev->slock,flags); | ||
132 | while (!list_empty(&q->active)) { | ||
133 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
134 | list_del(&buf->vb.queue); | ||
135 | buf->vb.state = STATE_ERROR; | ||
136 | wake_up(&buf->vb.done); | ||
137 | printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name, | ||
138 | buf, buf->vb.i, (unsigned long)buf->risc.dma); | ||
139 | } | ||
140 | cx8800_restart_vbi_queue(dev,q); | ||
141 | spin_unlock_irqrestore(&dev->slock,flags); | ||
142 | } | ||
143 | |||
144 | /* ------------------------------------------------------------------ */ | ||
145 | |||
146 | static int | ||
147 | vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) | ||
148 | { | ||
149 | *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; | ||
150 | if (0 == *count) | ||
151 | *count = vbibufs; | ||
152 | if (*count < 2) | ||
153 | *count = 2; | ||
154 | if (*count > 32) | ||
155 | *count = 32; | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static int | ||
160 | vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, | ||
161 | enum v4l2_field field) | ||
162 | { | ||
163 | struct cx8800_fh *fh = q->priv_data; | ||
164 | struct cx8800_dev *dev = fh->dev; | ||
165 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
166 | unsigned int size; | ||
167 | int rc; | ||
168 | |||
169 | size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; | ||
170 | if (0 != buf->vb.baddr && buf->vb.bsize < size) | ||
171 | return -EINVAL; | ||
172 | |||
173 | if (STATE_NEEDS_INIT == buf->vb.state) { | ||
174 | buf->vb.width = VBI_LINE_LENGTH; | ||
175 | buf->vb.height = VBI_LINE_COUNT; | ||
176 | buf->vb.size = size; | ||
177 | buf->vb.field = V4L2_FIELD_SEQ_TB; | ||
178 | |||
179 | if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL))) | ||
180 | goto fail; | ||
181 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
182 | buf->vb.dma.sglist, | ||
183 | 0, buf->vb.width * buf->vb.height, | ||
184 | buf->vb.width, 0, | ||
185 | buf->vb.height); | ||
186 | } | ||
187 | buf->vb.state = STATE_PREPARED; | ||
188 | return 0; | ||
189 | |||
190 | fail: | ||
191 | cx88_free_buffer(dev->pci,buf); | ||
192 | return rc; | ||
193 | } | ||
194 | |||
195 | static void | ||
196 | vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) | ||
197 | { | ||
198 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
199 | struct cx88_buffer *prev; | ||
200 | struct cx8800_fh *fh = vq->priv_data; | ||
201 | struct cx8800_dev *dev = fh->dev; | ||
202 | struct cx88_dmaqueue *q = &dev->vbiq; | ||
203 | |||
204 | /* add jump to stopper */ | ||
205 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); | ||
206 | buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); | ||
207 | |||
208 | if (list_empty(&q->active)) { | ||
209 | list_add_tail(&buf->vb.queue,&q->active); | ||
210 | cx8800_start_vbi_dma(dev, q, buf); | ||
211 | buf->vb.state = STATE_ACTIVE; | ||
212 | buf->count = q->count++; | ||
213 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
214 | dprintk(2,"[%p/%d] vbi_queue - first active\n", | ||
215 | buf, buf->vb.i); | ||
216 | |||
217 | } else { | ||
218 | prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); | ||
219 | list_add_tail(&buf->vb.queue,&q->active); | ||
220 | buf->vb.state = STATE_ACTIVE; | ||
221 | buf->count = q->count++; | ||
222 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
223 | dprintk(2,"[%p/%d] buffer_queue - append to active\n", | ||
224 | buf, buf->vb.i); | ||
225 | } | ||
226 | } | ||
227 | |||
228 | static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
229 | { | ||
230 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
231 | struct cx8800_fh *fh = q->priv_data; | ||
232 | |||
233 | cx88_free_buffer(fh->dev->pci,buf); | ||
234 | } | ||
235 | |||
236 | struct videobuf_queue_ops cx8800_vbi_qops = { | ||
237 | .buf_setup = vbi_setup, | ||
238 | .buf_prepare = vbi_prepare, | ||
239 | .buf_queue = vbi_queue, | ||
240 | .buf_release = vbi_release, | ||
241 | }; | ||
242 | |||
243 | /* ------------------------------------------------------------------ */ | ||
244 | /* | ||
245 | * Local variables: | ||
246 | * c-basic-offset: 8 | ||
247 | * End: | ||
248 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c new file mode 100644 index 000000000000..701f594e1816 --- /dev/null +++ b/drivers/media/video/cx88/cx88-video.c | |||
@@ -0,0 +1,2277 @@ | |||
1 | /* | ||
2 | * $Id: cx88-video.c,v 1.58 2005/03/07 15:58:05 kraxel Exp $ | ||
3 | * | ||
4 | * device driver for Conexant 2388x based TV cards | ||
5 | * video4linux video interface | ||
6 | * | ||
7 | * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/init.h> | ||
25 | #include <linux/list.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/moduleparam.h> | ||
28 | #include <linux/kmod.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/slab.h> | ||
31 | #include <linux/interrupt.h> | ||
32 | #include <linux/delay.h> | ||
33 | #include <linux/kthread.h> | ||
34 | #include <asm/div64.h> | ||
35 | |||
36 | #include "cx88.h" | ||
37 | |||
38 | MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); | ||
39 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
40 | MODULE_LICENSE("GPL"); | ||
41 | |||
42 | /* ------------------------------------------------------------------ */ | ||
43 | |||
44 | static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
45 | static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
46 | static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
47 | |||
48 | module_param_array(video_nr, int, NULL, 0444); | ||
49 | module_param_array(vbi_nr, int, NULL, 0444); | ||
50 | module_param_array(radio_nr, int, NULL, 0444); | ||
51 | |||
52 | MODULE_PARM_DESC(video_nr,"video device numbers"); | ||
53 | MODULE_PARM_DESC(vbi_nr,"vbi device numbers"); | ||
54 | MODULE_PARM_DESC(radio_nr,"radio device numbers"); | ||
55 | |||
56 | static unsigned int video_debug = 0; | ||
57 | module_param(video_debug,int,0644); | ||
58 | MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); | ||
59 | |||
60 | static unsigned int irq_debug = 0; | ||
61 | module_param(irq_debug,int,0644); | ||
62 | MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]"); | ||
63 | |||
64 | static unsigned int vid_limit = 16; | ||
65 | module_param(vid_limit,int,0644); | ||
66 | MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes"); | ||
67 | |||
68 | #define dprintk(level,fmt, arg...) if (video_debug >= level) \ | ||
69 | printk(KERN_DEBUG "%s/0: " fmt, dev->core->name , ## arg) | ||
70 | |||
71 | /* ------------------------------------------------------------------ */ | ||
72 | |||
73 | static LIST_HEAD(cx8800_devlist); | ||
74 | |||
75 | /* ------------------------------------------------------------------- */ | ||
76 | /* static data */ | ||
77 | |||
78 | static struct cx88_tvnorm tvnorms[] = { | ||
79 | { | ||
80 | .name = "NTSC-M", | ||
81 | .id = V4L2_STD_NTSC_M, | ||
82 | .cxiformat = VideoFormatNTSC, | ||
83 | .cxoformat = 0x181f0008, | ||
84 | },{ | ||
85 | .name = "NTSC-JP", | ||
86 | .id = V4L2_STD_NTSC_M_JP, | ||
87 | .cxiformat = VideoFormatNTSCJapan, | ||
88 | .cxoformat = 0x181f0008, | ||
89 | #if 0 | ||
90 | },{ | ||
91 | .name = "NTSC-4.43", | ||
92 | .id = FIXME, | ||
93 | .cxiformat = VideoFormatNTSC443, | ||
94 | .cxoformat = 0x181f0008, | ||
95 | #endif | ||
96 | },{ | ||
97 | .name = "PAL-BG", | ||
98 | .id = V4L2_STD_PAL_BG, | ||
99 | .cxiformat = VideoFormatPAL, | ||
100 | .cxoformat = 0x181f0008, | ||
101 | },{ | ||
102 | .name = "PAL-DK", | ||
103 | .id = V4L2_STD_PAL_DK, | ||
104 | .cxiformat = VideoFormatPAL, | ||
105 | .cxoformat = 0x181f0008, | ||
106 | },{ | ||
107 | .name = "PAL-I", | ||
108 | .id = V4L2_STD_PAL_I, | ||
109 | .cxiformat = VideoFormatPAL, | ||
110 | .cxoformat = 0x181f0008, | ||
111 | },{ | ||
112 | .name = "PAL-M", | ||
113 | .id = V4L2_STD_PAL_M, | ||
114 | .cxiformat = VideoFormatPALM, | ||
115 | .cxoformat = 0x1c1f0008, | ||
116 | },{ | ||
117 | .name = "PAL-N", | ||
118 | .id = V4L2_STD_PAL_N, | ||
119 | .cxiformat = VideoFormatPALN, | ||
120 | .cxoformat = 0x1c1f0008, | ||
121 | },{ | ||
122 | .name = "PAL-Nc", | ||
123 | .id = V4L2_STD_PAL_Nc, | ||
124 | .cxiformat = VideoFormatPALNC, | ||
125 | .cxoformat = 0x1c1f0008, | ||
126 | },{ | ||
127 | .name = "PAL-60", | ||
128 | .id = V4L2_STD_PAL_60, | ||
129 | .cxiformat = VideoFormatPAL60, | ||
130 | .cxoformat = 0x181f0008, | ||
131 | },{ | ||
132 | .name = "SECAM-L", | ||
133 | .id = V4L2_STD_SECAM_L, | ||
134 | .cxiformat = VideoFormatSECAM, | ||
135 | .cxoformat = 0x181f0008, | ||
136 | },{ | ||
137 | .name = "SECAM-DK", | ||
138 | .id = V4L2_STD_SECAM_DK, | ||
139 | .cxiformat = VideoFormatSECAM, | ||
140 | .cxoformat = 0x181f0008, | ||
141 | } | ||
142 | }; | ||
143 | |||
144 | static struct cx8800_fmt formats[] = { | ||
145 | { | ||
146 | .name = "8 bpp, gray", | ||
147 | .fourcc = V4L2_PIX_FMT_GREY, | ||
148 | .cxformat = ColorFormatY8, | ||
149 | .depth = 8, | ||
150 | .flags = FORMAT_FLAGS_PACKED, | ||
151 | },{ | ||
152 | .name = "15 bpp RGB, le", | ||
153 | .fourcc = V4L2_PIX_FMT_RGB555, | ||
154 | .cxformat = ColorFormatRGB15, | ||
155 | .depth = 16, | ||
156 | .flags = FORMAT_FLAGS_PACKED, | ||
157 | },{ | ||
158 | .name = "15 bpp RGB, be", | ||
159 | .fourcc = V4L2_PIX_FMT_RGB555X, | ||
160 | .cxformat = ColorFormatRGB15 | ColorFormatBSWAP, | ||
161 | .depth = 16, | ||
162 | .flags = FORMAT_FLAGS_PACKED, | ||
163 | },{ | ||
164 | .name = "16 bpp RGB, le", | ||
165 | .fourcc = V4L2_PIX_FMT_RGB565, | ||
166 | .cxformat = ColorFormatRGB16, | ||
167 | .depth = 16, | ||
168 | .flags = FORMAT_FLAGS_PACKED, | ||
169 | },{ | ||
170 | .name = "16 bpp RGB, be", | ||
171 | .fourcc = V4L2_PIX_FMT_RGB565X, | ||
172 | .cxformat = ColorFormatRGB16 | ColorFormatBSWAP, | ||
173 | .depth = 16, | ||
174 | .flags = FORMAT_FLAGS_PACKED, | ||
175 | },{ | ||
176 | .name = "24 bpp RGB, le", | ||
177 | .fourcc = V4L2_PIX_FMT_BGR24, | ||
178 | .cxformat = ColorFormatRGB24, | ||
179 | .depth = 24, | ||
180 | .flags = FORMAT_FLAGS_PACKED, | ||
181 | },{ | ||
182 | .name = "32 bpp RGB, le", | ||
183 | .fourcc = V4L2_PIX_FMT_BGR32, | ||
184 | .cxformat = ColorFormatRGB32, | ||
185 | .depth = 32, | ||
186 | .flags = FORMAT_FLAGS_PACKED, | ||
187 | },{ | ||
188 | .name = "32 bpp RGB, be", | ||
189 | .fourcc = V4L2_PIX_FMT_RGB32, | ||
190 | .cxformat = ColorFormatRGB32 | ColorFormatBSWAP | ColorFormatWSWAP, | ||
191 | .depth = 32, | ||
192 | .flags = FORMAT_FLAGS_PACKED, | ||
193 | },{ | ||
194 | .name = "4:2:2, packed, YUYV", | ||
195 | .fourcc = V4L2_PIX_FMT_YUYV, | ||
196 | .cxformat = ColorFormatYUY2, | ||
197 | .depth = 16, | ||
198 | .flags = FORMAT_FLAGS_PACKED, | ||
199 | },{ | ||
200 | .name = "4:2:2, packed, UYVY", | ||
201 | .fourcc = V4L2_PIX_FMT_UYVY, | ||
202 | .cxformat = ColorFormatYUY2 | ColorFormatBSWAP, | ||
203 | .depth = 16, | ||
204 | .flags = FORMAT_FLAGS_PACKED, | ||
205 | }, | ||
206 | }; | ||
207 | |||
208 | static struct cx8800_fmt* format_by_fourcc(unsigned int fourcc) | ||
209 | { | ||
210 | unsigned int i; | ||
211 | |||
212 | for (i = 0; i < ARRAY_SIZE(formats); i++) | ||
213 | if (formats[i].fourcc == fourcc) | ||
214 | return formats+i; | ||
215 | return NULL; | ||
216 | } | ||
217 | |||
218 | /* ------------------------------------------------------------------- */ | ||
219 | |||
220 | static const struct v4l2_queryctrl no_ctl = { | ||
221 | .name = "42", | ||
222 | .flags = V4L2_CTRL_FLAG_DISABLED, | ||
223 | }; | ||
224 | |||
225 | static struct cx88_ctrl cx8800_ctls[] = { | ||
226 | /* --- video --- */ | ||
227 | { | ||
228 | .v = { | ||
229 | .id = V4L2_CID_BRIGHTNESS, | ||
230 | .name = "Brightness", | ||
231 | .minimum = 0x00, | ||
232 | .maximum = 0xff, | ||
233 | .step = 1, | ||
234 | .default_value = 0, | ||
235 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
236 | }, | ||
237 | .off = 128, | ||
238 | .reg = MO_CONTR_BRIGHT, | ||
239 | .mask = 0x00ff, | ||
240 | .shift = 0, | ||
241 | },{ | ||
242 | .v = { | ||
243 | .id = V4L2_CID_CONTRAST, | ||
244 | .name = "Contrast", | ||
245 | .minimum = 0, | ||
246 | .maximum = 0xff, | ||
247 | .step = 1, | ||
248 | .default_value = 0, | ||
249 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
250 | }, | ||
251 | .reg = MO_CONTR_BRIGHT, | ||
252 | .mask = 0xff00, | ||
253 | .shift = 8, | ||
254 | },{ | ||
255 | .v = { | ||
256 | .id = V4L2_CID_HUE, | ||
257 | .name = "Hue", | ||
258 | .minimum = 0, | ||
259 | .maximum = 0xff, | ||
260 | .step = 1, | ||
261 | .default_value = 0, | ||
262 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
263 | }, | ||
264 | .off = 0, | ||
265 | .reg = MO_HUE, | ||
266 | .mask = 0x00ff, | ||
267 | .shift = 0, | ||
268 | },{ | ||
269 | /* strictly, this only describes only U saturation. | ||
270 | * V saturation is handled specially through code. | ||
271 | */ | ||
272 | .v = { | ||
273 | .id = V4L2_CID_SATURATION, | ||
274 | .name = "Saturation", | ||
275 | .minimum = 0, | ||
276 | .maximum = 0xff, | ||
277 | .step = 1, | ||
278 | .default_value = 0, | ||
279 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
280 | }, | ||
281 | .off = 0, | ||
282 | .reg = MO_UV_SATURATION, | ||
283 | .mask = 0x00ff, | ||
284 | .shift = 0, | ||
285 | },{ | ||
286 | /* --- audio --- */ | ||
287 | .v = { | ||
288 | .id = V4L2_CID_AUDIO_MUTE, | ||
289 | .name = "Mute", | ||
290 | .minimum = 0, | ||
291 | .maximum = 1, | ||
292 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
293 | }, | ||
294 | .reg = AUD_VOL_CTL, | ||
295 | .sreg = SHADOW_AUD_VOL_CTL, | ||
296 | .mask = (1 << 6), | ||
297 | .shift = 6, | ||
298 | },{ | ||
299 | .v = { | ||
300 | .id = V4L2_CID_AUDIO_VOLUME, | ||
301 | .name = "Volume", | ||
302 | .minimum = 0, | ||
303 | .maximum = 0x3f, | ||
304 | .step = 1, | ||
305 | .default_value = 0, | ||
306 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
307 | }, | ||
308 | .reg = AUD_VOL_CTL, | ||
309 | .sreg = SHADOW_AUD_VOL_CTL, | ||
310 | .mask = 0x3f, | ||
311 | .shift = 0, | ||
312 | },{ | ||
313 | .v = { | ||
314 | .id = V4L2_CID_AUDIO_BALANCE, | ||
315 | .name = "Balance", | ||
316 | .minimum = 0, | ||
317 | .maximum = 0x7f, | ||
318 | .step = 1, | ||
319 | .default_value = 0x40, | ||
320 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
321 | }, | ||
322 | .reg = AUD_BAL_CTL, | ||
323 | .sreg = SHADOW_AUD_BAL_CTL, | ||
324 | .mask = 0x7f, | ||
325 | .shift = 0, | ||
326 | } | ||
327 | }; | ||
328 | const int CX8800_CTLS = ARRAY_SIZE(cx8800_ctls); | ||
329 | |||
330 | /* ------------------------------------------------------------------- */ | ||
331 | /* resource management */ | ||
332 | |||
333 | static int res_get(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bit) | ||
334 | { | ||
335 | if (fh->resources & bit) | ||
336 | /* have it already allocated */ | ||
337 | return 1; | ||
338 | |||
339 | /* is it free? */ | ||
340 | down(&dev->lock); | ||
341 | if (dev->resources & bit) { | ||
342 | /* no, someone else uses it */ | ||
343 | up(&dev->lock); | ||
344 | return 0; | ||
345 | } | ||
346 | /* it's free, grab it */ | ||
347 | fh->resources |= bit; | ||
348 | dev->resources |= bit; | ||
349 | dprintk(1,"res: get %d\n",bit); | ||
350 | up(&dev->lock); | ||
351 | return 1; | ||
352 | } | ||
353 | |||
354 | static | ||
355 | int res_check(struct cx8800_fh *fh, unsigned int bit) | ||
356 | { | ||
357 | return (fh->resources & bit); | ||
358 | } | ||
359 | |||
360 | static | ||
361 | int res_locked(struct cx8800_dev *dev, unsigned int bit) | ||
362 | { | ||
363 | return (dev->resources & bit); | ||
364 | } | ||
365 | |||
366 | static | ||
367 | void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits) | ||
368 | { | ||
369 | if ((fh->resources & bits) != bits) | ||
370 | BUG(); | ||
371 | |||
372 | down(&dev->lock); | ||
373 | fh->resources &= ~bits; | ||
374 | dev->resources &= ~bits; | ||
375 | dprintk(1,"res: put %d\n",bits); | ||
376 | up(&dev->lock); | ||
377 | } | ||
378 | |||
379 | /* ------------------------------------------------------------------ */ | ||
380 | |||
381 | static int video_mux(struct cx8800_dev *dev, unsigned int input) | ||
382 | { | ||
383 | struct cx88_core *core = dev->core; | ||
384 | |||
385 | dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n", | ||
386 | input, INPUT(input)->vmux, | ||
387 | INPUT(input)->gpio0,INPUT(input)->gpio1, | ||
388 | INPUT(input)->gpio2,INPUT(input)->gpio3); | ||
389 | dev->core->input = input; | ||
390 | cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input)->vmux << 14); | ||
391 | cx_write(MO_GP3_IO, INPUT(input)->gpio3); | ||
392 | cx_write(MO_GP0_IO, INPUT(input)->gpio0); | ||
393 | cx_write(MO_GP1_IO, INPUT(input)->gpio1); | ||
394 | cx_write(MO_GP2_IO, INPUT(input)->gpio2); | ||
395 | |||
396 | switch (INPUT(input)->type) { | ||
397 | case CX88_VMUX_SVIDEO: | ||
398 | cx_set(MO_AFECFG_IO, 0x00000001); | ||
399 | cx_set(MO_INPUT_FORMAT, 0x00010010); | ||
400 | cx_set(MO_FILTER_EVEN, 0x00002020); | ||
401 | cx_set(MO_FILTER_ODD, 0x00002020); | ||
402 | break; | ||
403 | default: | ||
404 | cx_clear(MO_AFECFG_IO, 0x00000001); | ||
405 | cx_clear(MO_INPUT_FORMAT, 0x00010010); | ||
406 | cx_clear(MO_FILTER_EVEN, 0x00002020); | ||
407 | cx_clear(MO_FILTER_ODD, 0x00002020); | ||
408 | break; | ||
409 | } | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | /* ------------------------------------------------------------------ */ | ||
414 | |||
415 | static int start_video_dma(struct cx8800_dev *dev, | ||
416 | struct cx88_dmaqueue *q, | ||
417 | struct cx88_buffer *buf) | ||
418 | { | ||
419 | struct cx88_core *core = dev->core; | ||
420 | |||
421 | /* setup fifo + format */ | ||
422 | cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH21], | ||
423 | buf->bpl, buf->risc.dma); | ||
424 | cx88_set_scale(dev->core, buf->vb.width, buf->vb.height, buf->vb.field); | ||
425 | cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma); | ||
426 | |||
427 | /* reset counter */ | ||
428 | cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET); | ||
429 | q->count = 1; | ||
430 | |||
431 | /* enable irqs */ | ||
432 | cx_set(MO_PCI_INTMSK, core->pci_irqmask | 0x01); | ||
433 | cx_set(MO_VID_INTMSK, 0x0f0011); | ||
434 | |||
435 | /* enable capture */ | ||
436 | cx_set(VID_CAPTURE_CONTROL,0x06); | ||
437 | |||
438 | /* start dma */ | ||
439 | cx_set(MO_DEV_CNTRL2, (1<<5)); | ||
440 | cx_set(MO_VID_DMACNTRL, 0x11); | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | static int stop_video_dma(struct cx8800_dev *dev) | ||
446 | { | ||
447 | struct cx88_core *core = dev->core; | ||
448 | |||
449 | /* stop dma */ | ||
450 | cx_clear(MO_VID_DMACNTRL, 0x11); | ||
451 | |||
452 | /* disable capture */ | ||
453 | cx_clear(VID_CAPTURE_CONTROL,0x06); | ||
454 | |||
455 | /* disable irqs */ | ||
456 | cx_clear(MO_PCI_INTMSK, 0x000001); | ||
457 | cx_clear(MO_VID_INTMSK, 0x0f0011); | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static int restart_video_queue(struct cx8800_dev *dev, | ||
462 | struct cx88_dmaqueue *q) | ||
463 | { | ||
464 | struct cx88_buffer *buf, *prev; | ||
465 | struct list_head *item; | ||
466 | |||
467 | if (!list_empty(&q->active)) { | ||
468 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
469 | dprintk(2,"restart_queue [%p/%d]: restart dma\n", | ||
470 | buf, buf->vb.i); | ||
471 | start_video_dma(dev, q, buf); | ||
472 | list_for_each(item,&q->active) { | ||
473 | buf = list_entry(item, struct cx88_buffer, vb.queue); | ||
474 | buf->count = q->count++; | ||
475 | } | ||
476 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | prev = NULL; | ||
481 | for (;;) { | ||
482 | if (list_empty(&q->queued)) | ||
483 | return 0; | ||
484 | buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue); | ||
485 | if (NULL == prev) { | ||
486 | list_del(&buf->vb.queue); | ||
487 | list_add_tail(&buf->vb.queue,&q->active); | ||
488 | start_video_dma(dev, q, buf); | ||
489 | buf->vb.state = STATE_ACTIVE; | ||
490 | buf->count = q->count++; | ||
491 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
492 | dprintk(2,"[%p/%d] restart_queue - first active\n", | ||
493 | buf,buf->vb.i); | ||
494 | |||
495 | } else if (prev->vb.width == buf->vb.width && | ||
496 | prev->vb.height == buf->vb.height && | ||
497 | prev->fmt == buf->fmt) { | ||
498 | list_del(&buf->vb.queue); | ||
499 | list_add_tail(&buf->vb.queue,&q->active); | ||
500 | buf->vb.state = STATE_ACTIVE; | ||
501 | buf->count = q->count++; | ||
502 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
503 | dprintk(2,"[%p/%d] restart_queue - move to active\n", | ||
504 | buf,buf->vb.i); | ||
505 | } else { | ||
506 | return 0; | ||
507 | } | ||
508 | prev = buf; | ||
509 | } | ||
510 | } | ||
511 | |||
512 | /* ------------------------------------------------------------------ */ | ||
513 | |||
514 | static int | ||
515 | buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) | ||
516 | { | ||
517 | struct cx8800_fh *fh = q->priv_data; | ||
518 | |||
519 | *size = fh->fmt->depth*fh->width*fh->height >> 3; | ||
520 | if (0 == *count) | ||
521 | *count = 32; | ||
522 | while (*size * *count > vid_limit * 1024 * 1024) | ||
523 | (*count)--; | ||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | static int | ||
528 | buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, | ||
529 | enum v4l2_field field) | ||
530 | { | ||
531 | struct cx8800_fh *fh = q->priv_data; | ||
532 | struct cx8800_dev *dev = fh->dev; | ||
533 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
534 | int rc, init_buffer = 0; | ||
535 | |||
536 | BUG_ON(NULL == fh->fmt); | ||
537 | if (fh->width < 48 || fh->width > norm_maxw(dev->core->tvnorm) || | ||
538 | fh->height < 32 || fh->height > norm_maxh(dev->core->tvnorm)) | ||
539 | return -EINVAL; | ||
540 | buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; | ||
541 | if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) | ||
542 | return -EINVAL; | ||
543 | |||
544 | if (buf->fmt != fh->fmt || | ||
545 | buf->vb.width != fh->width || | ||
546 | buf->vb.height != fh->height || | ||
547 | buf->vb.field != field) { | ||
548 | buf->fmt = fh->fmt; | ||
549 | buf->vb.width = fh->width; | ||
550 | buf->vb.height = fh->height; | ||
551 | buf->vb.field = field; | ||
552 | init_buffer = 1; | ||
553 | } | ||
554 | |||
555 | if (STATE_NEEDS_INIT == buf->vb.state) { | ||
556 | init_buffer = 1; | ||
557 | if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL))) | ||
558 | goto fail; | ||
559 | } | ||
560 | |||
561 | if (init_buffer) { | ||
562 | buf->bpl = buf->vb.width * buf->fmt->depth >> 3; | ||
563 | switch (buf->vb.field) { | ||
564 | case V4L2_FIELD_TOP: | ||
565 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
566 | buf->vb.dma.sglist, 0, UNSET, | ||
567 | buf->bpl, 0, buf->vb.height); | ||
568 | break; | ||
569 | case V4L2_FIELD_BOTTOM: | ||
570 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
571 | buf->vb.dma.sglist, UNSET, 0, | ||
572 | buf->bpl, 0, buf->vb.height); | ||
573 | break; | ||
574 | case V4L2_FIELD_INTERLACED: | ||
575 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
576 | buf->vb.dma.sglist, 0, buf->bpl, | ||
577 | buf->bpl, buf->bpl, | ||
578 | buf->vb.height >> 1); | ||
579 | break; | ||
580 | case V4L2_FIELD_SEQ_TB: | ||
581 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
582 | buf->vb.dma.sglist, | ||
583 | 0, buf->bpl * (buf->vb.height >> 1), | ||
584 | buf->bpl, 0, | ||
585 | buf->vb.height >> 1); | ||
586 | break; | ||
587 | case V4L2_FIELD_SEQ_BT: | ||
588 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
589 | buf->vb.dma.sglist, | ||
590 | buf->bpl * (buf->vb.height >> 1), 0, | ||
591 | buf->bpl, 0, | ||
592 | buf->vb.height >> 1); | ||
593 | break; | ||
594 | default: | ||
595 | BUG(); | ||
596 | } | ||
597 | } | ||
598 | dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", | ||
599 | buf, buf->vb.i, | ||
600 | fh->width, fh->height, fh->fmt->depth, fh->fmt->name, | ||
601 | (unsigned long)buf->risc.dma); | ||
602 | |||
603 | buf->vb.state = STATE_PREPARED; | ||
604 | return 0; | ||
605 | |||
606 | fail: | ||
607 | cx88_free_buffer(dev->pci,buf); | ||
608 | return rc; | ||
609 | } | ||
610 | |||
611 | static void | ||
612 | buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) | ||
613 | { | ||
614 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
615 | struct cx88_buffer *prev; | ||
616 | struct cx8800_fh *fh = vq->priv_data; | ||
617 | struct cx8800_dev *dev = fh->dev; | ||
618 | struct cx88_dmaqueue *q = &dev->vidq; | ||
619 | |||
620 | /* add jump to stopper */ | ||
621 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); | ||
622 | buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); | ||
623 | |||
624 | if (!list_empty(&q->queued)) { | ||
625 | list_add_tail(&buf->vb.queue,&q->queued); | ||
626 | buf->vb.state = STATE_QUEUED; | ||
627 | dprintk(2,"[%p/%d] buffer_queue - append to queued\n", | ||
628 | buf, buf->vb.i); | ||
629 | |||
630 | } else if (list_empty(&q->active)) { | ||
631 | list_add_tail(&buf->vb.queue,&q->active); | ||
632 | start_video_dma(dev, q, buf); | ||
633 | buf->vb.state = STATE_ACTIVE; | ||
634 | buf->count = q->count++; | ||
635 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
636 | dprintk(2,"[%p/%d] buffer_queue - first active\n", | ||
637 | buf, buf->vb.i); | ||
638 | |||
639 | } else { | ||
640 | prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); | ||
641 | if (prev->vb.width == buf->vb.width && | ||
642 | prev->vb.height == buf->vb.height && | ||
643 | prev->fmt == buf->fmt) { | ||
644 | list_add_tail(&buf->vb.queue,&q->active); | ||
645 | buf->vb.state = STATE_ACTIVE; | ||
646 | buf->count = q->count++; | ||
647 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
648 | dprintk(2,"[%p/%d] buffer_queue - append to active\n", | ||
649 | buf, buf->vb.i); | ||
650 | |||
651 | } else { | ||
652 | list_add_tail(&buf->vb.queue,&q->queued); | ||
653 | buf->vb.state = STATE_QUEUED; | ||
654 | dprintk(2,"[%p/%d] buffer_queue - first queued\n", | ||
655 | buf, buf->vb.i); | ||
656 | } | ||
657 | } | ||
658 | } | ||
659 | |||
660 | static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
661 | { | ||
662 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
663 | struct cx8800_fh *fh = q->priv_data; | ||
664 | |||
665 | cx88_free_buffer(fh->dev->pci,buf); | ||
666 | } | ||
667 | |||
668 | struct videobuf_queue_ops cx8800_video_qops = { | ||
669 | .buf_setup = buffer_setup, | ||
670 | .buf_prepare = buffer_prepare, | ||
671 | .buf_queue = buffer_queue, | ||
672 | .buf_release = buffer_release, | ||
673 | }; | ||
674 | |||
675 | /* ------------------------------------------------------------------ */ | ||
676 | |||
677 | #if 0 /* overlay support not finished yet */ | ||
678 | static u32* ov_risc_field(struct cx8800_dev *dev, struct cx8800_fh *fh, | ||
679 | u32 *rp, struct btcx_skiplist *skips, | ||
680 | u32 sync_line, int skip_even, int skip_odd) | ||
681 | { | ||
682 | int line,maxy,start,end,skip,nskips; | ||
683 | u32 ri,ra; | ||
684 | u32 addr; | ||
685 | |||
686 | /* sync instruction */ | ||
687 | *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); | ||
688 | |||
689 | addr = (unsigned long)dev->fbuf.base; | ||
690 | addr += dev->fbuf.fmt.bytesperline * fh->win.w.top; | ||
691 | addr += (fh->fmt->depth >> 3) * fh->win.w.left; | ||
692 | |||
693 | /* scan lines */ | ||
694 | for (maxy = -1, line = 0; line < fh->win.w.height; | ||
695 | line++, addr += dev->fbuf.fmt.bytesperline) { | ||
696 | if ((line%2) == 0 && skip_even) | ||
697 | continue; | ||
698 | if ((line%2) == 1 && skip_odd) | ||
699 | continue; | ||
700 | |||
701 | /* calculate clipping */ | ||
702 | if (line > maxy) | ||
703 | btcx_calc_skips(line, fh->win.w.width, &maxy, | ||
704 | skips, &nskips, fh->clips, fh->nclips); | ||
705 | |||
706 | /* write out risc code */ | ||
707 | for (start = 0, skip = 0; start < fh->win.w.width; start = end) { | ||
708 | if (skip >= nskips) { | ||
709 | ri = RISC_WRITE; | ||
710 | end = fh->win.w.width; | ||
711 | } else if (start < skips[skip].start) { | ||
712 | ri = RISC_WRITE; | ||
713 | end = skips[skip].start; | ||
714 | } else { | ||
715 | ri = RISC_SKIP; | ||
716 | end = skips[skip].end; | ||
717 | skip++; | ||
718 | } | ||
719 | if (RISC_WRITE == ri) | ||
720 | ra = addr + (fh->fmt->depth>>3)*start; | ||
721 | else | ||
722 | ra = 0; | ||
723 | |||
724 | if (0 == start) | ||
725 | ri |= RISC_SOL; | ||
726 | if (fh->win.w.width == end) | ||
727 | ri |= RISC_EOL; | ||
728 | ri |= (fh->fmt->depth>>3) * (end-start); | ||
729 | |||
730 | *(rp++)=cpu_to_le32(ri); | ||
731 | if (0 != ra) | ||
732 | *(rp++)=cpu_to_le32(ra); | ||
733 | } | ||
734 | } | ||
735 | kfree(skips); | ||
736 | return rp; | ||
737 | } | ||
738 | |||
739 | static int ov_risc_frame(struct cx8800_dev *dev, struct cx8800_fh *fh, | ||
740 | struct cx88_buffer *buf) | ||
741 | { | ||
742 | struct btcx_skiplist *skips; | ||
743 | u32 instructions,fields; | ||
744 | u32 *rp; | ||
745 | int rc; | ||
746 | |||
747 | /* skip list for window clipping */ | ||
748 | if (NULL == (skips = kmalloc(sizeof(*skips) * fh->nclips,GFP_KERNEL))) | ||
749 | return -ENOMEM; | ||
750 | |||
751 | fields = 0; | ||
752 | if (V4L2_FIELD_HAS_TOP(fh->win.field)) | ||
753 | fields++; | ||
754 | if (V4L2_FIELD_HAS_BOTTOM(fh->win.field)) | ||
755 | fields++; | ||
756 | |||
757 | /* estimate risc mem: worst case is (clip+1) * lines instructions | ||
758 | + syncs + jump (all 2 dwords) */ | ||
759 | instructions = (fh->nclips+1) * fh->win.w.height; | ||
760 | instructions += 3 + 4; | ||
761 | if ((rc = btcx_riscmem_alloc(dev->pci,&buf->risc,instructions*8)) < 0) { | ||
762 | kfree(skips); | ||
763 | return rc; | ||
764 | } | ||
765 | |||
766 | /* write risc instructions */ | ||
767 | rp = buf->risc.cpu; | ||
768 | switch (fh->win.field) { | ||
769 | case V4L2_FIELD_TOP: | ||
770 | rp = ov_risc_field(dev, fh, rp, skips, 0, 0, 0); | ||
771 | break; | ||
772 | case V4L2_FIELD_BOTTOM: | ||
773 | rp = ov_risc_field(dev, fh, rp, skips, 0x200, 0, 0); | ||
774 | break; | ||
775 | case V4L2_FIELD_INTERLACED: | ||
776 | rp = ov_risc_field(dev, fh, rp, skips, 0, 0, 1); | ||
777 | rp = ov_risc_field(dev, fh, rp, skips, 0x200, 1, 0); | ||
778 | break; | ||
779 | default: | ||
780 | BUG(); | ||
781 | } | ||
782 | |||
783 | /* save pointer to jmp instruction address */ | ||
784 | buf->risc.jmp = rp; | ||
785 | kfree(skips); | ||
786 | return 0; | ||
787 | } | ||
788 | |||
789 | static int verify_window(struct cx8800_dev *dev, struct v4l2_window *win) | ||
790 | { | ||
791 | enum v4l2_field field; | ||
792 | int maxw, maxh; | ||
793 | |||
794 | if (NULL == dev->fbuf.base) | ||
795 | return -EINVAL; | ||
796 | if (win->w.width < 48 || win->w.height < 32) | ||
797 | return -EINVAL; | ||
798 | if (win->clipcount > 2048) | ||
799 | return -EINVAL; | ||
800 | |||
801 | field = win->field; | ||
802 | maxw = norm_maxw(core->tvnorm); | ||
803 | maxh = norm_maxh(core->tvnorm); | ||
804 | |||
805 | if (V4L2_FIELD_ANY == field) { | ||
806 | field = (win->w.height > maxh/2) | ||
807 | ? V4L2_FIELD_INTERLACED | ||
808 | : V4L2_FIELD_TOP; | ||
809 | } | ||
810 | switch (field) { | ||
811 | case V4L2_FIELD_TOP: | ||
812 | case V4L2_FIELD_BOTTOM: | ||
813 | maxh = maxh / 2; | ||
814 | break; | ||
815 | case V4L2_FIELD_INTERLACED: | ||
816 | break; | ||
817 | default: | ||
818 | return -EINVAL; | ||
819 | } | ||
820 | |||
821 | win->field = field; | ||
822 | if (win->w.width > maxw) | ||
823 | win->w.width = maxw; | ||
824 | if (win->w.height > maxh) | ||
825 | win->w.height = maxh; | ||
826 | return 0; | ||
827 | } | ||
828 | |||
829 | static int setup_window(struct cx8800_dev *dev, struct cx8800_fh *fh, | ||
830 | struct v4l2_window *win) | ||
831 | { | ||
832 | struct v4l2_clip *clips = NULL; | ||
833 | int n,size,retval = 0; | ||
834 | |||
835 | if (NULL == fh->fmt) | ||
836 | return -EINVAL; | ||
837 | retval = verify_window(dev,win); | ||
838 | if (0 != retval) | ||
839 | return retval; | ||
840 | |||
841 | /* copy clips -- luckily v4l1 + v4l2 are binary | ||
842 | compatible here ...*/ | ||
843 | n = win->clipcount; | ||
844 | size = sizeof(*clips)*(n+4); | ||
845 | clips = kmalloc(size,GFP_KERNEL); | ||
846 | if (NULL == clips) | ||
847 | return -ENOMEM; | ||
848 | if (n > 0) { | ||
849 | if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) { | ||
850 | kfree(clips); | ||
851 | return -EFAULT; | ||
852 | } | ||
853 | } | ||
854 | |||
855 | /* clip against screen */ | ||
856 | if (NULL != dev->fbuf.base) | ||
857 | n = btcx_screen_clips(dev->fbuf.fmt.width, dev->fbuf.fmt.height, | ||
858 | &win->w, clips, n); | ||
859 | btcx_sort_clips(clips,n); | ||
860 | |||
861 | /* 4-byte alignments */ | ||
862 | switch (fh->fmt->depth) { | ||
863 | case 8: | ||
864 | case 24: | ||
865 | btcx_align(&win->w, clips, n, 3); | ||
866 | break; | ||
867 | case 16: | ||
868 | btcx_align(&win->w, clips, n, 1); | ||
869 | break; | ||
870 | case 32: | ||
871 | /* no alignment fixups needed */ | ||
872 | break; | ||
873 | default: | ||
874 | BUG(); | ||
875 | } | ||
876 | |||
877 | down(&fh->vidq.lock); | ||
878 | if (fh->clips) | ||
879 | kfree(fh->clips); | ||
880 | fh->clips = clips; | ||
881 | fh->nclips = n; | ||
882 | fh->win = *win; | ||
883 | #if 0 | ||
884 | fh->ov.setup_ok = 1; | ||
885 | #endif | ||
886 | |||
887 | /* update overlay if needed */ | ||
888 | retval = 0; | ||
889 | #if 0 | ||
890 | if (check_btres(fh, RESOURCE_OVERLAY)) { | ||
891 | struct bttv_buffer *new; | ||
892 | |||
893 | new = videobuf_alloc(sizeof(*new)); | ||
894 | bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); | ||
895 | retval = bttv_switch_overlay(btv,fh,new); | ||
896 | } | ||
897 | #endif | ||
898 | up(&fh->vidq.lock); | ||
899 | return retval; | ||
900 | } | ||
901 | #endif | ||
902 | |||
903 | /* ------------------------------------------------------------------ */ | ||
904 | |||
905 | static struct videobuf_queue* get_queue(struct cx8800_fh *fh) | ||
906 | { | ||
907 | switch (fh->type) { | ||
908 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
909 | return &fh->vidq; | ||
910 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
911 | return &fh->vbiq; | ||
912 | default: | ||
913 | BUG(); | ||
914 | return NULL; | ||
915 | } | ||
916 | } | ||
917 | |||
918 | static int get_ressource(struct cx8800_fh *fh) | ||
919 | { | ||
920 | switch (fh->type) { | ||
921 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
922 | return RESOURCE_VIDEO; | ||
923 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
924 | return RESOURCE_VBI; | ||
925 | default: | ||
926 | BUG(); | ||
927 | return 0; | ||
928 | } | ||
929 | } | ||
930 | |||
931 | static int video_open(struct inode *inode, struct file *file) | ||
932 | { | ||
933 | int minor = iminor(inode); | ||
934 | struct cx8800_dev *h,*dev = NULL; | ||
935 | struct cx8800_fh *fh; | ||
936 | struct list_head *list; | ||
937 | enum v4l2_buf_type type = 0; | ||
938 | int radio = 0; | ||
939 | |||
940 | list_for_each(list,&cx8800_devlist) { | ||
941 | h = list_entry(list, struct cx8800_dev, devlist); | ||
942 | if (h->video_dev->minor == minor) { | ||
943 | dev = h; | ||
944 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
945 | } | ||
946 | if (h->vbi_dev->minor == minor) { | ||
947 | dev = h; | ||
948 | type = V4L2_BUF_TYPE_VBI_CAPTURE; | ||
949 | } | ||
950 | if (h->radio_dev && | ||
951 | h->radio_dev->minor == minor) { | ||
952 | radio = 1; | ||
953 | dev = h; | ||
954 | } | ||
955 | } | ||
956 | if (NULL == dev) | ||
957 | return -ENODEV; | ||
958 | |||
959 | dprintk(1,"open minor=%d radio=%d type=%s\n", | ||
960 | minor,radio,v4l2_type_names[type]); | ||
961 | |||
962 | /* allocate + initialize per filehandle data */ | ||
963 | fh = kmalloc(sizeof(*fh),GFP_KERNEL); | ||
964 | if (NULL == fh) | ||
965 | return -ENOMEM; | ||
966 | memset(fh,0,sizeof(*fh)); | ||
967 | file->private_data = fh; | ||
968 | fh->dev = dev; | ||
969 | fh->radio = radio; | ||
970 | fh->type = type; | ||
971 | fh->width = 320; | ||
972 | fh->height = 240; | ||
973 | fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); | ||
974 | |||
975 | videobuf_queue_init(&fh->vidq, &cx8800_video_qops, | ||
976 | dev->pci, &dev->slock, | ||
977 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
978 | V4L2_FIELD_INTERLACED, | ||
979 | sizeof(struct cx88_buffer), | ||
980 | fh); | ||
981 | videobuf_queue_init(&fh->vbiq, &cx8800_vbi_qops, | ||
982 | dev->pci, &dev->slock, | ||
983 | V4L2_BUF_TYPE_VBI_CAPTURE, | ||
984 | V4L2_FIELD_SEQ_TB, | ||
985 | sizeof(struct cx88_buffer), | ||
986 | fh); | ||
987 | |||
988 | if (fh->radio) { | ||
989 | struct cx88_core *core = dev->core; | ||
990 | int board = core->board; | ||
991 | dprintk(1,"video_open: setting radio device\n"); | ||
992 | cx_write(MO_GP0_IO, cx88_boards[board].radio.gpio0); | ||
993 | cx_write(MO_GP1_IO, cx88_boards[board].radio.gpio1); | ||
994 | cx_write(MO_GP2_IO, cx88_boards[board].radio.gpio2); | ||
995 | cx_write(MO_GP3_IO, cx88_boards[board].radio.gpio3); | ||
996 | dev->core->tvaudio = WW_FM; | ||
997 | cx88_set_tvaudio(core); | ||
998 | cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1); | ||
999 | cx88_call_i2c_clients(dev->core,AUDC_SET_RADIO,NULL); | ||
1000 | } | ||
1001 | |||
1002 | return 0; | ||
1003 | } | ||
1004 | |||
1005 | static ssize_t | ||
1006 | video_read(struct file *file, char *data, size_t count, loff_t *ppos) | ||
1007 | { | ||
1008 | struct cx8800_fh *fh = file->private_data; | ||
1009 | |||
1010 | switch (fh->type) { | ||
1011 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
1012 | if (res_locked(fh->dev,RESOURCE_VIDEO)) | ||
1013 | return -EBUSY; | ||
1014 | return videobuf_read_one(&fh->vidq, data, count, ppos, | ||
1015 | file->f_flags & O_NONBLOCK); | ||
1016 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
1017 | if (!res_get(fh->dev,fh,RESOURCE_VBI)) | ||
1018 | return -EBUSY; | ||
1019 | return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1, | ||
1020 | file->f_flags & O_NONBLOCK); | ||
1021 | default: | ||
1022 | BUG(); | ||
1023 | return 0; | ||
1024 | } | ||
1025 | } | ||
1026 | |||
1027 | static unsigned int | ||
1028 | video_poll(struct file *file, struct poll_table_struct *wait) | ||
1029 | { | ||
1030 | struct cx8800_fh *fh = file->private_data; | ||
1031 | struct cx88_buffer *buf; | ||
1032 | |||
1033 | if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { | ||
1034 | if (!res_get(fh->dev,fh,RESOURCE_VBI)) | ||
1035 | return POLLERR; | ||
1036 | return videobuf_poll_stream(file, &fh->vbiq, wait); | ||
1037 | } | ||
1038 | |||
1039 | if (res_check(fh,RESOURCE_VIDEO)) { | ||
1040 | /* streaming capture */ | ||
1041 | if (list_empty(&fh->vidq.stream)) | ||
1042 | return POLLERR; | ||
1043 | buf = list_entry(fh->vidq.stream.next,struct cx88_buffer,vb.stream); | ||
1044 | } else { | ||
1045 | /* read() capture */ | ||
1046 | buf = (struct cx88_buffer*)fh->vidq.read_buf; | ||
1047 | if (NULL == buf) | ||
1048 | return POLLERR; | ||
1049 | } | ||
1050 | poll_wait(file, &buf->vb.done, wait); | ||
1051 | if (buf->vb.state == STATE_DONE || | ||
1052 | buf->vb.state == STATE_ERROR) | ||
1053 | return POLLIN|POLLRDNORM; | ||
1054 | return 0; | ||
1055 | } | ||
1056 | |||
1057 | static int video_release(struct inode *inode, struct file *file) | ||
1058 | { | ||
1059 | struct cx8800_fh *fh = file->private_data; | ||
1060 | struct cx8800_dev *dev = fh->dev; | ||
1061 | |||
1062 | /* turn off overlay */ | ||
1063 | if (res_check(fh, RESOURCE_OVERLAY)) { | ||
1064 | /* FIXME */ | ||
1065 | res_free(dev,fh,RESOURCE_OVERLAY); | ||
1066 | } | ||
1067 | |||
1068 | /* stop video capture */ | ||
1069 | if (res_check(fh, RESOURCE_VIDEO)) { | ||
1070 | videobuf_queue_cancel(&fh->vidq); | ||
1071 | res_free(dev,fh,RESOURCE_VIDEO); | ||
1072 | } | ||
1073 | if (fh->vidq.read_buf) { | ||
1074 | buffer_release(&fh->vidq,fh->vidq.read_buf); | ||
1075 | kfree(fh->vidq.read_buf); | ||
1076 | } | ||
1077 | |||
1078 | /* stop vbi capture */ | ||
1079 | if (res_check(fh, RESOURCE_VBI)) { | ||
1080 | if (fh->vbiq.streaming) | ||
1081 | videobuf_streamoff(&fh->vbiq); | ||
1082 | if (fh->vbiq.reading) | ||
1083 | videobuf_read_stop(&fh->vbiq); | ||
1084 | res_free(dev,fh,RESOURCE_VBI); | ||
1085 | } | ||
1086 | |||
1087 | videobuf_mmap_free(&fh->vidq); | ||
1088 | videobuf_mmap_free(&fh->vbiq); | ||
1089 | file->private_data = NULL; | ||
1090 | kfree(fh); | ||
1091 | return 0; | ||
1092 | } | ||
1093 | |||
1094 | static int | ||
1095 | video_mmap(struct file *file, struct vm_area_struct * vma) | ||
1096 | { | ||
1097 | struct cx8800_fh *fh = file->private_data; | ||
1098 | |||
1099 | return videobuf_mmap_mapper(get_queue(fh), vma); | ||
1100 | } | ||
1101 | |||
1102 | /* ------------------------------------------------------------------ */ | ||
1103 | |||
1104 | static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl) | ||
1105 | { | ||
1106 | struct cx88_core *core = dev->core; | ||
1107 | struct cx88_ctrl *c = NULL; | ||
1108 | u32 value; | ||
1109 | int i; | ||
1110 | |||
1111 | for (i = 0; i < CX8800_CTLS; i++) | ||
1112 | if (cx8800_ctls[i].v.id == ctl->id) | ||
1113 | c = &cx8800_ctls[i]; | ||
1114 | if (NULL == c) | ||
1115 | return -EINVAL; | ||
1116 | |||
1117 | value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg); | ||
1118 | switch (ctl->id) { | ||
1119 | case V4L2_CID_AUDIO_BALANCE: | ||
1120 | ctl->value = (value & 0x40) ? (value & 0x3f) : (0x40 - (value & 0x3f)); | ||
1121 | break; | ||
1122 | case V4L2_CID_AUDIO_VOLUME: | ||
1123 | ctl->value = 0x3f - (value & 0x3f); | ||
1124 | break; | ||
1125 | default: | ||
1126 | ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift; | ||
1127 | break; | ||
1128 | } | ||
1129 | return 0; | ||
1130 | } | ||
1131 | |||
1132 | static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl) | ||
1133 | { | ||
1134 | struct cx88_core *core = dev->core; | ||
1135 | struct cx88_ctrl *c = NULL; | ||
1136 | u32 v_sat_value; | ||
1137 | u32 value; | ||
1138 | int i; | ||
1139 | |||
1140 | for (i = 0; i < CX8800_CTLS; i++) | ||
1141 | if (cx8800_ctls[i].v.id == ctl->id) | ||
1142 | c = &cx8800_ctls[i]; | ||
1143 | if (NULL == c) | ||
1144 | return -EINVAL; | ||
1145 | |||
1146 | if (ctl->value < c->v.minimum) | ||
1147 | return -ERANGE; | ||
1148 | if (ctl->value > c->v.maximum) | ||
1149 | return -ERANGE; | ||
1150 | switch (ctl->id) { | ||
1151 | case V4L2_CID_AUDIO_BALANCE: | ||
1152 | value = (ctl->value < 0x40) ? (0x40 - ctl->value) : ctl->value; | ||
1153 | break; | ||
1154 | case V4L2_CID_AUDIO_VOLUME: | ||
1155 | value = 0x3f - (ctl->value & 0x3f); | ||
1156 | break; | ||
1157 | case V4L2_CID_SATURATION: | ||
1158 | /* special v_sat handling */ | ||
1159 | v_sat_value = ctl->value - (0x7f - 0x5a); | ||
1160 | if (v_sat_value > 0xff) | ||
1161 | v_sat_value = 0xff; | ||
1162 | if (v_sat_value < 0x00) | ||
1163 | v_sat_value = 0x00; | ||
1164 | cx_andor(MO_UV_SATURATION, 0xff00, v_sat_value << 8); | ||
1165 | /* fall through to default route for u_sat */ | ||
1166 | default: | ||
1167 | value = ((ctl->value - c->off) << c->shift) & c->mask; | ||
1168 | break; | ||
1169 | } | ||
1170 | dprintk(1,"set_control id=0x%X reg=0x%x val=0x%x%s\n", | ||
1171 | ctl->id, c->reg, value, c->sreg ? " [shadowed]" : ""); | ||
1172 | if (c->sreg) { | ||
1173 | cx_sandor(c->sreg, c->reg, c->mask, value); | ||
1174 | } else { | ||
1175 | cx_andor(c->reg, c->mask, value); | ||
1176 | } | ||
1177 | return 0; | ||
1178 | } | ||
1179 | |||
1180 | static void init_controls(struct cx8800_dev *dev) | ||
1181 | { | ||
1182 | static struct v4l2_control mute = { | ||
1183 | .id = V4L2_CID_AUDIO_MUTE, | ||
1184 | .value = 1, | ||
1185 | }; | ||
1186 | static struct v4l2_control volume = { | ||
1187 | .id = V4L2_CID_AUDIO_VOLUME, | ||
1188 | .value = 0x3f, | ||
1189 | }; | ||
1190 | |||
1191 | set_control(dev,&mute); | ||
1192 | set_control(dev,&volume); | ||
1193 | } | ||
1194 | |||
1195 | /* ------------------------------------------------------------------ */ | ||
1196 | |||
1197 | static int cx8800_g_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, | ||
1198 | struct v4l2_format *f) | ||
1199 | { | ||
1200 | switch (f->type) { | ||
1201 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
1202 | memset(&f->fmt.pix,0,sizeof(f->fmt.pix)); | ||
1203 | f->fmt.pix.width = fh->width; | ||
1204 | f->fmt.pix.height = fh->height; | ||
1205 | f->fmt.pix.field = fh->vidq.field; | ||
1206 | f->fmt.pix.pixelformat = fh->fmt->fourcc; | ||
1207 | f->fmt.pix.bytesperline = | ||
1208 | (f->fmt.pix.width * fh->fmt->depth) >> 3; | ||
1209 | f->fmt.pix.sizeimage = | ||
1210 | f->fmt.pix.height * f->fmt.pix.bytesperline; | ||
1211 | return 0; | ||
1212 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
1213 | cx8800_vbi_fmt(dev, f); | ||
1214 | return 0; | ||
1215 | default: | ||
1216 | return -EINVAL; | ||
1217 | } | ||
1218 | } | ||
1219 | |||
1220 | static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, | ||
1221 | struct v4l2_format *f) | ||
1222 | { | ||
1223 | switch (f->type) { | ||
1224 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
1225 | { | ||
1226 | struct cx8800_fmt *fmt; | ||
1227 | enum v4l2_field field; | ||
1228 | unsigned int maxw, maxh; | ||
1229 | |||
1230 | fmt = format_by_fourcc(f->fmt.pix.pixelformat); | ||
1231 | if (NULL == fmt) | ||
1232 | return -EINVAL; | ||
1233 | |||
1234 | field = f->fmt.pix.field; | ||
1235 | maxw = norm_maxw(dev->core->tvnorm); | ||
1236 | maxh = norm_maxh(dev->core->tvnorm); | ||
1237 | |||
1238 | if (V4L2_FIELD_ANY == field) { | ||
1239 | field = (f->fmt.pix.height > maxh/2) | ||
1240 | ? V4L2_FIELD_INTERLACED | ||
1241 | : V4L2_FIELD_BOTTOM; | ||
1242 | } | ||
1243 | |||
1244 | switch (field) { | ||
1245 | case V4L2_FIELD_TOP: | ||
1246 | case V4L2_FIELD_BOTTOM: | ||
1247 | maxh = maxh / 2; | ||
1248 | break; | ||
1249 | case V4L2_FIELD_INTERLACED: | ||
1250 | break; | ||
1251 | default: | ||
1252 | return -EINVAL; | ||
1253 | } | ||
1254 | |||
1255 | f->fmt.pix.field = field; | ||
1256 | if (f->fmt.pix.height < 32) | ||
1257 | f->fmt.pix.height = 32; | ||
1258 | if (f->fmt.pix.height > maxh) | ||
1259 | f->fmt.pix.height = maxh; | ||
1260 | if (f->fmt.pix.width < 48) | ||
1261 | f->fmt.pix.width = 48; | ||
1262 | if (f->fmt.pix.width > maxw) | ||
1263 | f->fmt.pix.width = maxw; | ||
1264 | f->fmt.pix.width &= ~0x03; | ||
1265 | f->fmt.pix.bytesperline = | ||
1266 | (f->fmt.pix.width * fmt->depth) >> 3; | ||
1267 | f->fmt.pix.sizeimage = | ||
1268 | f->fmt.pix.height * f->fmt.pix.bytesperline; | ||
1269 | |||
1270 | return 0; | ||
1271 | } | ||
1272 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
1273 | cx8800_vbi_fmt(dev, f); | ||
1274 | return 0; | ||
1275 | default: | ||
1276 | return -EINVAL; | ||
1277 | } | ||
1278 | } | ||
1279 | |||
1280 | static int cx8800_s_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, | ||
1281 | struct v4l2_format *f) | ||
1282 | { | ||
1283 | int err; | ||
1284 | |||
1285 | switch (f->type) { | ||
1286 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
1287 | err = cx8800_try_fmt(dev,fh,f); | ||
1288 | if (0 != err) | ||
1289 | return err; | ||
1290 | |||
1291 | fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); | ||
1292 | fh->width = f->fmt.pix.width; | ||
1293 | fh->height = f->fmt.pix.height; | ||
1294 | fh->vidq.field = f->fmt.pix.field; | ||
1295 | return 0; | ||
1296 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
1297 | cx8800_vbi_fmt(dev, f); | ||
1298 | return 0; | ||
1299 | default: | ||
1300 | return -EINVAL; | ||
1301 | } | ||
1302 | } | ||
1303 | |||
1304 | /* | ||
1305 | * This function is _not_ called directly, but from | ||
1306 | * video_generic_ioctl (and maybe others). userspace | ||
1307 | * copying is done already, arg is a kernel pointer. | ||
1308 | */ | ||
1309 | static int video_do_ioctl(struct inode *inode, struct file *file, | ||
1310 | unsigned int cmd, void *arg) | ||
1311 | { | ||
1312 | struct cx8800_fh *fh = file->private_data; | ||
1313 | struct cx8800_dev *dev = fh->dev; | ||
1314 | struct cx88_core *core = dev->core; | ||
1315 | #if 0 | ||
1316 | unsigned long flags; | ||
1317 | #endif | ||
1318 | int err; | ||
1319 | |||
1320 | if (video_debug > 1) | ||
1321 | cx88_print_ioctl(core->name,cmd); | ||
1322 | switch (cmd) { | ||
1323 | case VIDIOC_QUERYCAP: | ||
1324 | { | ||
1325 | struct v4l2_capability *cap = arg; | ||
1326 | |||
1327 | memset(cap,0,sizeof(*cap)); | ||
1328 | strcpy(cap->driver, "cx8800"); | ||
1329 | strlcpy(cap->card, cx88_boards[core->board].name, | ||
1330 | sizeof(cap->card)); | ||
1331 | sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); | ||
1332 | cap->version = CX88_VERSION_CODE; | ||
1333 | cap->capabilities = | ||
1334 | V4L2_CAP_VIDEO_CAPTURE | | ||
1335 | V4L2_CAP_READWRITE | | ||
1336 | V4L2_CAP_STREAMING | | ||
1337 | V4L2_CAP_VBI_CAPTURE | | ||
1338 | #if 0 | ||
1339 | V4L2_CAP_VIDEO_OVERLAY | | ||
1340 | #endif | ||
1341 | 0; | ||
1342 | if (UNSET != core->tuner_type) | ||
1343 | cap->capabilities |= V4L2_CAP_TUNER; | ||
1344 | return 0; | ||
1345 | } | ||
1346 | |||
1347 | /* ---------- tv norms ---------- */ | ||
1348 | case VIDIOC_ENUMSTD: | ||
1349 | { | ||
1350 | struct v4l2_standard *e = arg; | ||
1351 | unsigned int i; | ||
1352 | |||
1353 | i = e->index; | ||
1354 | if (i >= ARRAY_SIZE(tvnorms)) | ||
1355 | return -EINVAL; | ||
1356 | err = v4l2_video_std_construct(e, tvnorms[e->index].id, | ||
1357 | tvnorms[e->index].name); | ||
1358 | e->index = i; | ||
1359 | if (err < 0) | ||
1360 | return err; | ||
1361 | return 0; | ||
1362 | } | ||
1363 | case VIDIOC_G_STD: | ||
1364 | { | ||
1365 | v4l2_std_id *id = arg; | ||
1366 | |||
1367 | *id = core->tvnorm->id; | ||
1368 | return 0; | ||
1369 | } | ||
1370 | case VIDIOC_S_STD: | ||
1371 | { | ||
1372 | v4l2_std_id *id = arg; | ||
1373 | unsigned int i; | ||
1374 | |||
1375 | for(i = 0; i < ARRAY_SIZE(tvnorms); i++) | ||
1376 | if (*id & tvnorms[i].id) | ||
1377 | break; | ||
1378 | if (i == ARRAY_SIZE(tvnorms)) | ||
1379 | return -EINVAL; | ||
1380 | |||
1381 | down(&dev->lock); | ||
1382 | cx88_set_tvnorm(dev->core,&tvnorms[i]); | ||
1383 | up(&dev->lock); | ||
1384 | return 0; | ||
1385 | } | ||
1386 | |||
1387 | /* ------ input switching ---------- */ | ||
1388 | case VIDIOC_ENUMINPUT: | ||
1389 | { | ||
1390 | static const char *iname[] = { | ||
1391 | [ CX88_VMUX_COMPOSITE1 ] = "Composite1", | ||
1392 | [ CX88_VMUX_COMPOSITE2 ] = "Composite2", | ||
1393 | [ CX88_VMUX_COMPOSITE3 ] = "Composite3", | ||
1394 | [ CX88_VMUX_COMPOSITE4 ] = "Composite4", | ||
1395 | [ CX88_VMUX_SVIDEO ] = "S-Video", | ||
1396 | [ CX88_VMUX_TELEVISION ] = "Television", | ||
1397 | [ CX88_VMUX_CABLE ] = "Cable TV", | ||
1398 | [ CX88_VMUX_DVB ] = "DVB", | ||
1399 | [ CX88_VMUX_DEBUG ] = "for debug only", | ||
1400 | }; | ||
1401 | struct v4l2_input *i = arg; | ||
1402 | unsigned int n; | ||
1403 | |||
1404 | n = i->index; | ||
1405 | if (n >= 4) | ||
1406 | return -EINVAL; | ||
1407 | if (0 == INPUT(n)->type) | ||
1408 | return -EINVAL; | ||
1409 | memset(i,0,sizeof(*i)); | ||
1410 | i->index = n; | ||
1411 | i->type = V4L2_INPUT_TYPE_CAMERA; | ||
1412 | strcpy(i->name,iname[INPUT(n)->type]); | ||
1413 | if ((CX88_VMUX_TELEVISION == INPUT(n)->type) || | ||
1414 | (CX88_VMUX_CABLE == INPUT(n)->type)) | ||
1415 | i->type = V4L2_INPUT_TYPE_TUNER; | ||
1416 | for (n = 0; n < ARRAY_SIZE(tvnorms); n++) | ||
1417 | i->std |= tvnorms[n].id; | ||
1418 | return 0; | ||
1419 | } | ||
1420 | case VIDIOC_G_INPUT: | ||
1421 | { | ||
1422 | unsigned int *i = arg; | ||
1423 | |||
1424 | *i = dev->core->input; | ||
1425 | return 0; | ||
1426 | } | ||
1427 | case VIDIOC_S_INPUT: | ||
1428 | { | ||
1429 | unsigned int *i = arg; | ||
1430 | |||
1431 | if (*i >= 4) | ||
1432 | return -EINVAL; | ||
1433 | down(&dev->lock); | ||
1434 | cx88_newstation(core); | ||
1435 | video_mux(dev,*i); | ||
1436 | up(&dev->lock); | ||
1437 | return 0; | ||
1438 | } | ||
1439 | |||
1440 | |||
1441 | #if 0 | ||
1442 | /* needs review */ | ||
1443 | case VIDIOC_G_AUDIO: | ||
1444 | { | ||
1445 | struct v4l2_audio *a = arg; | ||
1446 | unsigned int n = a->index; | ||
1447 | |||
1448 | memset(a,0,sizeof(*a)); | ||
1449 | a->index = n; | ||
1450 | switch (n) { | ||
1451 | case 0: | ||
1452 | if ((CX88_VMUX_TELEVISION == INPUT(n)->type) | ||
1453 | || (CX88_VMUX_CABLE == INPUT(n)->type)) { | ||
1454 | strcpy(a->name,"Television"); | ||
1455 | // FIXME figure out if stereo received and set V4L2_AUDCAP_STEREO. | ||
1456 | return 0; | ||
1457 | } | ||
1458 | break; | ||
1459 | case 1: | ||
1460 | if (CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD == core->board) { | ||
1461 | strcpy(a->name,"Line In"); | ||
1462 | a->capability = V4L2_AUDCAP_STEREO; | ||
1463 | return 0; | ||
1464 | } | ||
1465 | break; | ||
1466 | } | ||
1467 | // Audio input not available. | ||
1468 | return -EINVAL; | ||
1469 | } | ||
1470 | #endif | ||
1471 | |||
1472 | /* --- capture ioctls ---------------------------------------- */ | ||
1473 | case VIDIOC_ENUM_FMT: | ||
1474 | { | ||
1475 | struct v4l2_fmtdesc *f = arg; | ||
1476 | enum v4l2_buf_type type; | ||
1477 | unsigned int index; | ||
1478 | |||
1479 | index = f->index; | ||
1480 | type = f->type; | ||
1481 | switch (type) { | ||
1482 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
1483 | if (index >= ARRAY_SIZE(formats)) | ||
1484 | return -EINVAL; | ||
1485 | memset(f,0,sizeof(*f)); | ||
1486 | f->index = index; | ||
1487 | f->type = type; | ||
1488 | strlcpy(f->description,formats[index].name,sizeof(f->description)); | ||
1489 | f->pixelformat = formats[index].fourcc; | ||
1490 | break; | ||
1491 | default: | ||
1492 | return -EINVAL; | ||
1493 | } | ||
1494 | return 0; | ||
1495 | } | ||
1496 | case VIDIOC_G_FMT: | ||
1497 | { | ||
1498 | struct v4l2_format *f = arg; | ||
1499 | return cx8800_g_fmt(dev,fh,f); | ||
1500 | } | ||
1501 | case VIDIOC_S_FMT: | ||
1502 | { | ||
1503 | struct v4l2_format *f = arg; | ||
1504 | return cx8800_s_fmt(dev,fh,f); | ||
1505 | } | ||
1506 | case VIDIOC_TRY_FMT: | ||
1507 | { | ||
1508 | struct v4l2_format *f = arg; | ||
1509 | return cx8800_try_fmt(dev,fh,f); | ||
1510 | } | ||
1511 | |||
1512 | /* --- controls ---------------------------------------------- */ | ||
1513 | case VIDIOC_QUERYCTRL: | ||
1514 | { | ||
1515 | struct v4l2_queryctrl *c = arg; | ||
1516 | int i; | ||
1517 | |||
1518 | if (c->id < V4L2_CID_BASE || | ||
1519 | c->id >= V4L2_CID_LASTP1) | ||
1520 | return -EINVAL; | ||
1521 | for (i = 0; i < CX8800_CTLS; i++) | ||
1522 | if (cx8800_ctls[i].v.id == c->id) | ||
1523 | break; | ||
1524 | if (i == CX8800_CTLS) { | ||
1525 | *c = no_ctl; | ||
1526 | return 0; | ||
1527 | } | ||
1528 | *c = cx8800_ctls[i].v; | ||
1529 | return 0; | ||
1530 | } | ||
1531 | case VIDIOC_G_CTRL: | ||
1532 | return get_control(dev,arg); | ||
1533 | case VIDIOC_S_CTRL: | ||
1534 | return set_control(dev,arg); | ||
1535 | |||
1536 | /* --- tuner ioctls ------------------------------------------ */ | ||
1537 | case VIDIOC_G_TUNER: | ||
1538 | { | ||
1539 | struct v4l2_tuner *t = arg; | ||
1540 | u32 reg; | ||
1541 | |||
1542 | if (UNSET == core->tuner_type) | ||
1543 | return -EINVAL; | ||
1544 | if (0 != t->index) | ||
1545 | return -EINVAL; | ||
1546 | |||
1547 | memset(t,0,sizeof(*t)); | ||
1548 | strcpy(t->name, "Television"); | ||
1549 | t->type = V4L2_TUNER_ANALOG_TV; | ||
1550 | t->capability = V4L2_TUNER_CAP_NORM; | ||
1551 | t->rangehigh = 0xffffffffUL; | ||
1552 | |||
1553 | cx88_get_stereo(core ,t); | ||
1554 | reg = cx_read(MO_DEVICE_STATUS); | ||
1555 | t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; | ||
1556 | return 0; | ||
1557 | } | ||
1558 | case VIDIOC_S_TUNER: | ||
1559 | { | ||
1560 | struct v4l2_tuner *t = arg; | ||
1561 | |||
1562 | if (UNSET == core->tuner_type) | ||
1563 | return -EINVAL; | ||
1564 | if (0 != t->index) | ||
1565 | return -EINVAL; | ||
1566 | cx88_set_stereo(core, t->audmode, 1); | ||
1567 | return 0; | ||
1568 | } | ||
1569 | case VIDIOC_G_FREQUENCY: | ||
1570 | { | ||
1571 | struct v4l2_frequency *f = arg; | ||
1572 | |||
1573 | if (UNSET == core->tuner_type) | ||
1574 | return -EINVAL; | ||
1575 | if (f->tuner != 0) | ||
1576 | return -EINVAL; | ||
1577 | memset(f,0,sizeof(*f)); | ||
1578 | f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; | ||
1579 | f->frequency = dev->freq; | ||
1580 | return 0; | ||
1581 | } | ||
1582 | case VIDIOC_S_FREQUENCY: | ||
1583 | { | ||
1584 | struct v4l2_frequency *f = arg; | ||
1585 | |||
1586 | if (UNSET == core->tuner_type) | ||
1587 | return -EINVAL; | ||
1588 | if (f->tuner != 0) | ||
1589 | return -EINVAL; | ||
1590 | if (0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV) | ||
1591 | return -EINVAL; | ||
1592 | if (1 == fh->radio && f->type != V4L2_TUNER_RADIO) | ||
1593 | return -EINVAL; | ||
1594 | down(&dev->lock); | ||
1595 | dev->freq = f->frequency; | ||
1596 | cx88_newstation(core); | ||
1597 | #ifdef V4L2_I2C_CLIENTS | ||
1598 | cx88_call_i2c_clients(dev->core,VIDIOC_S_FREQUENCY,f); | ||
1599 | #else | ||
1600 | cx88_call_i2c_clients(dev->core,VIDIOCSFREQ,&dev->freq); | ||
1601 | #endif | ||
1602 | up(&dev->lock); | ||
1603 | return 0; | ||
1604 | } | ||
1605 | |||
1606 | /* --- streaming capture ------------------------------------- */ | ||
1607 | case VIDIOCGMBUF: | ||
1608 | { | ||
1609 | struct video_mbuf *mbuf = arg; | ||
1610 | struct videobuf_queue *q; | ||
1611 | struct v4l2_requestbuffers req; | ||
1612 | unsigned int i; | ||
1613 | |||
1614 | q = get_queue(fh); | ||
1615 | memset(&req,0,sizeof(req)); | ||
1616 | req.type = q->type; | ||
1617 | req.count = 8; | ||
1618 | req.memory = V4L2_MEMORY_MMAP; | ||
1619 | err = videobuf_reqbufs(q,&req); | ||
1620 | if (err < 0) | ||
1621 | return err; | ||
1622 | memset(mbuf,0,sizeof(*mbuf)); | ||
1623 | mbuf->frames = req.count; | ||
1624 | mbuf->size = 0; | ||
1625 | for (i = 0; i < mbuf->frames; i++) { | ||
1626 | mbuf->offsets[i] = q->bufs[i]->boff; | ||
1627 | mbuf->size += q->bufs[i]->bsize; | ||
1628 | } | ||
1629 | return 0; | ||
1630 | } | ||
1631 | case VIDIOC_REQBUFS: | ||
1632 | return videobuf_reqbufs(get_queue(fh), arg); | ||
1633 | |||
1634 | case VIDIOC_QUERYBUF: | ||
1635 | return videobuf_querybuf(get_queue(fh), arg); | ||
1636 | |||
1637 | case VIDIOC_QBUF: | ||
1638 | return videobuf_qbuf(get_queue(fh), arg); | ||
1639 | |||
1640 | case VIDIOC_DQBUF: | ||
1641 | return videobuf_dqbuf(get_queue(fh), arg, | ||
1642 | file->f_flags & O_NONBLOCK); | ||
1643 | |||
1644 | case VIDIOC_STREAMON: | ||
1645 | { | ||
1646 | int res = get_ressource(fh); | ||
1647 | |||
1648 | if (!res_get(dev,fh,res)) | ||
1649 | return -EBUSY; | ||
1650 | return videobuf_streamon(get_queue(fh)); | ||
1651 | } | ||
1652 | case VIDIOC_STREAMOFF: | ||
1653 | { | ||
1654 | int res = get_ressource(fh); | ||
1655 | |||
1656 | err = videobuf_streamoff(get_queue(fh)); | ||
1657 | if (err < 0) | ||
1658 | return err; | ||
1659 | res_free(dev,fh,res); | ||
1660 | return 0; | ||
1661 | } | ||
1662 | |||
1663 | default: | ||
1664 | return v4l_compat_translate_ioctl(inode,file,cmd,arg, | ||
1665 | video_do_ioctl); | ||
1666 | } | ||
1667 | return 0; | ||
1668 | } | ||
1669 | |||
1670 | static int video_ioctl(struct inode *inode, struct file *file, | ||
1671 | unsigned int cmd, unsigned long arg) | ||
1672 | { | ||
1673 | return video_usercopy(inode, file, cmd, arg, video_do_ioctl); | ||
1674 | } | ||
1675 | |||
1676 | /* ----------------------------------------------------------- */ | ||
1677 | |||
1678 | static int radio_do_ioctl(struct inode *inode, struct file *file, | ||
1679 | unsigned int cmd, void *arg) | ||
1680 | { | ||
1681 | struct cx8800_fh *fh = file->private_data; | ||
1682 | struct cx8800_dev *dev = fh->dev; | ||
1683 | struct cx88_core *core = dev->core; | ||
1684 | |||
1685 | if (video_debug > 1) | ||
1686 | cx88_print_ioctl(core->name,cmd); | ||
1687 | |||
1688 | switch (cmd) { | ||
1689 | case VIDIOC_QUERYCAP: | ||
1690 | { | ||
1691 | struct v4l2_capability *cap = arg; | ||
1692 | |||
1693 | memset(cap,0,sizeof(*cap)); | ||
1694 | strcpy(cap->driver, "cx8800"); | ||
1695 | strlcpy(cap->card, cx88_boards[core->board].name, | ||
1696 | sizeof(cap->card)); | ||
1697 | sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); | ||
1698 | cap->version = CX88_VERSION_CODE; | ||
1699 | cap->capabilities = V4L2_CAP_TUNER; | ||
1700 | return 0; | ||
1701 | } | ||
1702 | case VIDIOC_G_TUNER: | ||
1703 | { | ||
1704 | struct v4l2_tuner *t = arg; | ||
1705 | |||
1706 | if (t->index > 0) | ||
1707 | return -EINVAL; | ||
1708 | |||
1709 | memset(t,0,sizeof(*t)); | ||
1710 | strcpy(t->name, "Radio"); | ||
1711 | t->rangelow = (int)(65*16); | ||
1712 | t->rangehigh = (int)(108*16); | ||
1713 | |||
1714 | #ifdef V4L2_I2C_CLIENTS | ||
1715 | cx88_call_i2c_clients(dev->core,VIDIOC_G_TUNER,t); | ||
1716 | #else | ||
1717 | { | ||
1718 | struct video_tuner vt; | ||
1719 | memset(&vt,0,sizeof(vt)); | ||
1720 | cx88_call_i2c_clients(dev,VIDIOCGTUNER,&vt); | ||
1721 | t->signal = vt.signal; | ||
1722 | } | ||
1723 | #endif | ||
1724 | return 0; | ||
1725 | } | ||
1726 | case VIDIOC_ENUMINPUT: | ||
1727 | { | ||
1728 | struct v4l2_input *i = arg; | ||
1729 | |||
1730 | if (i->index != 0) | ||
1731 | return -EINVAL; | ||
1732 | strcpy(i->name,"Radio"); | ||
1733 | i->type = V4L2_INPUT_TYPE_TUNER; | ||
1734 | return 0; | ||
1735 | } | ||
1736 | case VIDIOC_G_INPUT: | ||
1737 | { | ||
1738 | int *i = arg; | ||
1739 | *i = 0; | ||
1740 | return 0; | ||
1741 | } | ||
1742 | case VIDIOC_G_AUDIO: | ||
1743 | { | ||
1744 | struct v4l2_audio *a = arg; | ||
1745 | |||
1746 | memset(a,0,sizeof(*a)); | ||
1747 | strcpy(a->name,"Radio"); | ||
1748 | return 0; | ||
1749 | } | ||
1750 | case VIDIOC_G_STD: | ||
1751 | { | ||
1752 | v4l2_std_id *id = arg; | ||
1753 | *id = 0; | ||
1754 | return 0; | ||
1755 | } | ||
1756 | case VIDIOC_S_AUDIO: | ||
1757 | case VIDIOC_S_TUNER: | ||
1758 | case VIDIOC_S_INPUT: | ||
1759 | case VIDIOC_S_STD: | ||
1760 | return 0; | ||
1761 | |||
1762 | case VIDIOC_QUERYCTRL: | ||
1763 | { | ||
1764 | struct v4l2_queryctrl *c = arg; | ||
1765 | int i; | ||
1766 | |||
1767 | if (c->id < V4L2_CID_BASE || | ||
1768 | c->id >= V4L2_CID_LASTP1) | ||
1769 | return -EINVAL; | ||
1770 | if (c->id == V4L2_CID_AUDIO_MUTE) { | ||
1771 | for (i = 0; i < CX8800_CTLS; i++) | ||
1772 | if (cx8800_ctls[i].v.id == c->id) | ||
1773 | break; | ||
1774 | *c = cx8800_ctls[i].v; | ||
1775 | } else | ||
1776 | *c = no_ctl; | ||
1777 | return 0; | ||
1778 | } | ||
1779 | |||
1780 | |||
1781 | case VIDIOC_G_CTRL: | ||
1782 | case VIDIOC_S_CTRL: | ||
1783 | case VIDIOC_G_FREQUENCY: | ||
1784 | case VIDIOC_S_FREQUENCY: | ||
1785 | return video_do_ioctl(inode,file,cmd,arg); | ||
1786 | |||
1787 | default: | ||
1788 | return v4l_compat_translate_ioctl(inode,file,cmd,arg, | ||
1789 | radio_do_ioctl); | ||
1790 | } | ||
1791 | return 0; | ||
1792 | }; | ||
1793 | |||
1794 | static int radio_ioctl(struct inode *inode, struct file *file, | ||
1795 | unsigned int cmd, unsigned long arg) | ||
1796 | { | ||
1797 | return video_usercopy(inode, file, cmd, arg, radio_do_ioctl); | ||
1798 | }; | ||
1799 | |||
1800 | /* ----------------------------------------------------------- */ | ||
1801 | |||
1802 | static void cx8800_vid_timeout(unsigned long data) | ||
1803 | { | ||
1804 | struct cx8800_dev *dev = (struct cx8800_dev*)data; | ||
1805 | struct cx88_core *core = dev->core; | ||
1806 | struct cx88_dmaqueue *q = &dev->vidq; | ||
1807 | struct cx88_buffer *buf; | ||
1808 | unsigned long flags; | ||
1809 | |||
1810 | cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH21]); | ||
1811 | |||
1812 | cx_clear(MO_VID_DMACNTRL, 0x11); | ||
1813 | cx_clear(VID_CAPTURE_CONTROL, 0x06); | ||
1814 | |||
1815 | spin_lock_irqsave(&dev->slock,flags); | ||
1816 | while (!list_empty(&q->active)) { | ||
1817 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
1818 | list_del(&buf->vb.queue); | ||
1819 | buf->vb.state = STATE_ERROR; | ||
1820 | wake_up(&buf->vb.done); | ||
1821 | printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name, | ||
1822 | buf, buf->vb.i, (unsigned long)buf->risc.dma); | ||
1823 | } | ||
1824 | restart_video_queue(dev,q); | ||
1825 | spin_unlock_irqrestore(&dev->slock,flags); | ||
1826 | } | ||
1827 | |||
1828 | static void cx8800_vid_irq(struct cx8800_dev *dev) | ||
1829 | { | ||
1830 | struct cx88_core *core = dev->core; | ||
1831 | u32 status, mask, count; | ||
1832 | |||
1833 | status = cx_read(MO_VID_INTSTAT); | ||
1834 | mask = cx_read(MO_VID_INTMSK); | ||
1835 | if (0 == (status & mask)) | ||
1836 | return; | ||
1837 | cx_write(MO_VID_INTSTAT, status); | ||
1838 | if (irq_debug || (status & mask & ~0xff)) | ||
1839 | cx88_print_irqbits(core->name, "irq vid", | ||
1840 | cx88_vid_irqs, status, mask); | ||
1841 | |||
1842 | /* risc op code error */ | ||
1843 | if (status & (1 << 16)) { | ||
1844 | printk(KERN_WARNING "%s/0: video risc op code error\n",core->name); | ||
1845 | cx_clear(MO_VID_DMACNTRL, 0x11); | ||
1846 | cx_clear(VID_CAPTURE_CONTROL, 0x06); | ||
1847 | cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH21]); | ||
1848 | } | ||
1849 | |||
1850 | /* risc1 y */ | ||
1851 | if (status & 0x01) { | ||
1852 | spin_lock(&dev->slock); | ||
1853 | count = cx_read(MO_VIDY_GPCNT); | ||
1854 | cx88_wakeup(dev->core, &dev->vidq, count); | ||
1855 | spin_unlock(&dev->slock); | ||
1856 | } | ||
1857 | |||
1858 | /* risc1 vbi */ | ||
1859 | if (status & 0x08) { | ||
1860 | spin_lock(&dev->slock); | ||
1861 | count = cx_read(MO_VBI_GPCNT); | ||
1862 | cx88_wakeup(dev->core, &dev->vbiq, count); | ||
1863 | spin_unlock(&dev->slock); | ||
1864 | } | ||
1865 | |||
1866 | /* risc2 y */ | ||
1867 | if (status & 0x10) { | ||
1868 | dprintk(2,"stopper video\n"); | ||
1869 | spin_lock(&dev->slock); | ||
1870 | restart_video_queue(dev,&dev->vidq); | ||
1871 | spin_unlock(&dev->slock); | ||
1872 | } | ||
1873 | |||
1874 | /* risc2 vbi */ | ||
1875 | if (status & 0x80) { | ||
1876 | dprintk(2,"stopper vbi\n"); | ||
1877 | spin_lock(&dev->slock); | ||
1878 | cx8800_restart_vbi_queue(dev,&dev->vbiq); | ||
1879 | spin_unlock(&dev->slock); | ||
1880 | } | ||
1881 | } | ||
1882 | |||
1883 | static irqreturn_t cx8800_irq(int irq, void *dev_id, struct pt_regs *regs) | ||
1884 | { | ||
1885 | struct cx8800_dev *dev = dev_id; | ||
1886 | struct cx88_core *core = dev->core; | ||
1887 | u32 status; | ||
1888 | int loop, handled = 0; | ||
1889 | |||
1890 | for (loop = 0; loop < 10; loop++) { | ||
1891 | status = cx_read(MO_PCI_INTSTAT) & (core->pci_irqmask | 0x01); | ||
1892 | if (0 == status) | ||
1893 | goto out; | ||
1894 | cx_write(MO_PCI_INTSTAT, status); | ||
1895 | handled = 1; | ||
1896 | |||
1897 | if (status & core->pci_irqmask) | ||
1898 | cx88_core_irq(core,status); | ||
1899 | if (status & 0x01) | ||
1900 | cx8800_vid_irq(dev); | ||
1901 | }; | ||
1902 | if (10 == loop) { | ||
1903 | printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", | ||
1904 | core->name); | ||
1905 | cx_write(MO_PCI_INTMSK,0); | ||
1906 | } | ||
1907 | |||
1908 | out: | ||
1909 | return IRQ_RETVAL(handled); | ||
1910 | } | ||
1911 | |||
1912 | /* ----------------------------------------------------------- */ | ||
1913 | /* exported stuff */ | ||
1914 | |||
1915 | static struct file_operations video_fops = | ||
1916 | { | ||
1917 | .owner = THIS_MODULE, | ||
1918 | .open = video_open, | ||
1919 | .release = video_release, | ||
1920 | .read = video_read, | ||
1921 | .poll = video_poll, | ||
1922 | .mmap = video_mmap, | ||
1923 | .ioctl = video_ioctl, | ||
1924 | .llseek = no_llseek, | ||
1925 | }; | ||
1926 | |||
1927 | struct video_device cx8800_video_template = | ||
1928 | { | ||
1929 | .name = "cx8800-video", | ||
1930 | .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES, | ||
1931 | .hardware = 0, | ||
1932 | .fops = &video_fops, | ||
1933 | .minor = -1, | ||
1934 | }; | ||
1935 | |||
1936 | struct video_device cx8800_vbi_template = | ||
1937 | { | ||
1938 | .name = "cx8800-vbi", | ||
1939 | .type = VID_TYPE_TELETEXT|VID_TYPE_TUNER, | ||
1940 | .hardware = 0, | ||
1941 | .fops = &video_fops, | ||
1942 | .minor = -1, | ||
1943 | }; | ||
1944 | |||
1945 | static struct file_operations radio_fops = | ||
1946 | { | ||
1947 | .owner = THIS_MODULE, | ||
1948 | .open = video_open, | ||
1949 | .release = video_release, | ||
1950 | .ioctl = radio_ioctl, | ||
1951 | .llseek = no_llseek, | ||
1952 | }; | ||
1953 | |||
1954 | struct video_device cx8800_radio_template = | ||
1955 | { | ||
1956 | .name = "cx8800-radio", | ||
1957 | .type = VID_TYPE_TUNER, | ||
1958 | .hardware = 0, | ||
1959 | .fops = &radio_fops, | ||
1960 | .minor = -1, | ||
1961 | }; | ||
1962 | |||
1963 | /* ----------------------------------------------------------- */ | ||
1964 | |||
1965 | static void cx8800_unregister_video(struct cx8800_dev *dev) | ||
1966 | { | ||
1967 | if (dev->radio_dev) { | ||
1968 | if (-1 != dev->radio_dev->minor) | ||
1969 | video_unregister_device(dev->radio_dev); | ||
1970 | else | ||
1971 | video_device_release(dev->radio_dev); | ||
1972 | dev->radio_dev = NULL; | ||
1973 | } | ||
1974 | if (dev->vbi_dev) { | ||
1975 | if (-1 != dev->vbi_dev->minor) | ||
1976 | video_unregister_device(dev->vbi_dev); | ||
1977 | else | ||
1978 | video_device_release(dev->vbi_dev); | ||
1979 | dev->vbi_dev = NULL; | ||
1980 | } | ||
1981 | if (dev->video_dev) { | ||
1982 | if (-1 != dev->video_dev->minor) | ||
1983 | video_unregister_device(dev->video_dev); | ||
1984 | else | ||
1985 | video_device_release(dev->video_dev); | ||
1986 | dev->video_dev = NULL; | ||
1987 | } | ||
1988 | } | ||
1989 | |||
1990 | static int __devinit cx8800_initdev(struct pci_dev *pci_dev, | ||
1991 | const struct pci_device_id *pci_id) | ||
1992 | { | ||
1993 | struct cx8800_dev *dev; | ||
1994 | struct cx88_core *core; | ||
1995 | int err; | ||
1996 | |||
1997 | dev = kmalloc(sizeof(*dev),GFP_KERNEL); | ||
1998 | if (NULL == dev) | ||
1999 | return -ENOMEM; | ||
2000 | memset(dev,0,sizeof(*dev)); | ||
2001 | |||
2002 | /* pci init */ | ||
2003 | dev->pci = pci_dev; | ||
2004 | if (pci_enable_device(pci_dev)) { | ||
2005 | err = -EIO; | ||
2006 | goto fail_free; | ||
2007 | } | ||
2008 | core = cx88_core_get(dev->pci); | ||
2009 | if (NULL == core) { | ||
2010 | err = -EINVAL; | ||
2011 | goto fail_free; | ||
2012 | } | ||
2013 | dev->core = core; | ||
2014 | |||
2015 | /* print pci info */ | ||
2016 | pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); | ||
2017 | pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); | ||
2018 | printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " | ||
2019 | "latency: %d, mmio: 0x%lx\n", core->name, | ||
2020 | pci_name(pci_dev), dev->pci_rev, pci_dev->irq, | ||
2021 | dev->pci_lat,pci_resource_start(pci_dev,0)); | ||
2022 | |||
2023 | pci_set_master(pci_dev); | ||
2024 | if (!pci_dma_supported(pci_dev,0xffffffff)) { | ||
2025 | printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name); | ||
2026 | err = -EIO; | ||
2027 | goto fail_core; | ||
2028 | } | ||
2029 | |||
2030 | /* initialize driver struct */ | ||
2031 | init_MUTEX(&dev->lock); | ||
2032 | spin_lock_init(&dev->slock); | ||
2033 | core->tvnorm = tvnorms; | ||
2034 | |||
2035 | /* init video dma queues */ | ||
2036 | INIT_LIST_HEAD(&dev->vidq.active); | ||
2037 | INIT_LIST_HEAD(&dev->vidq.queued); | ||
2038 | dev->vidq.timeout.function = cx8800_vid_timeout; | ||
2039 | dev->vidq.timeout.data = (unsigned long)dev; | ||
2040 | init_timer(&dev->vidq.timeout); | ||
2041 | cx88_risc_stopper(dev->pci,&dev->vidq.stopper, | ||
2042 | MO_VID_DMACNTRL,0x11,0x00); | ||
2043 | |||
2044 | /* init vbi dma queues */ | ||
2045 | INIT_LIST_HEAD(&dev->vbiq.active); | ||
2046 | INIT_LIST_HEAD(&dev->vbiq.queued); | ||
2047 | dev->vbiq.timeout.function = cx8800_vbi_timeout; | ||
2048 | dev->vbiq.timeout.data = (unsigned long)dev; | ||
2049 | init_timer(&dev->vbiq.timeout); | ||
2050 | cx88_risc_stopper(dev->pci,&dev->vbiq.stopper, | ||
2051 | MO_VID_DMACNTRL,0x88,0x00); | ||
2052 | |||
2053 | /* get irq */ | ||
2054 | err = request_irq(pci_dev->irq, cx8800_irq, | ||
2055 | SA_SHIRQ | SA_INTERRUPT, core->name, dev); | ||
2056 | if (err < 0) { | ||
2057 | printk(KERN_ERR "%s: can't get IRQ %d\n", | ||
2058 | core->name,pci_dev->irq); | ||
2059 | goto fail_core; | ||
2060 | } | ||
2061 | cx_set(MO_PCI_INTMSK, core->pci_irqmask); | ||
2062 | |||
2063 | /* load and configure helper modules */ | ||
2064 | if (TUNER_ABSENT != core->tuner_type) | ||
2065 | request_module("tuner"); | ||
2066 | if (core->tda9887_conf) | ||
2067 | request_module("tda9887"); | ||
2068 | if (core->tuner_type != UNSET) | ||
2069 | cx88_call_i2c_clients(dev->core,TUNER_SET_TYPE,&core->tuner_type); | ||
2070 | if (core->tda9887_conf) | ||
2071 | cx88_call_i2c_clients(dev->core,TDA9887_SET_CONFIG,&core->tda9887_conf); | ||
2072 | |||
2073 | /* register v4l devices */ | ||
2074 | dev->video_dev = cx88_vdev_init(core,dev->pci, | ||
2075 | &cx8800_video_template,"video"); | ||
2076 | err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, | ||
2077 | video_nr[core->nr]); | ||
2078 | if (err < 0) { | ||
2079 | printk(KERN_INFO "%s: can't register video device\n", | ||
2080 | core->name); | ||
2081 | goto fail_unreg; | ||
2082 | } | ||
2083 | printk(KERN_INFO "%s/0: registered device video%d [v4l2]\n", | ||
2084 | core->name,dev->video_dev->minor & 0x1f); | ||
2085 | |||
2086 | dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi"); | ||
2087 | err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, | ||
2088 | vbi_nr[core->nr]); | ||
2089 | if (err < 0) { | ||
2090 | printk(KERN_INFO "%s/0: can't register vbi device\n", | ||
2091 | core->name); | ||
2092 | goto fail_unreg; | ||
2093 | } | ||
2094 | printk(KERN_INFO "%s/0: registered device vbi%d\n", | ||
2095 | core->name,dev->vbi_dev->minor & 0x1f); | ||
2096 | |||
2097 | if (core->has_radio) { | ||
2098 | dev->radio_dev = cx88_vdev_init(core,dev->pci, | ||
2099 | &cx8800_radio_template,"radio"); | ||
2100 | err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, | ||
2101 | radio_nr[core->nr]); | ||
2102 | if (err < 0) { | ||
2103 | printk(KERN_INFO "%s/0: can't register radio device\n", | ||
2104 | core->name); | ||
2105 | goto fail_unreg; | ||
2106 | } | ||
2107 | printk(KERN_INFO "%s/0: registered device radio%d\n", | ||
2108 | core->name,dev->radio_dev->minor & 0x1f); | ||
2109 | } | ||
2110 | |||
2111 | /* everything worked */ | ||
2112 | list_add_tail(&dev->devlist,&cx8800_devlist); | ||
2113 | pci_set_drvdata(pci_dev,dev); | ||
2114 | |||
2115 | /* initial device configuration */ | ||
2116 | down(&dev->lock); | ||
2117 | init_controls(dev); | ||
2118 | cx88_set_tvnorm(dev->core,tvnorms); | ||
2119 | video_mux(dev,0); | ||
2120 | up(&dev->lock); | ||
2121 | |||
2122 | /* start tvaudio thread */ | ||
2123 | if (core->tuner_type != TUNER_ABSENT) | ||
2124 | core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio"); | ||
2125 | return 0; | ||
2126 | |||
2127 | fail_unreg: | ||
2128 | cx8800_unregister_video(dev); | ||
2129 | free_irq(pci_dev->irq, dev); | ||
2130 | fail_core: | ||
2131 | cx88_core_put(core,dev->pci); | ||
2132 | fail_free: | ||
2133 | kfree(dev); | ||
2134 | return err; | ||
2135 | } | ||
2136 | |||
2137 | static void __devexit cx8800_finidev(struct pci_dev *pci_dev) | ||
2138 | { | ||
2139 | struct cx8800_dev *dev = pci_get_drvdata(pci_dev); | ||
2140 | |||
2141 | /* stop thread */ | ||
2142 | if (dev->core->kthread) { | ||
2143 | kthread_stop(dev->core->kthread); | ||
2144 | dev->core->kthread = NULL; | ||
2145 | } | ||
2146 | |||
2147 | cx88_shutdown(dev->core); /* FIXME */ | ||
2148 | pci_disable_device(pci_dev); | ||
2149 | |||
2150 | /* unregister stuff */ | ||
2151 | |||
2152 | free_irq(pci_dev->irq, dev); | ||
2153 | cx8800_unregister_video(dev); | ||
2154 | pci_set_drvdata(pci_dev, NULL); | ||
2155 | |||
2156 | /* free memory */ | ||
2157 | btcx_riscmem_free(dev->pci,&dev->vidq.stopper); | ||
2158 | list_del(&dev->devlist); | ||
2159 | cx88_core_put(dev->core,dev->pci); | ||
2160 | kfree(dev); | ||
2161 | } | ||
2162 | |||
2163 | static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) | ||
2164 | { | ||
2165 | struct cx8800_dev *dev = pci_get_drvdata(pci_dev); | ||
2166 | struct cx88_core *core = dev->core; | ||
2167 | |||
2168 | /* stop video+vbi capture */ | ||
2169 | spin_lock(&dev->slock); | ||
2170 | if (!list_empty(&dev->vidq.active)) { | ||
2171 | printk("%s: suspend video\n", core->name); | ||
2172 | stop_video_dma(dev); | ||
2173 | del_timer(&dev->vidq.timeout); | ||
2174 | } | ||
2175 | if (!list_empty(&dev->vbiq.active)) { | ||
2176 | printk("%s: suspend vbi\n", core->name); | ||
2177 | cx8800_stop_vbi_dma(dev); | ||
2178 | del_timer(&dev->vbiq.timeout); | ||
2179 | } | ||
2180 | spin_unlock(&dev->slock); | ||
2181 | |||
2182 | #if 1 | ||
2183 | /* FIXME -- shutdown device */ | ||
2184 | cx88_shutdown(dev->core); | ||
2185 | #endif | ||
2186 | |||
2187 | pci_save_state(pci_dev); | ||
2188 | if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) { | ||
2189 | pci_disable_device(pci_dev); | ||
2190 | dev->state.disabled = 1; | ||
2191 | } | ||
2192 | return 0; | ||
2193 | } | ||
2194 | |||
2195 | static int cx8800_resume(struct pci_dev *pci_dev) | ||
2196 | { | ||
2197 | struct cx8800_dev *dev = pci_get_drvdata(pci_dev); | ||
2198 | struct cx88_core *core = dev->core; | ||
2199 | |||
2200 | if (dev->state.disabled) { | ||
2201 | pci_enable_device(pci_dev); | ||
2202 | dev->state.disabled = 0; | ||
2203 | } | ||
2204 | pci_set_power_state(pci_dev, PCI_D0); | ||
2205 | pci_restore_state(pci_dev); | ||
2206 | |||
2207 | #if 1 | ||
2208 | /* FIXME: re-initialize hardware */ | ||
2209 | cx88_reset(dev->core); | ||
2210 | #endif | ||
2211 | |||
2212 | /* restart video+vbi capture */ | ||
2213 | spin_lock(&dev->slock); | ||
2214 | if (!list_empty(&dev->vidq.active)) { | ||
2215 | printk("%s: resume video\n", core->name); | ||
2216 | restart_video_queue(dev,&dev->vidq); | ||
2217 | } | ||
2218 | if (!list_empty(&dev->vbiq.active)) { | ||
2219 | printk("%s: resume vbi\n", core->name); | ||
2220 | cx8800_restart_vbi_queue(dev,&dev->vbiq); | ||
2221 | } | ||
2222 | spin_unlock(&dev->slock); | ||
2223 | |||
2224 | return 0; | ||
2225 | } | ||
2226 | |||
2227 | /* ----------------------------------------------------------- */ | ||
2228 | |||
2229 | struct pci_device_id cx8800_pci_tbl[] = { | ||
2230 | { | ||
2231 | .vendor = 0x14f1, | ||
2232 | .device = 0x8800, | ||
2233 | .subvendor = PCI_ANY_ID, | ||
2234 | .subdevice = PCI_ANY_ID, | ||
2235 | },{ | ||
2236 | /* --- end of list --- */ | ||
2237 | } | ||
2238 | }; | ||
2239 | MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl); | ||
2240 | |||
2241 | static struct pci_driver cx8800_pci_driver = { | ||
2242 | .name = "cx8800", | ||
2243 | .id_table = cx8800_pci_tbl, | ||
2244 | .probe = cx8800_initdev, | ||
2245 | .remove = __devexit_p(cx8800_finidev), | ||
2246 | |||
2247 | .suspend = cx8800_suspend, | ||
2248 | .resume = cx8800_resume, | ||
2249 | }; | ||
2250 | |||
2251 | static int cx8800_init(void) | ||
2252 | { | ||
2253 | printk(KERN_INFO "cx2388x v4l2 driver version %d.%d.%d loaded\n", | ||
2254 | (CX88_VERSION_CODE >> 16) & 0xff, | ||
2255 | (CX88_VERSION_CODE >> 8) & 0xff, | ||
2256 | CX88_VERSION_CODE & 0xff); | ||
2257 | #ifdef SNAPSHOT | ||
2258 | printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", | ||
2259 | SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); | ||
2260 | #endif | ||
2261 | return pci_register_driver(&cx8800_pci_driver); | ||
2262 | } | ||
2263 | |||
2264 | static void cx8800_fini(void) | ||
2265 | { | ||
2266 | pci_unregister_driver(&cx8800_pci_driver); | ||
2267 | } | ||
2268 | |||
2269 | module_init(cx8800_init); | ||
2270 | module_exit(cx8800_fini); | ||
2271 | |||
2272 | /* ----------------------------------------------------------- */ | ||
2273 | /* | ||
2274 | * Local variables: | ||
2275 | * c-basic-offset: 8 | ||
2276 | * End: | ||
2277 | */ | ||
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h new file mode 100644 index 000000000000..b351d9eae615 --- /dev/null +++ b/drivers/media/video/cx88/cx88.h | |||
@@ -0,0 +1,551 @@ | |||
1 | /* | ||
2 | * $Id: cx88.h,v 1.56 2005/03/04 09:12:23 kraxel Exp $ | ||
3 | * | ||
4 | * v4l2 device driver for cx2388x based TV cards | ||
5 | * | ||
6 | * (c) 2003,04 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs] | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | */ | ||
22 | |||
23 | #include <linux/pci.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/i2c-algo-bit.h> | ||
26 | #include <linux/videodev.h> | ||
27 | #include <linux/kdev_t.h> | ||
28 | |||
29 | #include <media/tuner.h> | ||
30 | #include <media/tveeprom.h> | ||
31 | #include <media/audiochip.h> | ||
32 | #include <media/video-buf.h> | ||
33 | #include <media/video-buf-dvb.h> | ||
34 | |||
35 | #include "btcx-risc.h" | ||
36 | #include "cx88-reg.h" | ||
37 | |||
38 | #include <linux/version.h> | ||
39 | #define CX88_VERSION_CODE KERNEL_VERSION(0,0,4) | ||
40 | |||
41 | #ifndef TRUE | ||
42 | # define TRUE (1==1) | ||
43 | #endif | ||
44 | #ifndef FALSE | ||
45 | # define FALSE (1==0) | ||
46 | #endif | ||
47 | #define UNSET (-1U) | ||
48 | |||
49 | #define CX88_MAXBOARDS 8 | ||
50 | |||
51 | /* ----------------------------------------------------------- */ | ||
52 | /* defines and enums */ | ||
53 | |||
54 | #define V4L2_I2C_CLIENTS 1 | ||
55 | |||
56 | #define FORMAT_FLAGS_PACKED 0x01 | ||
57 | #define FORMAT_FLAGS_PLANAR 0x02 | ||
58 | |||
59 | #define VBI_LINE_COUNT 17 | ||
60 | #define VBI_LINE_LENGTH 2048 | ||
61 | |||
62 | /* need "shadow" registers for some write-only ones ... */ | ||
63 | #define SHADOW_AUD_VOL_CTL 1 | ||
64 | #define SHADOW_AUD_BAL_CTL 2 | ||
65 | #define SHADOW_MAX 2 | ||
66 | |||
67 | /* ----------------------------------------------------------- */ | ||
68 | /* tv norms */ | ||
69 | |||
70 | struct cx88_tvnorm { | ||
71 | char *name; | ||
72 | v4l2_std_id id; | ||
73 | u32 cxiformat; | ||
74 | u32 cxoformat; | ||
75 | }; | ||
76 | |||
77 | static unsigned int inline norm_maxw(struct cx88_tvnorm *norm) | ||
78 | { | ||
79 | return (norm->id & V4L2_STD_625_50) ? 768 : 640; | ||
80 | // return (norm->id & V4L2_STD_625_50) ? 720 : 640; | ||
81 | } | ||
82 | |||
83 | static unsigned int inline norm_maxh(struct cx88_tvnorm *norm) | ||
84 | { | ||
85 | return (norm->id & V4L2_STD_625_50) ? 576 : 480; | ||
86 | } | ||
87 | |||
88 | /* ----------------------------------------------------------- */ | ||
89 | /* static data */ | ||
90 | |||
91 | struct cx8800_fmt { | ||
92 | char *name; | ||
93 | u32 fourcc; /* v4l2 format id */ | ||
94 | int depth; | ||
95 | int flags; | ||
96 | u32 cxformat; | ||
97 | }; | ||
98 | |||
99 | struct cx88_ctrl { | ||
100 | struct v4l2_queryctrl v; | ||
101 | u32 off; | ||
102 | u32 reg; | ||
103 | u32 sreg; | ||
104 | u32 mask; | ||
105 | u32 shift; | ||
106 | }; | ||
107 | |||
108 | /* ----------------------------------------------------------- */ | ||
109 | /* SRAM memory management data (see cx88-core.c) */ | ||
110 | |||
111 | #define SRAM_CH21 0 /* video */ | ||
112 | #define SRAM_CH22 1 | ||
113 | #define SRAM_CH23 2 | ||
114 | #define SRAM_CH24 3 /* vbi */ | ||
115 | #define SRAM_CH25 4 /* audio */ | ||
116 | #define SRAM_CH26 5 | ||
117 | #define SRAM_CH28 6 /* mpeg */ | ||
118 | /* more */ | ||
119 | |||
120 | struct sram_channel { | ||
121 | char *name; | ||
122 | u32 cmds_start; | ||
123 | u32 ctrl_start; | ||
124 | u32 cdt; | ||
125 | u32 fifo_start; | ||
126 | u32 fifo_size; | ||
127 | u32 ptr1_reg; | ||
128 | u32 ptr2_reg; | ||
129 | u32 cnt1_reg; | ||
130 | u32 cnt2_reg; | ||
131 | }; | ||
132 | extern struct sram_channel cx88_sram_channels[]; | ||
133 | |||
134 | /* ----------------------------------------------------------- */ | ||
135 | /* card configuration */ | ||
136 | |||
137 | #define CX88_BOARD_NOAUTO UNSET | ||
138 | #define CX88_BOARD_UNKNOWN 0 | ||
139 | #define CX88_BOARD_HAUPPAUGE 1 | ||
140 | #define CX88_BOARD_GDI 2 | ||
141 | #define CX88_BOARD_PIXELVIEW 3 | ||
142 | #define CX88_BOARD_ATI_WONDER_PRO 4 | ||
143 | #define CX88_BOARD_WINFAST2000XP_EXPERT 5 | ||
144 | #define CX88_BOARD_AVERTV_303 6 | ||
145 | #define CX88_BOARD_MSI_TVANYWHERE_MASTER 7 | ||
146 | #define CX88_BOARD_WINFAST_DV2000 8 | ||
147 | #define CX88_BOARD_LEADTEK_PVR2000 9 | ||
148 | #define CX88_BOARD_IODATA_GVVCP3PCI 10 | ||
149 | #define CX88_BOARD_PROLINK_PLAYTVPVR 11 | ||
150 | #define CX88_BOARD_ASUS_PVR_416 12 | ||
151 | #define CX88_BOARD_MSI_TVANYWHERE 13 | ||
152 | #define CX88_BOARD_KWORLD_DVB_T 14 | ||
153 | #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1 15 | ||
154 | #define CX88_BOARD_KWORLD_LTV883 16 | ||
155 | #define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD 17 | ||
156 | #define CX88_BOARD_HAUPPAUGE_DVB_T1 18 | ||
157 | #define CX88_BOARD_CONEXANT_DVB_T1 19 | ||
158 | #define CX88_BOARD_PROVIDEO_PV259 20 | ||
159 | #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS 21 | ||
160 | #define CX88_BOARD_PCHDTV_HD3000 22 | ||
161 | #define CX88_BOARD_DNTV_LIVE_DVB_T 23 | ||
162 | #define CX88_BOARD_HAUPPAUGE_ROSLYN 24 | ||
163 | #define CX88_BOARD_DIGITALLOGIC_MEC 25 | ||
164 | #define CX88_BOARD_IODATA_GVBCTV7E 26 | ||
165 | |||
166 | enum cx88_itype { | ||
167 | CX88_VMUX_COMPOSITE1 = 1, | ||
168 | CX88_VMUX_COMPOSITE2, | ||
169 | CX88_VMUX_COMPOSITE3, | ||
170 | CX88_VMUX_COMPOSITE4, | ||
171 | CX88_VMUX_SVIDEO, | ||
172 | CX88_VMUX_TELEVISION, | ||
173 | CX88_VMUX_CABLE, | ||
174 | CX88_VMUX_DVB, | ||
175 | CX88_VMUX_DEBUG, | ||
176 | CX88_RADIO, | ||
177 | }; | ||
178 | |||
179 | struct cx88_input { | ||
180 | enum cx88_itype type; | ||
181 | unsigned int vmux; | ||
182 | u32 gpio0, gpio1, gpio2, gpio3; | ||
183 | }; | ||
184 | |||
185 | struct cx88_board { | ||
186 | char *name; | ||
187 | unsigned int tuner_type; | ||
188 | int tda9887_conf; | ||
189 | struct cx88_input input[8]; | ||
190 | struct cx88_input radio; | ||
191 | int blackbird:1; | ||
192 | int dvb:1; | ||
193 | }; | ||
194 | |||
195 | struct cx88_subid { | ||
196 | u16 subvendor; | ||
197 | u16 subdevice; | ||
198 | u32 card; | ||
199 | }; | ||
200 | |||
201 | #define INPUT(nr) (&cx88_boards[core->board].input[nr]) | ||
202 | |||
203 | /* ----------------------------------------------------------- */ | ||
204 | /* device / file handle status */ | ||
205 | |||
206 | #define RESOURCE_OVERLAY 1 | ||
207 | #define RESOURCE_VIDEO 2 | ||
208 | #define RESOURCE_VBI 4 | ||
209 | |||
210 | #define BUFFER_TIMEOUT (HZ/2) /* 0.5 seconds */ | ||
211 | //#define BUFFER_TIMEOUT (HZ*2) | ||
212 | |||
213 | /* buffer for one video frame */ | ||
214 | struct cx88_buffer { | ||
215 | /* common v4l buffer stuff -- must be first */ | ||
216 | struct videobuf_buffer vb; | ||
217 | |||
218 | /* cx88 specific */ | ||
219 | unsigned int bpl; | ||
220 | struct btcx_riscmem risc; | ||
221 | struct cx8800_fmt *fmt; | ||
222 | u32 count; | ||
223 | }; | ||
224 | |||
225 | struct cx88_dmaqueue { | ||
226 | struct list_head active; | ||
227 | struct list_head queued; | ||
228 | struct timer_list timeout; | ||
229 | struct btcx_riscmem stopper; | ||
230 | u32 count; | ||
231 | }; | ||
232 | |||
233 | struct cx88_core { | ||
234 | struct list_head devlist; | ||
235 | atomic_t refcount; | ||
236 | |||
237 | /* board name */ | ||
238 | int nr; | ||
239 | char name[32]; | ||
240 | |||
241 | /* pci stuff */ | ||
242 | int pci_bus; | ||
243 | int pci_slot; | ||
244 | u32 __iomem *lmmio; | ||
245 | u8 __iomem *bmmio; | ||
246 | u32 shadow[SHADOW_MAX]; | ||
247 | int pci_irqmask; | ||
248 | |||
249 | /* i2c i/o */ | ||
250 | struct i2c_adapter i2c_adap; | ||
251 | struct i2c_algo_bit_data i2c_algo; | ||
252 | struct i2c_client i2c_client; | ||
253 | u32 i2c_state, i2c_rc; | ||
254 | |||
255 | /* config info -- analog */ | ||
256 | unsigned int board; | ||
257 | unsigned int tuner_type; | ||
258 | unsigned int tda9887_conf; | ||
259 | unsigned int has_radio; | ||
260 | |||
261 | /* config info -- dvb */ | ||
262 | struct dvb_pll_desc *pll_desc; | ||
263 | unsigned int pll_addr; | ||
264 | |||
265 | /* state info */ | ||
266 | struct task_struct *kthread; | ||
267 | struct cx88_tvnorm *tvnorm; | ||
268 | u32 tvaudio; | ||
269 | u32 audiomode_manual; | ||
270 | u32 audiomode_current; | ||
271 | u32 input; | ||
272 | u32 astat; | ||
273 | |||
274 | /* IR remote control state */ | ||
275 | struct cx88_IR *ir; | ||
276 | }; | ||
277 | |||
278 | struct cx8800_dev; | ||
279 | struct cx8802_dev; | ||
280 | |||
281 | /* ----------------------------------------------------------- */ | ||
282 | /* function 0: video stuff */ | ||
283 | |||
284 | struct cx8800_fh { | ||
285 | struct cx8800_dev *dev; | ||
286 | enum v4l2_buf_type type; | ||
287 | int radio; | ||
288 | unsigned int resources; | ||
289 | |||
290 | /* video overlay */ | ||
291 | struct v4l2_window win; | ||
292 | struct v4l2_clip *clips; | ||
293 | unsigned int nclips; | ||
294 | |||
295 | /* video capture */ | ||
296 | struct cx8800_fmt *fmt; | ||
297 | unsigned int width,height; | ||
298 | struct videobuf_queue vidq; | ||
299 | |||
300 | /* vbi capture */ | ||
301 | struct videobuf_queue vbiq; | ||
302 | }; | ||
303 | |||
304 | struct cx8800_suspend_state { | ||
305 | int disabled; | ||
306 | }; | ||
307 | |||
308 | struct cx8800_dev { | ||
309 | struct cx88_core *core; | ||
310 | struct list_head devlist; | ||
311 | struct semaphore lock; | ||
312 | spinlock_t slock; | ||
313 | |||
314 | /* various device info */ | ||
315 | unsigned int resources; | ||
316 | struct video_device *video_dev; | ||
317 | struct video_device *vbi_dev; | ||
318 | struct video_device *radio_dev; | ||
319 | |||
320 | /* pci i/o */ | ||
321 | struct pci_dev *pci; | ||
322 | unsigned char pci_rev,pci_lat; | ||
323 | |||
324 | #if 0 | ||
325 | /* video overlay */ | ||
326 | struct v4l2_framebuffer fbuf; | ||
327 | struct cx88_buffer *screen; | ||
328 | #endif | ||
329 | |||
330 | /* capture queues */ | ||
331 | struct cx88_dmaqueue vidq; | ||
332 | struct cx88_dmaqueue vbiq; | ||
333 | |||
334 | /* various v4l controls */ | ||
335 | u32 freq; | ||
336 | |||
337 | /* other global state info */ | ||
338 | struct cx8800_suspend_state state; | ||
339 | }; | ||
340 | |||
341 | /* ----------------------------------------------------------- */ | ||
342 | /* function 1: audio/alsa stuff */ | ||
343 | |||
344 | struct cx8801_dev { | ||
345 | struct cx88_core *core; | ||
346 | |||
347 | /* pci i/o */ | ||
348 | struct pci_dev *pci; | ||
349 | unsigned char pci_rev,pci_lat; | ||
350 | }; | ||
351 | |||
352 | /* ----------------------------------------------------------- */ | ||
353 | /* function 2: mpeg stuff */ | ||
354 | |||
355 | struct cx8802_fh { | ||
356 | struct cx8802_dev *dev; | ||
357 | struct videobuf_queue mpegq; | ||
358 | }; | ||
359 | |||
360 | struct cx8802_suspend_state { | ||
361 | int disabled; | ||
362 | }; | ||
363 | |||
364 | struct cx8802_dev { | ||
365 | struct cx88_core *core; | ||
366 | struct semaphore lock; | ||
367 | spinlock_t slock; | ||
368 | |||
369 | /* pci i/o */ | ||
370 | struct pci_dev *pci; | ||
371 | unsigned char pci_rev,pci_lat; | ||
372 | |||
373 | /* dma queues */ | ||
374 | struct cx88_dmaqueue mpegq; | ||
375 | u32 ts_packet_size; | ||
376 | u32 ts_packet_count; | ||
377 | |||
378 | /* other global state info */ | ||
379 | struct cx8802_suspend_state state; | ||
380 | |||
381 | /* for blackbird only */ | ||
382 | struct list_head devlist; | ||
383 | struct video_device *mpeg_dev; | ||
384 | u32 mailbox; | ||
385 | int width; | ||
386 | int height; | ||
387 | |||
388 | /* for dvb only */ | ||
389 | struct videobuf_dvb dvb; | ||
390 | void* fe_handle; | ||
391 | int (*fe_release)(void *handle); | ||
392 | |||
393 | /* for switching modulation types */ | ||
394 | unsigned char ts_gen_cntrl; | ||
395 | }; | ||
396 | |||
397 | /* ----------------------------------------------------------- */ | ||
398 | |||
399 | #define cx_read(reg) readl(core->lmmio + ((reg)>>2)) | ||
400 | #define cx_write(reg,value) writel((value), core->lmmio + ((reg)>>2)) | ||
401 | #define cx_writeb(reg,value) writeb((value), core->bmmio + (reg)) | ||
402 | |||
403 | #define cx_andor(reg,mask,value) \ | ||
404 | writel((readl(core->lmmio+((reg)>>2)) & ~(mask)) |\ | ||
405 | ((value) & (mask)), core->lmmio+((reg)>>2)) | ||
406 | #define cx_set(reg,bit) cx_andor((reg),(bit),(bit)) | ||
407 | #define cx_clear(reg,bit) cx_andor((reg),(bit),0) | ||
408 | |||
409 | #define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); } | ||
410 | |||
411 | /* shadow registers */ | ||
412 | #define cx_sread(sreg) (core->shadow[sreg]) | ||
413 | #define cx_swrite(sreg,reg,value) \ | ||
414 | (core->shadow[sreg] = value, \ | ||
415 | writel(core->shadow[sreg], core->lmmio + ((reg)>>2))) | ||
416 | #define cx_sandor(sreg,reg,mask,value) \ | ||
417 | (core->shadow[sreg] = (core->shadow[sreg] & ~(mask)) | ((value) & (mask)), \ | ||
418 | writel(core->shadow[sreg], core->lmmio + ((reg)>>2))) | ||
419 | |||
420 | /* ----------------------------------------------------------- */ | ||
421 | /* cx88-core.c */ | ||
422 | |||
423 | extern char *cx88_pci_irqs[32]; | ||
424 | extern char *cx88_vid_irqs[32]; | ||
425 | extern char *cx88_mpeg_irqs[32]; | ||
426 | extern void cx88_print_irqbits(char *name, char *tag, char **strings, | ||
427 | u32 bits, u32 mask); | ||
428 | extern void cx88_print_ioctl(char *name, unsigned int cmd); | ||
429 | |||
430 | extern int cx88_core_irq(struct cx88_core *core, u32 status); | ||
431 | extern void cx88_wakeup(struct cx88_core *core, | ||
432 | struct cx88_dmaqueue *q, u32 count); | ||
433 | extern void cx88_shutdown(struct cx88_core *core); | ||
434 | extern int cx88_reset(struct cx88_core *core); | ||
435 | |||
436 | extern int | ||
437 | cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
438 | struct scatterlist *sglist, | ||
439 | unsigned int top_offset, unsigned int bottom_offset, | ||
440 | unsigned int bpl, unsigned int padding, unsigned int lines); | ||
441 | extern int | ||
442 | cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
443 | struct scatterlist *sglist, unsigned int bpl, | ||
444 | unsigned int lines); | ||
445 | extern int | ||
446 | cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
447 | u32 reg, u32 mask, u32 value); | ||
448 | extern void | ||
449 | cx88_free_buffer(struct pci_dev *pci, struct cx88_buffer *buf); | ||
450 | |||
451 | extern void cx88_risc_disasm(struct cx88_core *core, | ||
452 | struct btcx_riscmem *risc); | ||
453 | extern int cx88_sram_channel_setup(struct cx88_core *core, | ||
454 | struct sram_channel *ch, | ||
455 | unsigned int bpl, u32 risc); | ||
456 | extern void cx88_sram_channel_dump(struct cx88_core *core, | ||
457 | struct sram_channel *ch); | ||
458 | |||
459 | extern int cx88_set_scale(struct cx88_core *core, unsigned int width, | ||
460 | unsigned int height, enum v4l2_field field); | ||
461 | extern int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm); | ||
462 | |||
463 | extern struct video_device *cx88_vdev_init(struct cx88_core *core, | ||
464 | struct pci_dev *pci, | ||
465 | struct video_device *template, | ||
466 | char *type); | ||
467 | extern struct cx88_core* cx88_core_get(struct pci_dev *pci); | ||
468 | extern void cx88_core_put(struct cx88_core *core, | ||
469 | struct pci_dev *pci); | ||
470 | |||
471 | /* ----------------------------------------------------------- */ | ||
472 | /* cx88-vbi.c */ | ||
473 | |||
474 | void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f); | ||
475 | int cx8800_start_vbi_dma(struct cx8800_dev *dev, | ||
476 | struct cx88_dmaqueue *q, | ||
477 | struct cx88_buffer *buf); | ||
478 | int cx8800_stop_vbi_dma(struct cx8800_dev *dev); | ||
479 | int cx8800_restart_vbi_queue(struct cx8800_dev *dev, | ||
480 | struct cx88_dmaqueue *q); | ||
481 | void cx8800_vbi_timeout(unsigned long data); | ||
482 | |||
483 | extern struct videobuf_queue_ops cx8800_vbi_qops; | ||
484 | |||
485 | /* ----------------------------------------------------------- */ | ||
486 | /* cx88-i2c.c */ | ||
487 | |||
488 | extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci); | ||
489 | extern void cx88_call_i2c_clients(struct cx88_core *core, | ||
490 | unsigned int cmd, void *arg); | ||
491 | |||
492 | |||
493 | /* ----------------------------------------------------------- */ | ||
494 | /* cx88-cards.c */ | ||
495 | |||
496 | extern struct cx88_board cx88_boards[]; | ||
497 | extern const unsigned int cx88_bcount; | ||
498 | |||
499 | extern struct cx88_subid cx88_subids[]; | ||
500 | extern const unsigned int cx88_idcount; | ||
501 | |||
502 | extern void cx88_card_list(struct cx88_core *core, struct pci_dev *pci); | ||
503 | extern void cx88_card_setup(struct cx88_core *core); | ||
504 | |||
505 | /* ----------------------------------------------------------- */ | ||
506 | /* cx88-tvaudio.c */ | ||
507 | |||
508 | #define WW_NONE 1 | ||
509 | #define WW_BTSC 2 | ||
510 | #define WW_NICAM_I 3 | ||
511 | #define WW_NICAM_BGDKL 4 | ||
512 | #define WW_A1 5 | ||
513 | #define WW_A2_BG 6 | ||
514 | #define WW_A2_DK 7 | ||
515 | #define WW_A2_M 8 | ||
516 | #define WW_EIAJ 9 | ||
517 | #define WW_SYSTEM_L_AM 10 | ||
518 | #define WW_I2SPT 11 | ||
519 | #define WW_FM 12 | ||
520 | |||
521 | void cx88_set_tvaudio(struct cx88_core *core); | ||
522 | void cx88_newstation(struct cx88_core *core); | ||
523 | void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t); | ||
524 | void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual); | ||
525 | int cx88_audio_thread(void *data); | ||
526 | |||
527 | /* ----------------------------------------------------------- */ | ||
528 | /* cx88-input.c */ | ||
529 | |||
530 | int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci); | ||
531 | int cx88_ir_fini(struct cx88_core *core); | ||
532 | void cx88_ir_irq(struct cx88_core *core); | ||
533 | |||
534 | /* ----------------------------------------------------------- */ | ||
535 | /* cx88-mpeg.c */ | ||
536 | |||
537 | int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf); | ||
538 | void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf); | ||
539 | void cx8802_cancel_buffers(struct cx8802_dev *dev); | ||
540 | |||
541 | int cx8802_init_common(struct cx8802_dev *dev); | ||
542 | void cx8802_fini_common(struct cx8802_dev *dev); | ||
543 | |||
544 | int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state); | ||
545 | int cx8802_resume_common(struct pci_dev *pci_dev); | ||
546 | |||
547 | /* | ||
548 | * Local variables: | ||
549 | * c-basic-offset: 8 | ||
550 | * End: | ||
551 | */ | ||