blob: 0765e9db5339953580f48dc643c96d5143ccb08c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * device driver for Conexant 2388x based TV cards
4 * driver core
5 *
6 * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
7 *
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -03008 * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
9 * - Multituner support
10 * - video_ioctl2 conversion
11 * - PAL/M fixes
12 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070013 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 */
27
28#include <linux/init.h>
29#include <linux/list.h>
30#include <linux/module.h>
31#include <linux/moduleparam.h>
32#include <linux/kernel.h>
33#include <linux/slab.h>
34#include <linux/kmod.h>
35#include <linux/sound.h>
36#include <linux/interrupt.h>
37#include <linux/pci.h>
38#include <linux/delay.h>
Mauro Carvalho Chehab98f30ed2005-11-08 21:37:17 -080039#include <linux/videodev2.h>
Ingo Molnar1e4baed2006-01-15 07:52:23 -020040#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42#include "cx88.h"
Michael Krufky5e453dc2006-01-09 15:32:31 -020043#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
46MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
47MODULE_LICENSE("GPL");
48
49/* ------------------------------------------------------------------ */
50
51static unsigned int core_debug = 0;
52module_param(core_debug,int,0644);
53MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
54
Linus Torvalds1da177e2005-04-16 15:20:36 -070055static unsigned int nicam = 0;
56module_param(nicam,int,0644);
57MODULE_PARM_DESC(nicam,"tv audio is nicam");
58
59static unsigned int nocomb = 0;
60module_param(nocomb,int,0644);
61MODULE_PARM_DESC(nocomb,"disable comb filter");
62
63#define dprintk(level,fmt, arg...) if (core_debug >= level) \
64 printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
65
66static unsigned int cx88_devcount;
67static LIST_HEAD(cx88_devlist);
Ingo Molnar1e4baed2006-01-15 07:52:23 -020068static DEFINE_MUTEX(devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#define NO_SYNC_LINE (-1U)
71
72static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist,
73 unsigned int offset, u32 sync_line,
74 unsigned int bpl, unsigned int padding,
75 unsigned int lines)
76{
77 struct scatterlist *sg;
78 unsigned int line,todo;
79
80 /* sync instruction */
81 if (sync_line != NO_SYNC_LINE)
82 *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
83
84 /* scan lines */
85 sg = sglist;
86 for (line = 0; line < lines; line++) {
87 while (offset && offset >= sg_dma_len(sg)) {
88 offset -= sg_dma_len(sg);
89 sg++;
90 }
91 if (bpl <= sg_dma_len(sg)-offset) {
92 /* fits into current chunk */
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -080093 *(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL|RISC_EOL|bpl);
94 *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
95 offset+=bpl;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 } else {
Peter Naullsd1009bd2006-08-08 09:10:05 -030097 /* scanline needs to be split */
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -080098 todo = bpl;
99 *(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL|
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 (sg_dma_len(sg)-offset));
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800101 *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
102 todo -= (sg_dma_len(sg)-offset);
103 offset = 0;
104 sg++;
105 while (todo > sg_dma_len(sg)) {
Mauro Carvalho Chehabf2421ca2005-11-08 21:37:45 -0800106 *(rp++)=cpu_to_le32(RISC_WRITE|
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 sg_dma_len(sg));
Mauro Carvalho Chehabf2421ca2005-11-08 21:37:45 -0800108 *(rp++)=cpu_to_le32(sg_dma_address(sg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 todo -= sg_dma_len(sg);
110 sg++;
111 }
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800112 *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 *(rp++)=cpu_to_le32(sg_dma_address(sg));
114 offset += todo;
115 }
116 offset += padding;
117 }
118
119 return rp;
120}
121
122int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
123 struct scatterlist *sglist,
124 unsigned int top_offset, unsigned int bottom_offset,
125 unsigned int bpl, unsigned int padding, unsigned int lines)
126{
127 u32 instructions,fields;
128 u32 *rp;
129 int rc;
130
131 fields = 0;
132 if (UNSET != top_offset)
133 fields++;
134 if (UNSET != bottom_offset)
135 fields++;
136
137 /* estimate risc mem: worst case is one write per page border +
Duncan Sandsbba3ad72006-04-11 10:18:57 -0300138 one write per scan line + syncs + jump (all 2 dwords). Padding
139 can cause next bpl to start close to a page border. First DMA
140 region may be smaller than PAGE_SIZE */
141 instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines);
142 instructions += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
144 return rc;
145
146 /* write risc instructions */
147 rp = risc->cpu;
148 if (UNSET != top_offset)
149 rp = cx88_risc_field(rp, sglist, top_offset, 0,
150 bpl, padding, lines);
151 if (UNSET != bottom_offset)
152 rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200,
153 bpl, padding, lines);
154
155 /* save pointer to jmp instruction address */
156 risc->jmp = rp;
Duncan Sands4a287cf2006-02-27 00:09:48 -0300157 BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 return 0;
159}
160
161int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
162 struct scatterlist *sglist, unsigned int bpl,
163 unsigned int lines)
164{
165 u32 instructions;
166 u32 *rp;
167 int rc;
168
169 /* estimate risc mem: worst case is one write per page border +
Duncan Sandsbba3ad72006-04-11 10:18:57 -0300170 one write per scan line + syncs + jump (all 2 dwords). Here
171 there is no padding and no sync. First DMA region may be smaller
172 than PAGE_SIZE */
173 instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
174 instructions += 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
176 return rc;
177
178 /* write risc instructions */
179 rp = risc->cpu;
180 rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines);
181
182 /* save pointer to jmp instruction address */
183 risc->jmp = rp;
Duncan Sands4a287cf2006-02-27 00:09:48 -0300184 BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 return 0;
186}
187
188int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
189 u32 reg, u32 mask, u32 value)
190{
191 u32 *rp;
192 int rc;
193
194 if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0)
195 return rc;
196
197 /* write risc instructions */
198 rp = risc->cpu;
199 *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM);
200 *(rp++) = cpu_to_le32(reg);
201 *(rp++) = cpu_to_le32(value);
202 *(rp++) = cpu_to_le32(mask);
203 *(rp++) = cpu_to_le32(RISC_JUMP);
204 *(rp++) = cpu_to_le32(risc->dma);
205 return 0;
206}
207
208void
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300209cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210{
Eric Sesterhennae246012006-03-13 13:17:11 -0300211 BUG_ON(in_interrupt());
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 videobuf_waiton(&buf->vb,0,0);
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300213 videobuf_dma_unmap(q, &buf->vb.dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 videobuf_dma_free(&buf->vb.dma);
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300215 btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 buf->vb.state = STATE_NEEDS_INIT;
217}
218
219/* ------------------------------------------------------------------ */
220/* our SRAM memory layout */
221
222/* we are going to put all thr risc programs into host memory, so we
223 * can use the whole SDRAM for the DMA fifos. To simplify things, we
224 * use a static memory layout. That surely will waste memory in case
225 * we don't use all DMA channels at the same time (which will be the
226 * case most of the time). But that still gives us enougth FIFO space
227 * to be able to deal with insane long pci latencies ...
228 *
229 * FIFO space allocations:
230 * channel 21 (y video) - 10.0k
231 * channel 22 (u video) - 2.0k
232 * channel 23 (v video) - 2.0k
233 * channel 24 (vbi) - 4.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200234 * channels 25+26 (audio) - 4.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 * channel 28 (mpeg) - 4.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200236 * TOTAL = 29.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 *
238 * Every channel has 160 bytes control data (64 bytes instruction
239 * queue and 6 CDT entries), which is close to 2k total.
240 *
241 * Address layout:
242 * 0x0000 - 0x03ff CMDs / reserved
243 * 0x0400 - 0x0bff instruction queues + CDs
244 * 0x0c00 - FIFOs
245 */
246
247struct sram_channel cx88_sram_channels[] = {
248 [SRAM_CH21] = {
249 .name = "video y / packed",
250 .cmds_start = 0x180040,
251 .ctrl_start = 0x180400,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800252 .cdt = 0x180400 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 .fifo_start = 0x180c00,
254 .fifo_size = 0x002800,
255 .ptr1_reg = MO_DMA21_PTR1,
256 .ptr2_reg = MO_DMA21_PTR2,
257 .cnt1_reg = MO_DMA21_CNT1,
258 .cnt2_reg = MO_DMA21_CNT2,
259 },
260 [SRAM_CH22] = {
261 .name = "video u",
262 .cmds_start = 0x180080,
263 .ctrl_start = 0x1804a0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800264 .cdt = 0x1804a0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 .fifo_start = 0x183400,
266 .fifo_size = 0x000800,
267 .ptr1_reg = MO_DMA22_PTR1,
268 .ptr2_reg = MO_DMA22_PTR2,
269 .cnt1_reg = MO_DMA22_CNT1,
270 .cnt2_reg = MO_DMA22_CNT2,
271 },
272 [SRAM_CH23] = {
273 .name = "video v",
274 .cmds_start = 0x1800c0,
275 .ctrl_start = 0x180540,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800276 .cdt = 0x180540 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 .fifo_start = 0x183c00,
278 .fifo_size = 0x000800,
279 .ptr1_reg = MO_DMA23_PTR1,
280 .ptr2_reg = MO_DMA23_PTR2,
281 .cnt1_reg = MO_DMA23_CNT1,
282 .cnt2_reg = MO_DMA23_CNT2,
283 },
284 [SRAM_CH24] = {
285 .name = "vbi",
286 .cmds_start = 0x180100,
287 .ctrl_start = 0x1805e0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800288 .cdt = 0x1805e0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 .fifo_start = 0x184400,
290 .fifo_size = 0x001000,
291 .ptr1_reg = MO_DMA24_PTR1,
292 .ptr2_reg = MO_DMA24_PTR2,
293 .cnt1_reg = MO_DMA24_CNT1,
294 .cnt2_reg = MO_DMA24_CNT2,
295 },
296 [SRAM_CH25] = {
297 .name = "audio from",
298 .cmds_start = 0x180140,
299 .ctrl_start = 0x180680,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800300 .cdt = 0x180680 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 .fifo_start = 0x185400,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200302 .fifo_size = 0x001000,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 .ptr1_reg = MO_DMA25_PTR1,
304 .ptr2_reg = MO_DMA25_PTR2,
305 .cnt1_reg = MO_DMA25_CNT1,
306 .cnt2_reg = MO_DMA25_CNT2,
307 },
308 [SRAM_CH26] = {
309 .name = "audio to",
310 .cmds_start = 0x180180,
311 .ctrl_start = 0x180720,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800312 .cdt = 0x180680 + 64, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 .fifo_start = 0x185400, /* same as audio IN */
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200314 .fifo_size = 0x001000, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 .ptr1_reg = MO_DMA26_PTR1,
316 .ptr2_reg = MO_DMA26_PTR2,
317 .cnt1_reg = MO_DMA26_CNT1,
318 .cnt2_reg = MO_DMA26_CNT2,
319 },
320 [SRAM_CH28] = {
321 .name = "mpeg",
322 .cmds_start = 0x180200,
323 .ctrl_start = 0x1807C0,
324 .cdt = 0x1807C0 + 64,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200325 .fifo_start = 0x186400,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 .fifo_size = 0x001000,
327 .ptr1_reg = MO_DMA28_PTR1,
328 .ptr2_reg = MO_DMA28_PTR2,
329 .cnt1_reg = MO_DMA28_CNT1,
330 .cnt2_reg = MO_DMA28_CNT2,
331 },
332};
333
334int cx88_sram_channel_setup(struct cx88_core *core,
335 struct sram_channel *ch,
336 unsigned int bpl, u32 risc)
337{
338 unsigned int i,lines;
339 u32 cdt;
340
341 bpl = (bpl + 7) & ~7; /* alignment */
342 cdt = ch->cdt;
343 lines = ch->fifo_size / bpl;
344 if (lines > 6)
345 lines = 6;
346 BUG_ON(lines < 2);
347
348 /* write CDT */
349 for (i = 0; i < lines; i++)
350 cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
351
352 /* write CMDS */
353 cx_write(ch->cmds_start + 0, risc);
354 cx_write(ch->cmds_start + 4, cdt);
355 cx_write(ch->cmds_start + 8, (lines*16) >> 3);
356 cx_write(ch->cmds_start + 12, ch->ctrl_start);
357 cx_write(ch->cmds_start + 16, 64 >> 2);
358 for (i = 20; i < 64; i += 4)
359 cx_write(ch->cmds_start + i, 0);
360
361 /* fill registers */
362 cx_write(ch->ptr1_reg, ch->fifo_start);
363 cx_write(ch->ptr2_reg, cdt);
364 cx_write(ch->cnt1_reg, (bpl >> 3) -1);
365 cx_write(ch->cnt2_reg, (lines*16) >> 3);
366
367 dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
368 return 0;
369}
370
371/* ------------------------------------------------------------------ */
372/* debug helper code */
373
Peter Hagervallf9e7a022005-11-08 21:36:29 -0800374static int cx88_risc_decode(u32 risc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375{
376 static char *instr[16] = {
377 [ RISC_SYNC >> 28 ] = "sync",
378 [ RISC_WRITE >> 28 ] = "write",
379 [ RISC_WRITEC >> 28 ] = "writec",
380 [ RISC_READ >> 28 ] = "read",
381 [ RISC_READC >> 28 ] = "readc",
382 [ RISC_JUMP >> 28 ] = "jump",
383 [ RISC_SKIP >> 28 ] = "skip",
384 [ RISC_WRITERM >> 28 ] = "writerm",
385 [ RISC_WRITECM >> 28 ] = "writecm",
386 [ RISC_WRITECR >> 28 ] = "writecr",
387 };
388 static int incr[16] = {
389 [ RISC_WRITE >> 28 ] = 2,
390 [ RISC_JUMP >> 28 ] = 2,
391 [ RISC_WRITERM >> 28 ] = 3,
392 [ RISC_WRITECM >> 28 ] = 3,
393 [ RISC_WRITECR >> 28 ] = 4,
394 };
395 static char *bits[] = {
396 "12", "13", "14", "resync",
397 "cnt0", "cnt1", "18", "19",
398 "20", "21", "22", "23",
399 "irq1", "irq2", "eol", "sol",
400 };
401 int i;
402
403 printk("0x%08x [ %s", risc,
404 instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
405 for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
406 if (risc & (1 << (i + 12)))
407 printk(" %s",bits[i]);
408 printk(" count=%d ]\n", risc & 0xfff);
409 return incr[risc >> 28] ? incr[risc >> 28] : 1;
410}
411
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412
413void cx88_sram_channel_dump(struct cx88_core *core,
414 struct sram_channel *ch)
415{
416 static char *name[] = {
417 "initial risc",
418 "cdt base",
419 "cdt size",
420 "iq base",
421 "iq size",
422 "risc pc",
423 "iq wr ptr",
424 "iq rd ptr",
425 "cdt current",
426 "pci target",
427 "line / byte",
428 };
429 u32 risc;
430 unsigned int i,j,n;
431
432 printk("%s: %s - dma channel status dump\n",
433 core->name,ch->name);
434 for (i = 0; i < ARRAY_SIZE(name); i++)
435 printk("%s: cmds: %-12s: 0x%08x\n",
436 core->name,name[i],
437 cx_read(ch->cmds_start + 4*i));
438 for (i = 0; i < 4; i++) {
439 risc = cx_read(ch->cmds_start + 4 * (i+11));
440 printk("%s: risc%d: ", core->name, i);
441 cx88_risc_decode(risc);
442 }
443 for (i = 0; i < 16; i += n) {
444 risc = cx_read(ch->ctrl_start + 4 * i);
445 printk("%s: iq %x: ", core->name, i);
446 n = cx88_risc_decode(risc);
447 for (j = 1; j < n; j++) {
448 risc = cx_read(ch->ctrl_start + 4 * (i+j));
449 printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
450 core->name, i+j, risc, j);
451 }
452 }
453
454 printk("%s: fifo: 0x%08x -> 0x%x\n",
455 core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
456 printk("%s: ctrl: 0x%08x -> 0x%x\n",
457 core->name, ch->ctrl_start, ch->ctrl_start+6*16);
458 printk("%s: ptr1_reg: 0x%08x\n",
459 core->name,cx_read(ch->ptr1_reg));
460 printk("%s: ptr2_reg: 0x%08x\n",
461 core->name,cx_read(ch->ptr2_reg));
462 printk("%s: cnt1_reg: 0x%08x\n",
463 core->name,cx_read(ch->cnt1_reg));
464 printk("%s: cnt2_reg: 0x%08x\n",
465 core->name,cx_read(ch->cnt2_reg));
466}
467
Adrian Bunk408b6642005-05-01 08:59:29 -0700468static char *cx88_pci_irqs[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
470 "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
471 "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
472 "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
473};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474
475void cx88_print_irqbits(char *name, char *tag, char **strings,
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300476 int len, u32 bits, u32 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477{
478 unsigned int i;
479
480 printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300481 for (i = 0; i < len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 if (!(bits & (1 << i)))
483 continue;
484 if (strings[i])
485 printk(" %s", strings[i]);
486 else
487 printk(" %d", i);
488 if (!(mask & (1 << i)))
489 continue;
490 printk("*");
491 }
492 printk("\n");
493}
494
495/* ------------------------------------------------------------------ */
496
497int cx88_core_irq(struct cx88_core *core, u32 status)
498{
499 int handled = 0;
500
501 if (status & (1<<18)) {
502 cx88_ir_irq(core);
503 handled++;
504 }
505 if (!handled)
506 cx88_print_irqbits(core->name, "irq pci",
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300507 cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
508 status, core->pci_irqmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 return handled;
510}
511
512void cx88_wakeup(struct cx88_core *core,
513 struct cx88_dmaqueue *q, u32 count)
514{
515 struct cx88_buffer *buf;
516 int bc;
517
518 for (bc = 0;; bc++) {
519 if (list_empty(&q->active))
520 break;
521 buf = list_entry(q->active.next,
522 struct cx88_buffer, vb.queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 /* count comes from the hw and is is 16bit wide --
524 * this trick handles wrap-arounds correctly for
525 * up to 32767 buffers in flight... */
526 if ((s16) (count - buf->count) < 0)
527 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 do_gettimeofday(&buf->vb.ts);
529 dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
530 count, buf->count);
531 buf->vb.state = STATE_DONE;
532 list_del(&buf->vb.queue);
533 wake_up(&buf->vb.done);
534 }
535 if (list_empty(&q->active)) {
536 del_timer(&q->timeout);
537 } else {
538 mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
539 }
540 if (bc != 1)
541 printk("%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc);
542}
543
544void cx88_shutdown(struct cx88_core *core)
545{
546 /* disable RISC controller + IRQs */
547 cx_write(MO_DEV_CNTRL2, 0);
548
549 /* stop dma transfers */
550 cx_write(MO_VID_DMACNTRL, 0x0);
551 cx_write(MO_AUD_DMACNTRL, 0x0);
552 cx_write(MO_TS_DMACNTRL, 0x0);
553 cx_write(MO_VIP_DMACNTRL, 0x0);
554 cx_write(MO_GPHST_DMACNTRL, 0x0);
555
556 /* stop interrupts */
557 cx_write(MO_PCI_INTMSK, 0x0);
558 cx_write(MO_VID_INTMSK, 0x0);
559 cx_write(MO_AUD_INTMSK, 0x0);
560 cx_write(MO_TS_INTMSK, 0x0);
561 cx_write(MO_VIP_INTMSK, 0x0);
562 cx_write(MO_GPHST_INTMSK, 0x0);
563
564 /* stop capturing */
565 cx_write(VID_CAPTURE_CONTROL, 0);
566}
567
568int cx88_reset(struct cx88_core *core)
569{
570 dprintk(1,"%s\n",__FUNCTION__);
571 cx88_shutdown(core);
572
573 /* clear irq status */
574 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
575 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
576 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
577
578 /* wait a bit */
579 msleep(100);
580
581 /* init sram */
582 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
583 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
584 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
585 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
586 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
587 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
588 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
589
590 /* misc init ... */
591 cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
592 (1 << 12) | // agc gain
593 (1 << 11) | // adaptibe agc
594 (0 << 10) | // chroma agc
595 (0 << 9) | // ckillen
596 (7)));
597
598 /* setup image format */
599 cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
600
601 /* setup FIFO Threshholds */
602 cx_write(MO_PDMA_STHRSH, 0x0807);
603 cx_write(MO_PDMA_DTHRSH, 0x0807);
604
605 /* fixes flashing of image */
606 cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
607 cx_write(MO_AGC_BACK_VBI, 0x00E00555);
608
609 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
610 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
611 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
612
613 /* Reset on-board parts */
614 cx_write(MO_SRST_IO, 0);
615 msleep(10);
616 cx_write(MO_SRST_IO, 1);
617
618 return 0;
619}
620
621/* ------------------------------------------------------------------ */
622
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300623static unsigned int inline norm_swidth(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300625 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626}
627
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300628static unsigned int inline norm_hdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300630 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631}
632
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300633static unsigned int inline norm_vdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300635 return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636}
637
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300638static unsigned int inline norm_fsc8(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300640 if (norm & V4L2_STD_PAL_M)
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300641 return 28604892; // 3.575611 MHz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300643 if (norm & (V4L2_STD_PAL_Nc))
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300644 return 28656448; // 3.582056 MHz
645
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300646 if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300647 return 28636360; // 3.57954545 MHz +/- 10 Hz
648
649 /* SECAM have also different sub carrier for chroma,
650 but step_db and step_dr, at cx88_set_tvnorm already handles that.
651
652 The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
653 */
654
655 return 35468950; // 4.43361875 MHz +/- 5 Hz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656}
657
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300658static unsigned int inline norm_htotal(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659{
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700660
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300661 unsigned int fsc4=norm_fsc8(norm)/2;
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700662
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300663 /* returns 4*FSC / vtotal / frames per seconds */
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300664 return (norm & V4L2_STD_625_50) ?
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300665 ((fsc4+312)/625+12)/25 :
666 ((fsc4+262)/525*1001+15000)/30000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667}
668
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300669static unsigned int inline norm_vbipack(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300671 return (norm & V4L2_STD_625_50) ? 511 : 400;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672}
673
674int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
675 enum v4l2_field field)
676{
677 unsigned int swidth = norm_swidth(core->tvnorm);
678 unsigned int sheight = norm_maxh(core->tvnorm);
679 u32 value;
680
681 dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
682 V4L2_FIELD_HAS_TOP(field) ? "T" : "",
683 V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300684 v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685 if (!V4L2_FIELD_HAS_BOTH(field))
686 height *= 2;
687
688 // recalc H delay and scale registers
689 value = (width * norm_hdelay(core->tvnorm)) / swidth;
690 value &= 0x3fe;
691 cx_write(MO_HDELAY_EVEN, value);
692 cx_write(MO_HDELAY_ODD, value);
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300693 dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
695 value = (swidth * 4096 / width) - 4096;
696 cx_write(MO_HSCALE_EVEN, value);
697 cx_write(MO_HSCALE_ODD, value);
698 dprintk(1,"set_scale: hscale 0x%04x\n", value);
699
700 cx_write(MO_HACTIVE_EVEN, width);
701 cx_write(MO_HACTIVE_ODD, width);
702 dprintk(1,"set_scale: hactive 0x%04x\n", width);
703
704 // recalc V scale Register (delay is constant)
705 cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
706 cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
707 dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
708
709 value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
710 cx_write(MO_VSCALE_EVEN, value);
711 cx_write(MO_VSCALE_ODD, value);
712 dprintk(1,"set_scale: vscale 0x%04x\n", value);
713
714 cx_write(MO_VACTIVE_EVEN, sheight);
715 cx_write(MO_VACTIVE_ODD, sheight);
716 dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
717
718 // setup filters
719 value = 0;
720 value |= (1 << 19); // CFILT (default)
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300721 if (core->tvnorm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 value |= (1 << 15);
723 value |= (1 << 16);
724 }
Trent Piepho6a59d642007-08-15 14:41:57 -0300725 if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 value |= (1 << 13) | (1 << 5);
727 if (V4L2_FIELD_INTERLACED == field)
728 value |= (1 << 3); // VINT (interlaced vertical scaling)
729 if (width < 385)
730 value |= (1 << 0); // 3-tap interpolation
731 if (width < 193)
732 value |= (1 << 1); // 5-tap interpolation
733 if (nocomb)
734 value |= (3 << 5); // disable comb filter
735
736 cx_write(MO_FILTER_EVEN, value);
737 cx_write(MO_FILTER_ODD, value);
738 dprintk(1,"set_scale: filter 0x%04x\n", value);
739
740 return 0;
741}
742
743static const u32 xtal = 28636363;
744
745static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
746{
747 static u32 pre[] = { 0, 0, 0, 3, 2, 1 };
748 u64 pll;
749 u32 reg;
750 int i;
751
752 if (prescale < 2)
753 prescale = 2;
754 if (prescale > 5)
755 prescale = 5;
756
757 pll = ofreq * 8 * prescale * (u64)(1 << 20);
758 do_div(pll,xtal);
759 reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
760 if (((reg >> 20) & 0x3f) < 14) {
761 printk("%s/0: pll out of range\n",core->name);
762 return -1;
763 }
764
765 dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
766 reg, cx_read(MO_PLL_REG), ofreq);
767 cx_write(MO_PLL_REG, reg);
768 for (i = 0; i < 100; i++) {
769 reg = cx_read(MO_DEVICE_STATUS);
770 if (reg & (1<<2)) {
771 dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
772 prescale,ofreq);
773 return 0;
774 }
775 dprintk(1,"pll not locked yet, waiting ...\n");
776 msleep(10);
777 }
778 dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
779 return -1;
780}
781
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800782int cx88_start_audio_dma(struct cx88_core *core)
783{
Marcin Rudowski17801f52006-02-06 09:15:14 -0200784 /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
785 int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300786
787 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
788 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
789 return 0;
790
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800791 /* setup fifo + format */
Marcin Rudowski17801f52006-02-06 09:15:14 -0200792 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
793 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800794
Marcin Rudowski17801f52006-02-06 09:15:14 -0200795 cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
796 cx_write(MO_AUDR_LNGTH, bpl); /* fifo bpl size */
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800797
798 /* start dma */
799 cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300800
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800801 return 0;
802}
803
804int cx88_stop_audio_dma(struct cx88_core *core)
805{
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300806 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
807 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
808 return 0;
809
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800810 /* stop dma */
811 cx_write(MO_AUD_DMACNTRL, 0x0000);
812
813 return 0;
814}
815
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816static int set_tvaudio(struct cx88_core *core)
817{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300818 v4l2_std_id norm = core->tvnorm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Trent Piepho6a59d642007-08-15 14:41:57 -0300820 if (CX88_VMUX_TELEVISION != INPUT(core->input).type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 return 0;
822
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300823 if (V4L2_STD_PAL_BG & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800824 core->tvaudio = WW_BG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700825
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300826 } else if (V4L2_STD_PAL_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800827 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300829 } else if (V4L2_STD_PAL_I & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800830 core->tvaudio = WW_I;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300832 } else if (V4L2_STD_SECAM_L & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800833 core->tvaudio = WW_L;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300835 } else if (V4L2_STD_SECAM_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800836 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300838 } else if ((V4L2_STD_NTSC_M & norm) ||
839 (V4L2_STD_PAL_M & norm)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 core->tvaudio = WW_BTSC;
841
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300842 } else if (V4L2_STD_NTSC_M_JP & norm) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 core->tvaudio = WW_EIAJ;
844
845 } else {
846 printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300847 core->name, v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 core->tvaudio = 0;
849 return 0;
850 }
851
852 cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
853 cx88_set_tvaudio(core);
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -0700854 /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800856/*
857 This should be needed only on cx88-alsa. It seems that some cx88 chips have
858 bugs and does require DMA enabled for it to work.
859 */
860 cx88_start_audio_dma(core);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 return 0;
862}
863
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800864
865
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300866int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867{
868 u32 fsc8;
869 u32 adc_clock;
870 u32 vdec_clock;
871 u32 step_db,step_dr;
872 u64 tmp64;
873 u32 bdelay,agcdelay,htotal;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300874 u32 cxiformat, cxoformat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875
876 core->tvnorm = norm;
877 fsc8 = norm_fsc8(norm);
878 adc_clock = xtal;
879 vdec_clock = fsc8;
880 step_db = fsc8;
881 step_dr = fsc8;
882
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300883 if (norm & V4L2_STD_NTSC_M_JP) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300884 cxiformat = VideoFormatNTSCJapan;
885 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300886 } else if (norm & V4L2_STD_NTSC_443) {
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300887 cxiformat = VideoFormatNTSC443;
888 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300889 } else if (norm & V4L2_STD_PAL_M) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300890 cxiformat = VideoFormatPALM;
891 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300892 } else if (norm & V4L2_STD_PAL_N) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300893 cxiformat = VideoFormatPALN;
894 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300895 } else if (norm & V4L2_STD_PAL_Nc) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300896 cxiformat = VideoFormatPALNC;
897 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300898 } else if (norm & V4L2_STD_PAL_60) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300899 cxiformat = VideoFormatPAL60;
900 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300901 } else if (norm & V4L2_STD_NTSC) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300902 cxiformat = VideoFormatNTSC;
903 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300904 } else if (norm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 step_db = 4250000 * 8;
906 step_dr = 4406250 * 8;
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300907
908 cxiformat = VideoFormatSECAM;
909 cxoformat = 0x181f0008;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300910 } else { /* PAL */
911 cxiformat = VideoFormatPAL;
912 cxoformat = 0x181f0008;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 }
914
915 dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300916 v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
917 step_db, step_dr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 set_pll(core,2,vdec_clock);
919
920 dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300921 cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
922 cx_andor(MO_INPUT_FORMAT, 0xf, cxiformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 // FIXME: as-is from DScaler
925 dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300926 cxoformat, cx_read(MO_OUTPUT_FORMAT));
927 cx_write(MO_OUTPUT_FORMAT, cxoformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
929 // MO_SCONV_REG = adc clock / video dec clock * 2^17
930 tmp64 = adc_clock * (u64)(1 << 17);
931 do_div(tmp64, vdec_clock);
932 dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
933 (u32)tmp64, cx_read(MO_SCONV_REG));
934 cx_write(MO_SCONV_REG, (u32)tmp64);
935
936 // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
937 tmp64 = step_db * (u64)(1 << 22);
938 do_div(tmp64, vdec_clock);
939 dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
940 (u32)tmp64, cx_read(MO_SUB_STEP));
941 cx_write(MO_SUB_STEP, (u32)tmp64);
942
943 // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
944 tmp64 = step_dr * (u64)(1 << 22);
945 do_div(tmp64, vdec_clock);
946 dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
947 (u32)tmp64, cx_read(MO_SUB_STEP_DR));
948 cx_write(MO_SUB_STEP_DR, (u32)tmp64);
949
950 // bdelay + agcdelay
951 bdelay = vdec_clock * 65 / 20000000 + 21;
952 agcdelay = vdec_clock * 68 / 20000000 + 15;
953 dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
954 (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
955 cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
956
957 // htotal
958 tmp64 = norm_htotal(norm) * (u64)vdec_clock;
959 do_div(tmp64, fsc8);
Mauro Carvalho Chehabccbf64b2006-09-29 12:39:36 -0300960 htotal = (u32)tmp64 | (HLNotchFilter4xFsc << 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
962 htotal, cx_read(MO_HTOTAL), (u32)tmp64);
963 cx_write(MO_HTOTAL, htotal);
964
Trent Piepho3eb73172006-05-23 23:54:44 -0300965 // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
966 // the effective vbi offset ~244 samples, the same as the Bt8x8
967 cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
969 // this is needed as well to set all tvnorm parameter
970 cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
971
972 // audio
973 set_tvaudio(core);
974
975 // tell i2c chips
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300976 cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
978 // done
979 return 0;
980}
981
982/* ------------------------------------------------------------------ */
983
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984struct video_device *cx88_vdev_init(struct cx88_core *core,
985 struct pci_dev *pci,
986 struct video_device *template,
987 char *type)
988{
989 struct video_device *vfd;
990
991 vfd = video_device_alloc();
992 if (NULL == vfd)
993 return NULL;
994 *vfd = *template;
995 vfd->minor = -1;
996 vfd->dev = &pci->dev;
997 vfd->release = video_device_release;
998 snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
Trent Piepho6a59d642007-08-15 14:41:57 -0300999 core->name, type, core->board.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 return vfd;
1001}
1002
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003struct cx88_core* cx88_core_get(struct pci_dev *pci)
1004{
1005 struct cx88_core *core;
1006 struct list_head *item;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001008 mutex_lock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009 list_for_each(item,&cx88_devlist) {
1010 core = list_entry(item, struct cx88_core, devlist);
1011 if (pci->bus->number != core->pci_bus)
1012 continue;
1013 if (PCI_SLOT(pci->devfn) != core->pci_slot)
1014 continue;
1015
Trent Piephobbc83592007-08-15 14:41:58 -03001016 if (0 != cx88_get_resources(core, pci)) {
1017 mutex_unlock(&devlist);
1018 return NULL;
1019 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 atomic_inc(&core->refcount);
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001021 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 return core;
1023 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024
Trent Piephobbc83592007-08-15 14:41:58 -03001025 core = cx88_core_create(pci, cx88_devcount);
1026 if (NULL != core) {
1027 cx88_devcount++;
1028 list_add_tail(&core->devlist, &cx88_devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001031 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 return core;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033}
1034
1035void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
1036{
1037 release_mem_region(pci_resource_start(pci,0),
1038 pci_resource_len(pci,0));
1039
1040 if (!atomic_dec_and_test(&core->refcount))
1041 return;
1042
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001043 mutex_lock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 cx88_ir_fini(core);
1045 if (0 == core->i2c_rc)
Jean Delvare32697112006-12-10 21:21:33 +01001046 i2c_del_adapter(&core->i2c_adap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 list_del(&core->devlist);
1048 iounmap(core->lmmio);
1049 cx88_devcount--;
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001050 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 kfree(core);
1052}
1053
1054/* ------------------------------------------------------------------ */
1055
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056EXPORT_SYMBOL(cx88_print_irqbits);
1057
1058EXPORT_SYMBOL(cx88_core_irq);
1059EXPORT_SYMBOL(cx88_wakeup);
1060EXPORT_SYMBOL(cx88_reset);
1061EXPORT_SYMBOL(cx88_shutdown);
1062
1063EXPORT_SYMBOL(cx88_risc_buffer);
1064EXPORT_SYMBOL(cx88_risc_databuffer);
1065EXPORT_SYMBOL(cx88_risc_stopper);
1066EXPORT_SYMBOL(cx88_free_buffer);
1067
1068EXPORT_SYMBOL(cx88_sram_channels);
1069EXPORT_SYMBOL(cx88_sram_channel_setup);
1070EXPORT_SYMBOL(cx88_sram_channel_dump);
1071
1072EXPORT_SYMBOL(cx88_set_tvnorm);
1073EXPORT_SYMBOL(cx88_set_scale);
1074
1075EXPORT_SYMBOL(cx88_vdev_init);
1076EXPORT_SYMBOL(cx88_core_get);
1077EXPORT_SYMBOL(cx88_core_put);
1078
1079/*
1080 * Local variables:
1081 * c-basic-offset: 8
1082 * End:
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -07001083 * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 */