blob: ce7f1f0ae054ff94282aa2423555e3de5622ef9c [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/kernel.h>
32#include <linux/slab.h>
33#include <linux/kmod.h>
34#include <linux/sound.h>
35#include <linux/interrupt.h>
36#include <linux/pci.h>
37#include <linux/delay.h>
Mauro Carvalho Chehab98f30ed2005-11-08 21:37:17 -080038#include <linux/videodev2.h>
Ingo Molnar1e4baed2006-01-15 07:52:23 -020039#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
41#include "cx88.h"
Michael Krufky5e453dc2006-01-09 15:32:31 -020042#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
45MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
46MODULE_LICENSE("GPL");
47
48/* ------------------------------------------------------------------ */
49
50static unsigned int core_debug = 0;
51module_param(core_debug,int,0644);
52MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
53
Linus Torvalds1da177e2005-04-16 15:20:36 -070054static unsigned int nicam = 0;
55module_param(nicam,int,0644);
56MODULE_PARM_DESC(nicam,"tv audio is nicam");
57
58static unsigned int nocomb = 0;
59module_param(nocomb,int,0644);
60MODULE_PARM_DESC(nocomb,"disable comb filter");
61
62#define dprintk(level,fmt, arg...) if (core_debug >= level) \
63 printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
64
65static unsigned int cx88_devcount;
66static LIST_HEAD(cx88_devlist);
Ingo Molnar1e4baed2006-01-15 07:52:23 -020067static DEFINE_MUTEX(devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Linus Torvalds1da177e2005-04-16 15:20:36 -070069#define NO_SYNC_LINE (-1U)
70
71static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist,
72 unsigned int offset, u32 sync_line,
73 unsigned int bpl, unsigned int padding,
74 unsigned int lines)
75{
76 struct scatterlist *sg;
77 unsigned int line,todo;
78
79 /* sync instruction */
80 if (sync_line != NO_SYNC_LINE)
81 *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
82
83 /* scan lines */
84 sg = sglist;
85 for (line = 0; line < lines; line++) {
86 while (offset && offset >= sg_dma_len(sg)) {
87 offset -= sg_dma_len(sg);
88 sg++;
89 }
90 if (bpl <= sg_dma_len(sg)-offset) {
91 /* fits into current chunk */
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -080092 *(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL|RISC_EOL|bpl);
93 *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
94 offset+=bpl;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 } else {
Peter Naullsd1009bd2006-08-08 09:10:05 -030096 /* scanline needs to be split */
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -080097 todo = bpl;
98 *(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL|
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 (sg_dma_len(sg)-offset));
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800100 *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
101 todo -= (sg_dma_len(sg)-offset);
102 offset = 0;
103 sg++;
104 while (todo > sg_dma_len(sg)) {
Mauro Carvalho Chehabf2421ca2005-11-08 21:37:45 -0800105 *(rp++)=cpu_to_le32(RISC_WRITE|
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 sg_dma_len(sg));
Mauro Carvalho Chehabf2421ca2005-11-08 21:37:45 -0800107 *(rp++)=cpu_to_le32(sg_dma_address(sg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 todo -= sg_dma_len(sg);
109 sg++;
110 }
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800111 *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 *(rp++)=cpu_to_le32(sg_dma_address(sg));
113 offset += todo;
114 }
115 offset += padding;
116 }
117
118 return rp;
119}
120
121int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
122 struct scatterlist *sglist,
123 unsigned int top_offset, unsigned int bottom_offset,
124 unsigned int bpl, unsigned int padding, unsigned int lines)
125{
126 u32 instructions,fields;
127 u32 *rp;
128 int rc;
129
130 fields = 0;
131 if (UNSET != top_offset)
132 fields++;
133 if (UNSET != bottom_offset)
134 fields++;
135
136 /* estimate risc mem: worst case is one write per page border +
Duncan Sandsbba3ad72006-04-11 10:18:57 -0300137 one write per scan line + syncs + jump (all 2 dwords). Padding
138 can cause next bpl to start close to a page border. First DMA
139 region may be smaller than PAGE_SIZE */
140 instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines);
141 instructions += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
143 return rc;
144
145 /* write risc instructions */
146 rp = risc->cpu;
147 if (UNSET != top_offset)
148 rp = cx88_risc_field(rp, sglist, top_offset, 0,
149 bpl, padding, lines);
150 if (UNSET != bottom_offset)
151 rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200,
152 bpl, padding, lines);
153
154 /* save pointer to jmp instruction address */
155 risc->jmp = rp;
Duncan Sands4a287cf2006-02-27 00:09:48 -0300156 BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 return 0;
158}
159
160int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
161 struct scatterlist *sglist, unsigned int bpl,
162 unsigned int lines)
163{
164 u32 instructions;
165 u32 *rp;
166 int rc;
167
168 /* estimate risc mem: worst case is one write per page border +
Duncan Sandsbba3ad72006-04-11 10:18:57 -0300169 one write per scan line + syncs + jump (all 2 dwords). Here
170 there is no padding and no sync. First DMA region may be smaller
171 than PAGE_SIZE */
172 instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
173 instructions += 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
175 return rc;
176
177 /* write risc instructions */
178 rp = risc->cpu;
179 rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines);
180
181 /* save pointer to jmp instruction address */
182 risc->jmp = rp;
Duncan Sands4a287cf2006-02-27 00:09:48 -0300183 BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 return 0;
185}
186
187int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
188 u32 reg, u32 mask, u32 value)
189{
190 u32 *rp;
191 int rc;
192
193 if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0)
194 return rc;
195
196 /* write risc instructions */
197 rp = risc->cpu;
198 *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM);
199 *(rp++) = cpu_to_le32(reg);
200 *(rp++) = cpu_to_le32(value);
201 *(rp++) = cpu_to_le32(mask);
202 *(rp++) = cpu_to_le32(RISC_JUMP);
203 *(rp++) = cpu_to_le32(risc->dma);
204 return 0;
205}
206
207void
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300208cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
Eric Sesterhennae246012006-03-13 13:17:11 -0300210 BUG_ON(in_interrupt());
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 videobuf_waiton(&buf->vb,0,0);
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300212 videobuf_dma_unmap(q, &buf->vb.dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 videobuf_dma_free(&buf->vb.dma);
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300214 btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 buf->vb.state = STATE_NEEDS_INIT;
216}
217
218/* ------------------------------------------------------------------ */
219/* our SRAM memory layout */
220
221/* we are going to put all thr risc programs into host memory, so we
222 * can use the whole SDRAM for the DMA fifos. To simplify things, we
223 * use a static memory layout. That surely will waste memory in case
224 * we don't use all DMA channels at the same time (which will be the
225 * case most of the time). But that still gives us enougth FIFO space
226 * to be able to deal with insane long pci latencies ...
227 *
228 * FIFO space allocations:
229 * channel 21 (y video) - 10.0k
230 * channel 22 (u video) - 2.0k
231 * channel 23 (v video) - 2.0k
232 * channel 24 (vbi) - 4.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200233 * channels 25+26 (audio) - 4.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 * channel 28 (mpeg) - 4.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200235 * TOTAL = 29.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 *
237 * Every channel has 160 bytes control data (64 bytes instruction
238 * queue and 6 CDT entries), which is close to 2k total.
239 *
240 * Address layout:
241 * 0x0000 - 0x03ff CMDs / reserved
242 * 0x0400 - 0x0bff instruction queues + CDs
243 * 0x0c00 - FIFOs
244 */
245
246struct sram_channel cx88_sram_channels[] = {
247 [SRAM_CH21] = {
248 .name = "video y / packed",
249 .cmds_start = 0x180040,
250 .ctrl_start = 0x180400,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800251 .cdt = 0x180400 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 .fifo_start = 0x180c00,
253 .fifo_size = 0x002800,
254 .ptr1_reg = MO_DMA21_PTR1,
255 .ptr2_reg = MO_DMA21_PTR2,
256 .cnt1_reg = MO_DMA21_CNT1,
257 .cnt2_reg = MO_DMA21_CNT2,
258 },
259 [SRAM_CH22] = {
260 .name = "video u",
261 .cmds_start = 0x180080,
262 .ctrl_start = 0x1804a0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800263 .cdt = 0x1804a0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 .fifo_start = 0x183400,
265 .fifo_size = 0x000800,
266 .ptr1_reg = MO_DMA22_PTR1,
267 .ptr2_reg = MO_DMA22_PTR2,
268 .cnt1_reg = MO_DMA22_CNT1,
269 .cnt2_reg = MO_DMA22_CNT2,
270 },
271 [SRAM_CH23] = {
272 .name = "video v",
273 .cmds_start = 0x1800c0,
274 .ctrl_start = 0x180540,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800275 .cdt = 0x180540 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 .fifo_start = 0x183c00,
277 .fifo_size = 0x000800,
278 .ptr1_reg = MO_DMA23_PTR1,
279 .ptr2_reg = MO_DMA23_PTR2,
280 .cnt1_reg = MO_DMA23_CNT1,
281 .cnt2_reg = MO_DMA23_CNT2,
282 },
283 [SRAM_CH24] = {
284 .name = "vbi",
285 .cmds_start = 0x180100,
286 .ctrl_start = 0x1805e0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800287 .cdt = 0x1805e0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 .fifo_start = 0x184400,
289 .fifo_size = 0x001000,
290 .ptr1_reg = MO_DMA24_PTR1,
291 .ptr2_reg = MO_DMA24_PTR2,
292 .cnt1_reg = MO_DMA24_CNT1,
293 .cnt2_reg = MO_DMA24_CNT2,
294 },
295 [SRAM_CH25] = {
296 .name = "audio from",
297 .cmds_start = 0x180140,
298 .ctrl_start = 0x180680,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800299 .cdt = 0x180680 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 .fifo_start = 0x185400,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200301 .fifo_size = 0x001000,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 .ptr1_reg = MO_DMA25_PTR1,
303 .ptr2_reg = MO_DMA25_PTR2,
304 .cnt1_reg = MO_DMA25_CNT1,
305 .cnt2_reg = MO_DMA25_CNT2,
306 },
307 [SRAM_CH26] = {
308 .name = "audio to",
309 .cmds_start = 0x180180,
310 .ctrl_start = 0x180720,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800311 .cdt = 0x180680 + 64, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 .fifo_start = 0x185400, /* same as audio IN */
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200313 .fifo_size = 0x001000, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 .ptr1_reg = MO_DMA26_PTR1,
315 .ptr2_reg = MO_DMA26_PTR2,
316 .cnt1_reg = MO_DMA26_CNT1,
317 .cnt2_reg = MO_DMA26_CNT2,
318 },
319 [SRAM_CH28] = {
320 .name = "mpeg",
321 .cmds_start = 0x180200,
322 .ctrl_start = 0x1807C0,
323 .cdt = 0x1807C0 + 64,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200324 .fifo_start = 0x186400,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 .fifo_size = 0x001000,
326 .ptr1_reg = MO_DMA28_PTR1,
327 .ptr2_reg = MO_DMA28_PTR2,
328 .cnt1_reg = MO_DMA28_CNT1,
329 .cnt2_reg = MO_DMA28_CNT2,
330 },
331};
332
333int cx88_sram_channel_setup(struct cx88_core *core,
334 struct sram_channel *ch,
335 unsigned int bpl, u32 risc)
336{
337 unsigned int i,lines;
338 u32 cdt;
339
340 bpl = (bpl + 7) & ~7; /* alignment */
341 cdt = ch->cdt;
342 lines = ch->fifo_size / bpl;
343 if (lines > 6)
344 lines = 6;
345 BUG_ON(lines < 2);
346
347 /* write CDT */
348 for (i = 0; i < lines; i++)
349 cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
350
351 /* write CMDS */
352 cx_write(ch->cmds_start + 0, risc);
353 cx_write(ch->cmds_start + 4, cdt);
354 cx_write(ch->cmds_start + 8, (lines*16) >> 3);
355 cx_write(ch->cmds_start + 12, ch->ctrl_start);
356 cx_write(ch->cmds_start + 16, 64 >> 2);
357 for (i = 20; i < 64; i += 4)
358 cx_write(ch->cmds_start + i, 0);
359
360 /* fill registers */
361 cx_write(ch->ptr1_reg, ch->fifo_start);
362 cx_write(ch->ptr2_reg, cdt);
363 cx_write(ch->cnt1_reg, (bpl >> 3) -1);
364 cx_write(ch->cnt2_reg, (lines*16) >> 3);
365
366 dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
367 return 0;
368}
369
370/* ------------------------------------------------------------------ */
371/* debug helper code */
372
Peter Hagervallf9e7a022005-11-08 21:36:29 -0800373static int cx88_risc_decode(u32 risc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374{
375 static char *instr[16] = {
376 [ RISC_SYNC >> 28 ] = "sync",
377 [ RISC_WRITE >> 28 ] = "write",
378 [ RISC_WRITEC >> 28 ] = "writec",
379 [ RISC_READ >> 28 ] = "read",
380 [ RISC_READC >> 28 ] = "readc",
381 [ RISC_JUMP >> 28 ] = "jump",
382 [ RISC_SKIP >> 28 ] = "skip",
383 [ RISC_WRITERM >> 28 ] = "writerm",
384 [ RISC_WRITECM >> 28 ] = "writecm",
385 [ RISC_WRITECR >> 28 ] = "writecr",
386 };
387 static int incr[16] = {
388 [ RISC_WRITE >> 28 ] = 2,
389 [ RISC_JUMP >> 28 ] = 2,
390 [ RISC_WRITERM >> 28 ] = 3,
391 [ RISC_WRITECM >> 28 ] = 3,
392 [ RISC_WRITECR >> 28 ] = 4,
393 };
394 static char *bits[] = {
395 "12", "13", "14", "resync",
396 "cnt0", "cnt1", "18", "19",
397 "20", "21", "22", "23",
398 "irq1", "irq2", "eol", "sol",
399 };
400 int i;
401
402 printk("0x%08x [ %s", risc,
403 instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
404 for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
405 if (risc & (1 << (i + 12)))
406 printk(" %s",bits[i]);
407 printk(" count=%d ]\n", risc & 0xfff);
408 return incr[risc >> 28] ? incr[risc >> 28] : 1;
409}
410
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
412void cx88_sram_channel_dump(struct cx88_core *core,
413 struct sram_channel *ch)
414{
415 static char *name[] = {
416 "initial risc",
417 "cdt base",
418 "cdt size",
419 "iq base",
420 "iq size",
421 "risc pc",
422 "iq wr ptr",
423 "iq rd ptr",
424 "cdt current",
425 "pci target",
426 "line / byte",
427 };
428 u32 risc;
429 unsigned int i,j,n;
430
431 printk("%s: %s - dma channel status dump\n",
432 core->name,ch->name);
433 for (i = 0; i < ARRAY_SIZE(name); i++)
434 printk("%s: cmds: %-12s: 0x%08x\n",
435 core->name,name[i],
436 cx_read(ch->cmds_start + 4*i));
Trent Piepho16cf1d02007-08-21 08:19:16 -0300437 for (n = 1, i = 0; i < 4; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438 risc = cx_read(ch->cmds_start + 4 * (i+11));
439 printk("%s: risc%d: ", core->name, i);
Trent Piepho16cf1d02007-08-21 08:19:16 -0300440 if (--n)
441 printk("0x%08x [ arg #%d ]\n", risc, n);
442 else
443 n = cx88_risc_decode(risc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 }
445 for (i = 0; i < 16; i += n) {
446 risc = cx_read(ch->ctrl_start + 4 * i);
447 printk("%s: iq %x: ", core->name, i);
448 n = cx88_risc_decode(risc);
449 for (j = 1; j < n; j++) {
450 risc = cx_read(ch->ctrl_start + 4 * (i+j));
451 printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
452 core->name, i+j, risc, j);
453 }
454 }
455
456 printk("%s: fifo: 0x%08x -> 0x%x\n",
457 core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
458 printk("%s: ctrl: 0x%08x -> 0x%x\n",
459 core->name, ch->ctrl_start, ch->ctrl_start+6*16);
460 printk("%s: ptr1_reg: 0x%08x\n",
461 core->name,cx_read(ch->ptr1_reg));
462 printk("%s: ptr2_reg: 0x%08x\n",
463 core->name,cx_read(ch->ptr2_reg));
464 printk("%s: cnt1_reg: 0x%08x\n",
465 core->name,cx_read(ch->cnt1_reg));
466 printk("%s: cnt2_reg: 0x%08x\n",
467 core->name,cx_read(ch->cnt2_reg));
468}
469
Adrian Bunk408b6642005-05-01 08:59:29 -0700470static char *cx88_pci_irqs[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
472 "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
473 "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
474 "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
475};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476
477void cx88_print_irqbits(char *name, char *tag, char **strings,
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300478 int len, u32 bits, u32 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479{
480 unsigned int i;
481
482 printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300483 for (i = 0; i < len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 if (!(bits & (1 << i)))
485 continue;
486 if (strings[i])
487 printk(" %s", strings[i]);
488 else
489 printk(" %d", i);
490 if (!(mask & (1 << i)))
491 continue;
492 printk("*");
493 }
494 printk("\n");
495}
496
497/* ------------------------------------------------------------------ */
498
499int cx88_core_irq(struct cx88_core *core, u32 status)
500{
501 int handled = 0;
502
Trent Piepho8ddac9e2007-08-18 06:57:55 -0300503 if (status & PCI_INT_IR_SMPINT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 cx88_ir_irq(core);
505 handled++;
506 }
507 if (!handled)
508 cx88_print_irqbits(core->name, "irq pci",
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300509 cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
510 status, core->pci_irqmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 return handled;
512}
513
514void cx88_wakeup(struct cx88_core *core,
515 struct cx88_dmaqueue *q, u32 count)
516{
517 struct cx88_buffer *buf;
518 int bc;
519
520 for (bc = 0;; bc++) {
521 if (list_empty(&q->active))
522 break;
523 buf = list_entry(q->active.next,
524 struct cx88_buffer, vb.queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 /* count comes from the hw and is is 16bit wide --
526 * this trick handles wrap-arounds correctly for
527 * up to 32767 buffers in flight... */
528 if ((s16) (count - buf->count) < 0)
529 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 do_gettimeofday(&buf->vb.ts);
531 dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
532 count, buf->count);
533 buf->vb.state = STATE_DONE;
534 list_del(&buf->vb.queue);
535 wake_up(&buf->vb.done);
536 }
537 if (list_empty(&q->active)) {
538 del_timer(&q->timeout);
539 } else {
540 mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
541 }
542 if (bc != 1)
543 printk("%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc);
544}
545
546void cx88_shutdown(struct cx88_core *core)
547{
548 /* disable RISC controller + IRQs */
549 cx_write(MO_DEV_CNTRL2, 0);
550
551 /* stop dma transfers */
552 cx_write(MO_VID_DMACNTRL, 0x0);
553 cx_write(MO_AUD_DMACNTRL, 0x0);
554 cx_write(MO_TS_DMACNTRL, 0x0);
555 cx_write(MO_VIP_DMACNTRL, 0x0);
556 cx_write(MO_GPHST_DMACNTRL, 0x0);
557
558 /* stop interrupts */
559 cx_write(MO_PCI_INTMSK, 0x0);
560 cx_write(MO_VID_INTMSK, 0x0);
561 cx_write(MO_AUD_INTMSK, 0x0);
562 cx_write(MO_TS_INTMSK, 0x0);
563 cx_write(MO_VIP_INTMSK, 0x0);
564 cx_write(MO_GPHST_INTMSK, 0x0);
565
566 /* stop capturing */
567 cx_write(VID_CAPTURE_CONTROL, 0);
568}
569
570int cx88_reset(struct cx88_core *core)
571{
572 dprintk(1,"%s\n",__FUNCTION__);
573 cx88_shutdown(core);
574
575 /* clear irq status */
576 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
577 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
578 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
579
580 /* wait a bit */
581 msleep(100);
582
583 /* init sram */
584 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
585 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
586 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
587 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
588 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
589 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
590 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
591
592 /* misc init ... */
593 cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
594 (1 << 12) | // agc gain
595 (1 << 11) | // adaptibe agc
596 (0 << 10) | // chroma agc
597 (0 << 9) | // ckillen
598 (7)));
599
600 /* setup image format */
601 cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
602
603 /* setup FIFO Threshholds */
604 cx_write(MO_PDMA_STHRSH, 0x0807);
605 cx_write(MO_PDMA_DTHRSH, 0x0807);
606
607 /* fixes flashing of image */
608 cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
609 cx_write(MO_AGC_BACK_VBI, 0x00E00555);
610
611 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
612 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
613 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
614
615 /* Reset on-board parts */
616 cx_write(MO_SRST_IO, 0);
617 msleep(10);
618 cx_write(MO_SRST_IO, 1);
619
620 return 0;
621}
622
623/* ------------------------------------------------------------------ */
624
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300625static unsigned int inline norm_swidth(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300627 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628}
629
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300630static unsigned int inline norm_hdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300632 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633}
634
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300635static unsigned int inline norm_vdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300637 return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638}
639
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300640static unsigned int inline norm_fsc8(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300642 if (norm & V4L2_STD_PAL_M)
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300643 return 28604892; // 3.575611 MHz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300645 if (norm & (V4L2_STD_PAL_Nc))
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300646 return 28656448; // 3.582056 MHz
647
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300648 if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300649 return 28636360; // 3.57954545 MHz +/- 10 Hz
650
651 /* SECAM have also different sub carrier for chroma,
652 but step_db and step_dr, at cx88_set_tvnorm already handles that.
653
654 The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
655 */
656
657 return 35468950; // 4.43361875 MHz +/- 5 Hz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658}
659
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300660static unsigned int inline norm_htotal(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661{
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700662
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300663 unsigned int fsc4=norm_fsc8(norm)/2;
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700664
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300665 /* returns 4*FSC / vtotal / frames per seconds */
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300666 return (norm & V4L2_STD_625_50) ?
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300667 ((fsc4+312)/625+12)/25 :
668 ((fsc4+262)/525*1001+15000)/30000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669}
670
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300671static unsigned int inline norm_vbipack(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700672{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300673 return (norm & V4L2_STD_625_50) ? 511 : 400;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674}
675
676int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
677 enum v4l2_field field)
678{
679 unsigned int swidth = norm_swidth(core->tvnorm);
680 unsigned int sheight = norm_maxh(core->tvnorm);
681 u32 value;
682
683 dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
684 V4L2_FIELD_HAS_TOP(field) ? "T" : "",
685 V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300686 v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687 if (!V4L2_FIELD_HAS_BOTH(field))
688 height *= 2;
689
690 // recalc H delay and scale registers
691 value = (width * norm_hdelay(core->tvnorm)) / swidth;
692 value &= 0x3fe;
693 cx_write(MO_HDELAY_EVEN, value);
694 cx_write(MO_HDELAY_ODD, value);
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300695 dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696
697 value = (swidth * 4096 / width) - 4096;
698 cx_write(MO_HSCALE_EVEN, value);
699 cx_write(MO_HSCALE_ODD, value);
700 dprintk(1,"set_scale: hscale 0x%04x\n", value);
701
702 cx_write(MO_HACTIVE_EVEN, width);
703 cx_write(MO_HACTIVE_ODD, width);
704 dprintk(1,"set_scale: hactive 0x%04x\n", width);
705
706 // recalc V scale Register (delay is constant)
707 cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
708 cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
709 dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
710
711 value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
712 cx_write(MO_VSCALE_EVEN, value);
713 cx_write(MO_VSCALE_ODD, value);
714 dprintk(1,"set_scale: vscale 0x%04x\n", value);
715
716 cx_write(MO_VACTIVE_EVEN, sheight);
717 cx_write(MO_VACTIVE_ODD, sheight);
718 dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
719
720 // setup filters
721 value = 0;
722 value |= (1 << 19); // CFILT (default)
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300723 if (core->tvnorm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 value |= (1 << 15);
725 value |= (1 << 16);
726 }
Trent Piepho6a59d642007-08-15 14:41:57 -0300727 if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 value |= (1 << 13) | (1 << 5);
729 if (V4L2_FIELD_INTERLACED == field)
730 value |= (1 << 3); // VINT (interlaced vertical scaling)
731 if (width < 385)
732 value |= (1 << 0); // 3-tap interpolation
733 if (width < 193)
734 value |= (1 << 1); // 5-tap interpolation
735 if (nocomb)
736 value |= (3 << 5); // disable comb filter
737
738 cx_write(MO_FILTER_EVEN, value);
739 cx_write(MO_FILTER_ODD, value);
740 dprintk(1,"set_scale: filter 0x%04x\n", value);
741
742 return 0;
743}
744
745static const u32 xtal = 28636363;
746
747static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
748{
749 static u32 pre[] = { 0, 0, 0, 3, 2, 1 };
750 u64 pll;
751 u32 reg;
752 int i;
753
754 if (prescale < 2)
755 prescale = 2;
756 if (prescale > 5)
757 prescale = 5;
758
759 pll = ofreq * 8 * prescale * (u64)(1 << 20);
760 do_div(pll,xtal);
761 reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
762 if (((reg >> 20) & 0x3f) < 14) {
763 printk("%s/0: pll out of range\n",core->name);
764 return -1;
765 }
766
767 dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
768 reg, cx_read(MO_PLL_REG), ofreq);
769 cx_write(MO_PLL_REG, reg);
770 for (i = 0; i < 100; i++) {
771 reg = cx_read(MO_DEVICE_STATUS);
772 if (reg & (1<<2)) {
773 dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
774 prescale,ofreq);
775 return 0;
776 }
777 dprintk(1,"pll not locked yet, waiting ...\n");
778 msleep(10);
779 }
780 dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
781 return -1;
782}
783
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800784int cx88_start_audio_dma(struct cx88_core *core)
785{
Marcin Rudowski17801f52006-02-06 09:15:14 -0200786 /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
787 int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300788
789 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
790 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
791 return 0;
792
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800793 /* setup fifo + format */
Marcin Rudowski17801f52006-02-06 09:15:14 -0200794 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
795 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800796
Marcin Rudowski17801f52006-02-06 09:15:14 -0200797 cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
798 cx_write(MO_AUDR_LNGTH, bpl); /* fifo bpl size */
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800799
800 /* start dma */
801 cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300802
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800803 return 0;
804}
805
806int cx88_stop_audio_dma(struct cx88_core *core)
807{
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300808 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
809 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
810 return 0;
811
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800812 /* stop dma */
813 cx_write(MO_AUD_DMACNTRL, 0x0000);
814
815 return 0;
816}
817
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818static int set_tvaudio(struct cx88_core *core)
819{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300820 v4l2_std_id norm = core->tvnorm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
Trent Piepho6a59d642007-08-15 14:41:57 -0300822 if (CX88_VMUX_TELEVISION != INPUT(core->input).type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 return 0;
824
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300825 if (V4L2_STD_PAL_BG & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800826 core->tvaudio = WW_BG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300828 } else if (V4L2_STD_PAL_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800829 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300831 } else if (V4L2_STD_PAL_I & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800832 core->tvaudio = WW_I;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300834 } else if (V4L2_STD_SECAM_L & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800835 core->tvaudio = WW_L;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300837 } else if (V4L2_STD_SECAM_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800838 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300840 } else if ((V4L2_STD_NTSC_M & norm) ||
841 (V4L2_STD_PAL_M & norm)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 core->tvaudio = WW_BTSC;
843
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300844 } else if (V4L2_STD_NTSC_M_JP & norm) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 core->tvaudio = WW_EIAJ;
846
847 } else {
848 printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300849 core->name, v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 core->tvaudio = 0;
851 return 0;
852 }
853
854 cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
855 cx88_set_tvaudio(core);
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -0700856 /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800858/*
859 This should be needed only on cx88-alsa. It seems that some cx88 chips have
860 bugs and does require DMA enabled for it to work.
861 */
862 cx88_start_audio_dma(core);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 return 0;
864}
865
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800866
867
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300868int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869{
870 u32 fsc8;
871 u32 adc_clock;
872 u32 vdec_clock;
873 u32 step_db,step_dr;
874 u64 tmp64;
875 u32 bdelay,agcdelay,htotal;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300876 u32 cxiformat, cxoformat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
878 core->tvnorm = norm;
879 fsc8 = norm_fsc8(norm);
880 adc_clock = xtal;
881 vdec_clock = fsc8;
882 step_db = fsc8;
883 step_dr = fsc8;
884
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300885 if (norm & V4L2_STD_NTSC_M_JP) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300886 cxiformat = VideoFormatNTSCJapan;
887 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300888 } else if (norm & V4L2_STD_NTSC_443) {
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300889 cxiformat = VideoFormatNTSC443;
890 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300891 } else if (norm & V4L2_STD_PAL_M) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300892 cxiformat = VideoFormatPALM;
893 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300894 } else if (norm & V4L2_STD_PAL_N) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300895 cxiformat = VideoFormatPALN;
896 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300897 } else if (norm & V4L2_STD_PAL_Nc) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300898 cxiformat = VideoFormatPALNC;
899 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300900 } else if (norm & V4L2_STD_PAL_60) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300901 cxiformat = VideoFormatPAL60;
902 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300903 } else if (norm & V4L2_STD_NTSC) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300904 cxiformat = VideoFormatNTSC;
905 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300906 } else if (norm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 step_db = 4250000 * 8;
908 step_dr = 4406250 * 8;
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300909
910 cxiformat = VideoFormatSECAM;
911 cxoformat = 0x181f0008;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300912 } else { /* PAL */
913 cxiformat = VideoFormatPAL;
914 cxoformat = 0x181f0008;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 }
916
917 dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300918 v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
919 step_db, step_dr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 set_pll(core,2,vdec_clock);
921
922 dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300923 cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
924 cx_andor(MO_INPUT_FORMAT, 0xf, cxiformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 // FIXME: as-is from DScaler
927 dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300928 cxoformat, cx_read(MO_OUTPUT_FORMAT));
929 cx_write(MO_OUTPUT_FORMAT, cxoformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
931 // MO_SCONV_REG = adc clock / video dec clock * 2^17
932 tmp64 = adc_clock * (u64)(1 << 17);
933 do_div(tmp64, vdec_clock);
934 dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
935 (u32)tmp64, cx_read(MO_SCONV_REG));
936 cx_write(MO_SCONV_REG, (u32)tmp64);
937
938 // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
939 tmp64 = step_db * (u64)(1 << 22);
940 do_div(tmp64, vdec_clock);
941 dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
942 (u32)tmp64, cx_read(MO_SUB_STEP));
943 cx_write(MO_SUB_STEP, (u32)tmp64);
944
945 // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
946 tmp64 = step_dr * (u64)(1 << 22);
947 do_div(tmp64, vdec_clock);
948 dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
949 (u32)tmp64, cx_read(MO_SUB_STEP_DR));
950 cx_write(MO_SUB_STEP_DR, (u32)tmp64);
951
952 // bdelay + agcdelay
953 bdelay = vdec_clock * 65 / 20000000 + 21;
954 agcdelay = vdec_clock * 68 / 20000000 + 15;
955 dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
956 (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
957 cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
958
959 // htotal
960 tmp64 = norm_htotal(norm) * (u64)vdec_clock;
961 do_div(tmp64, fsc8);
Mauro Carvalho Chehabccbf64b2006-09-29 12:39:36 -0300962 htotal = (u32)tmp64 | (HLNotchFilter4xFsc << 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
964 htotal, cx_read(MO_HTOTAL), (u32)tmp64);
965 cx_write(MO_HTOTAL, htotal);
966
Trent Piepho3eb73172006-05-23 23:54:44 -0300967 // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
968 // the effective vbi offset ~244 samples, the same as the Bt8x8
969 cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
971 // this is needed as well to set all tvnorm parameter
972 cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
973
974 // audio
975 set_tvaudio(core);
976
977 // tell i2c chips
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300978 cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979
980 // done
981 return 0;
982}
983
984/* ------------------------------------------------------------------ */
985
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986struct video_device *cx88_vdev_init(struct cx88_core *core,
987 struct pci_dev *pci,
988 struct video_device *template,
989 char *type)
990{
991 struct video_device *vfd;
992
993 vfd = video_device_alloc();
994 if (NULL == vfd)
995 return NULL;
996 *vfd = *template;
997 vfd->minor = -1;
998 vfd->dev = &pci->dev;
999 vfd->release = video_device_release;
1000 snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
Trent Piepho6a59d642007-08-15 14:41:57 -03001001 core->name, type, core->board.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 return vfd;
1003}
1004
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005struct cx88_core* cx88_core_get(struct pci_dev *pci)
1006{
1007 struct cx88_core *core;
1008 struct list_head *item;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001010 mutex_lock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 list_for_each(item,&cx88_devlist) {
1012 core = list_entry(item, struct cx88_core, devlist);
1013 if (pci->bus->number != core->pci_bus)
1014 continue;
1015 if (PCI_SLOT(pci->devfn) != core->pci_slot)
1016 continue;
1017
Trent Piephobbc83592007-08-15 14:41:58 -03001018 if (0 != cx88_get_resources(core, pci)) {
1019 mutex_unlock(&devlist);
1020 return NULL;
1021 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 atomic_inc(&core->refcount);
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001023 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 return core;
1025 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026
Trent Piephobbc83592007-08-15 14:41:58 -03001027 core = cx88_core_create(pci, cx88_devcount);
1028 if (NULL != core) {
1029 cx88_devcount++;
1030 list_add_tail(&core->devlist, &cx88_devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001033 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 return core;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035}
1036
1037void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
1038{
1039 release_mem_region(pci_resource_start(pci,0),
1040 pci_resource_len(pci,0));
1041
1042 if (!atomic_dec_and_test(&core->refcount))
1043 return;
1044
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001045 mutex_lock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 cx88_ir_fini(core);
1047 if (0 == core->i2c_rc)
Jean Delvare32697112006-12-10 21:21:33 +01001048 i2c_del_adapter(&core->i2c_adap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 list_del(&core->devlist);
1050 iounmap(core->lmmio);
1051 cx88_devcount--;
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001052 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 kfree(core);
1054}
1055
1056/* ------------------------------------------------------------------ */
1057
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058EXPORT_SYMBOL(cx88_print_irqbits);
1059
1060EXPORT_SYMBOL(cx88_core_irq);
1061EXPORT_SYMBOL(cx88_wakeup);
1062EXPORT_SYMBOL(cx88_reset);
1063EXPORT_SYMBOL(cx88_shutdown);
1064
1065EXPORT_SYMBOL(cx88_risc_buffer);
1066EXPORT_SYMBOL(cx88_risc_databuffer);
1067EXPORT_SYMBOL(cx88_risc_stopper);
1068EXPORT_SYMBOL(cx88_free_buffer);
1069
1070EXPORT_SYMBOL(cx88_sram_channels);
1071EXPORT_SYMBOL(cx88_sram_channel_setup);
1072EXPORT_SYMBOL(cx88_sram_channel_dump);
1073
1074EXPORT_SYMBOL(cx88_set_tvnorm);
1075EXPORT_SYMBOL(cx88_set_scale);
1076
1077EXPORT_SYMBOL(cx88_vdev_init);
1078EXPORT_SYMBOL(cx88_core_get);
1079EXPORT_SYMBOL(cx88_core_put);
1080
1081/*
1082 * Local variables:
1083 * c-basic-offset: 8
1084 * End:
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -07001085 * 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 -07001086 */