blob: 62e8dd24c5f5413b978f6019a1d8b01c955aa4ed [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
Trent Piepho05b27232007-08-24 01:06:34 -030071/* @lpi: lines per IRQ, or 0 to not generate irqs. Note: IRQ to be
72 generated _after_ lpi lines are transferred. */
Linus Torvalds1da177e2005-04-16 15:20:36 -070073static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist,
74 unsigned int offset, u32 sync_line,
75 unsigned int bpl, unsigned int padding,
Trent Piepho05b27232007-08-24 01:06:34 -030076 unsigned int lines, unsigned int lpi)
Linus Torvalds1da177e2005-04-16 15:20:36 -070077{
78 struct scatterlist *sg;
Trent Piepho05b27232007-08-24 01:06:34 -030079 unsigned int line,todo,sol;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
81 /* sync instruction */
82 if (sync_line != NO_SYNC_LINE)
83 *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
84
85 /* scan lines */
86 sg = sglist;
87 for (line = 0; line < lines; line++) {
88 while (offset && offset >= sg_dma_len(sg)) {
89 offset -= sg_dma_len(sg);
90 sg++;
91 }
Trent Piepho05b27232007-08-24 01:06:34 -030092 if (lpi && line>0 && !(line % lpi))
93 sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
94 else
95 sol = RISC_SOL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 if (bpl <= sg_dma_len(sg)-offset) {
97 /* fits into current chunk */
Trent Piepho05b27232007-08-24 01:06:34 -030098 *(rp++)=cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -080099 *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
100 offset+=bpl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 } else {
Peter Naullsd1009bd2006-08-08 09:10:05 -0300102 /* scanline needs to be split */
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800103 todo = bpl;
Trent Piepho05b27232007-08-24 01:06:34 -0300104 *(rp++)=cpu_to_le32(RISC_WRITE|sol|
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 (sg_dma_len(sg)-offset));
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800106 *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
107 todo -= (sg_dma_len(sg)-offset);
108 offset = 0;
109 sg++;
110 while (todo > sg_dma_len(sg)) {
Mauro Carvalho Chehabf2421ca2005-11-08 21:37:45 -0800111 *(rp++)=cpu_to_le32(RISC_WRITE|
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 sg_dma_len(sg));
Mauro Carvalho Chehabf2421ca2005-11-08 21:37:45 -0800113 *(rp++)=cpu_to_le32(sg_dma_address(sg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 todo -= sg_dma_len(sg);
115 sg++;
116 }
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800117 *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 *(rp++)=cpu_to_le32(sg_dma_address(sg));
119 offset += todo;
120 }
121 offset += padding;
122 }
123
124 return rp;
125}
126
127int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
128 struct scatterlist *sglist,
129 unsigned int top_offset, unsigned int bottom_offset,
130 unsigned int bpl, unsigned int padding, unsigned int lines)
131{
132 u32 instructions,fields;
133 u32 *rp;
134 int rc;
135
136 fields = 0;
137 if (UNSET != top_offset)
138 fields++;
139 if (UNSET != bottom_offset)
140 fields++;
141
142 /* estimate risc mem: worst case is one write per page border +
Duncan Sandsbba3ad72006-04-11 10:18:57 -0300143 one write per scan line + syncs + jump (all 2 dwords). Padding
144 can cause next bpl to start close to a page border. First DMA
145 region may be smaller than PAGE_SIZE */
146 instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines);
147 instructions += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
149 return rc;
150
151 /* write risc instructions */
152 rp = risc->cpu;
153 if (UNSET != top_offset)
154 rp = cx88_risc_field(rp, sglist, top_offset, 0,
Trent Piepho05b27232007-08-24 01:06:34 -0300155 bpl, padding, lines, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 if (UNSET != bottom_offset)
157 rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200,
Trent Piepho05b27232007-08-24 01:06:34 -0300158 bpl, padding, lines, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
160 /* save pointer to jmp instruction address */
161 risc->jmp = rp;
Duncan Sands4a287cf2006-02-27 00:09:48 -0300162 BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 return 0;
164}
165
166int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
167 struct scatterlist *sglist, unsigned int bpl,
Trent Piepho05b27232007-08-24 01:06:34 -0300168 unsigned int lines, unsigned int lpi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169{
170 u32 instructions;
171 u32 *rp;
172 int rc;
173
174 /* estimate risc mem: worst case is one write per page border +
Duncan Sandsbba3ad72006-04-11 10:18:57 -0300175 one write per scan line + syncs + jump (all 2 dwords). Here
176 there is no padding and no sync. First DMA region may be smaller
177 than PAGE_SIZE */
178 instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
179 instructions += 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
181 return rc;
182
183 /* write risc instructions */
184 rp = risc->cpu;
Trent Piepho05b27232007-08-24 01:06:34 -0300185 rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines, lpi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
187 /* save pointer to jmp instruction address */
188 risc->jmp = rp;
Duncan Sands4a287cf2006-02-27 00:09:48 -0300189 BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 return 0;
191}
192
193int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
194 u32 reg, u32 mask, u32 value)
195{
196 u32 *rp;
197 int rc;
198
199 if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0)
200 return rc;
201
202 /* write risc instructions */
203 rp = risc->cpu;
204 *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM);
205 *(rp++) = cpu_to_le32(reg);
206 *(rp++) = cpu_to_le32(value);
207 *(rp++) = cpu_to_le32(mask);
208 *(rp++) = cpu_to_le32(RISC_JUMP);
209 *(rp++) = cpu_to_le32(risc->dma);
210 return 0;
211}
212
213void
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300214cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215{
Mauro Carvalho Chehabc1accaa2007-08-23 16:37:49 -0300216 struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
217
Eric Sesterhennae246012006-03-13 13:17:11 -0300218 BUG_ON(in_interrupt());
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 videobuf_waiton(&buf->vb,0,0);
Mauro Carvalho Chehabc1accaa2007-08-23 16:37:49 -0300220 videobuf_dma_unmap(q, dma);
221 videobuf_dma_free(dma);
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300222 btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 buf->vb.state = STATE_NEEDS_INIT;
224}
225
226/* ------------------------------------------------------------------ */
227/* our SRAM memory layout */
228
229/* we are going to put all thr risc programs into host memory, so we
230 * can use the whole SDRAM for the DMA fifos. To simplify things, we
231 * use a static memory layout. That surely will waste memory in case
232 * we don't use all DMA channels at the same time (which will be the
233 * case most of the time). But that still gives us enougth FIFO space
234 * to be able to deal with insane long pci latencies ...
235 *
236 * FIFO space allocations:
237 * channel 21 (y video) - 10.0k
238 * channel 22 (u video) - 2.0k
239 * channel 23 (v video) - 2.0k
240 * channel 24 (vbi) - 4.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200241 * channels 25+26 (audio) - 4.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 * channel 28 (mpeg) - 4.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200243 * TOTAL = 29.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 *
245 * Every channel has 160 bytes control data (64 bytes instruction
246 * queue and 6 CDT entries), which is close to 2k total.
247 *
248 * Address layout:
249 * 0x0000 - 0x03ff CMDs / reserved
250 * 0x0400 - 0x0bff instruction queues + CDs
251 * 0x0c00 - FIFOs
252 */
253
254struct sram_channel cx88_sram_channels[] = {
255 [SRAM_CH21] = {
256 .name = "video y / packed",
257 .cmds_start = 0x180040,
258 .ctrl_start = 0x180400,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800259 .cdt = 0x180400 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 .fifo_start = 0x180c00,
261 .fifo_size = 0x002800,
262 .ptr1_reg = MO_DMA21_PTR1,
263 .ptr2_reg = MO_DMA21_PTR2,
264 .cnt1_reg = MO_DMA21_CNT1,
265 .cnt2_reg = MO_DMA21_CNT2,
266 },
267 [SRAM_CH22] = {
268 .name = "video u",
269 .cmds_start = 0x180080,
270 .ctrl_start = 0x1804a0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800271 .cdt = 0x1804a0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 .fifo_start = 0x183400,
273 .fifo_size = 0x000800,
274 .ptr1_reg = MO_DMA22_PTR1,
275 .ptr2_reg = MO_DMA22_PTR2,
276 .cnt1_reg = MO_DMA22_CNT1,
277 .cnt2_reg = MO_DMA22_CNT2,
278 },
279 [SRAM_CH23] = {
280 .name = "video v",
281 .cmds_start = 0x1800c0,
282 .ctrl_start = 0x180540,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800283 .cdt = 0x180540 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 .fifo_start = 0x183c00,
285 .fifo_size = 0x000800,
286 .ptr1_reg = MO_DMA23_PTR1,
287 .ptr2_reg = MO_DMA23_PTR2,
288 .cnt1_reg = MO_DMA23_CNT1,
289 .cnt2_reg = MO_DMA23_CNT2,
290 },
291 [SRAM_CH24] = {
292 .name = "vbi",
293 .cmds_start = 0x180100,
294 .ctrl_start = 0x1805e0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800295 .cdt = 0x1805e0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 .fifo_start = 0x184400,
297 .fifo_size = 0x001000,
298 .ptr1_reg = MO_DMA24_PTR1,
299 .ptr2_reg = MO_DMA24_PTR2,
300 .cnt1_reg = MO_DMA24_CNT1,
301 .cnt2_reg = MO_DMA24_CNT2,
302 },
303 [SRAM_CH25] = {
304 .name = "audio from",
305 .cmds_start = 0x180140,
306 .ctrl_start = 0x180680,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800307 .cdt = 0x180680 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 .fifo_start = 0x185400,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200309 .fifo_size = 0x001000,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 .ptr1_reg = MO_DMA25_PTR1,
311 .ptr2_reg = MO_DMA25_PTR2,
312 .cnt1_reg = MO_DMA25_CNT1,
313 .cnt2_reg = MO_DMA25_CNT2,
314 },
315 [SRAM_CH26] = {
316 .name = "audio to",
317 .cmds_start = 0x180180,
318 .ctrl_start = 0x180720,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800319 .cdt = 0x180680 + 64, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 .fifo_start = 0x185400, /* same as audio IN */
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200321 .fifo_size = 0x001000, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 .ptr1_reg = MO_DMA26_PTR1,
323 .ptr2_reg = MO_DMA26_PTR2,
324 .cnt1_reg = MO_DMA26_CNT1,
325 .cnt2_reg = MO_DMA26_CNT2,
326 },
327 [SRAM_CH28] = {
328 .name = "mpeg",
329 .cmds_start = 0x180200,
330 .ctrl_start = 0x1807C0,
331 .cdt = 0x1807C0 + 64,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200332 .fifo_start = 0x186400,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 .fifo_size = 0x001000,
334 .ptr1_reg = MO_DMA28_PTR1,
335 .ptr2_reg = MO_DMA28_PTR2,
336 .cnt1_reg = MO_DMA28_CNT1,
337 .cnt2_reg = MO_DMA28_CNT2,
338 },
339};
340
341int cx88_sram_channel_setup(struct cx88_core *core,
342 struct sram_channel *ch,
343 unsigned int bpl, u32 risc)
344{
345 unsigned int i,lines;
346 u32 cdt;
347
348 bpl = (bpl + 7) & ~7; /* alignment */
349 cdt = ch->cdt;
350 lines = ch->fifo_size / bpl;
351 if (lines > 6)
352 lines = 6;
353 BUG_ON(lines < 2);
354
355 /* write CDT */
356 for (i = 0; i < lines; i++)
357 cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
358
359 /* write CMDS */
360 cx_write(ch->cmds_start + 0, risc);
361 cx_write(ch->cmds_start + 4, cdt);
362 cx_write(ch->cmds_start + 8, (lines*16) >> 3);
363 cx_write(ch->cmds_start + 12, ch->ctrl_start);
364 cx_write(ch->cmds_start + 16, 64 >> 2);
365 for (i = 20; i < 64; i += 4)
366 cx_write(ch->cmds_start + i, 0);
367
368 /* fill registers */
369 cx_write(ch->ptr1_reg, ch->fifo_start);
370 cx_write(ch->ptr2_reg, cdt);
371 cx_write(ch->cnt1_reg, (bpl >> 3) -1);
372 cx_write(ch->cnt2_reg, (lines*16) >> 3);
373
374 dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
375 return 0;
376}
377
378/* ------------------------------------------------------------------ */
379/* debug helper code */
380
Peter Hagervallf9e7a022005-11-08 21:36:29 -0800381static int cx88_risc_decode(u32 risc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382{
383 static char *instr[16] = {
384 [ RISC_SYNC >> 28 ] = "sync",
385 [ RISC_WRITE >> 28 ] = "write",
386 [ RISC_WRITEC >> 28 ] = "writec",
387 [ RISC_READ >> 28 ] = "read",
388 [ RISC_READC >> 28 ] = "readc",
389 [ RISC_JUMP >> 28 ] = "jump",
390 [ RISC_SKIP >> 28 ] = "skip",
391 [ RISC_WRITERM >> 28 ] = "writerm",
392 [ RISC_WRITECM >> 28 ] = "writecm",
393 [ RISC_WRITECR >> 28 ] = "writecr",
394 };
395 static int incr[16] = {
396 [ RISC_WRITE >> 28 ] = 2,
397 [ RISC_JUMP >> 28 ] = 2,
398 [ RISC_WRITERM >> 28 ] = 3,
399 [ RISC_WRITECM >> 28 ] = 3,
400 [ RISC_WRITECR >> 28 ] = 4,
401 };
402 static char *bits[] = {
403 "12", "13", "14", "resync",
404 "cnt0", "cnt1", "18", "19",
405 "20", "21", "22", "23",
406 "irq1", "irq2", "eol", "sol",
407 };
408 int i;
409
410 printk("0x%08x [ %s", risc,
411 instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
412 for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
413 if (risc & (1 << (i + 12)))
414 printk(" %s",bits[i]);
415 printk(" count=%d ]\n", risc & 0xfff);
416 return incr[risc >> 28] ? incr[risc >> 28] : 1;
417}
418
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419
420void cx88_sram_channel_dump(struct cx88_core *core,
421 struct sram_channel *ch)
422{
423 static char *name[] = {
424 "initial risc",
425 "cdt base",
426 "cdt size",
427 "iq base",
428 "iq size",
429 "risc pc",
430 "iq wr ptr",
431 "iq rd ptr",
432 "cdt current",
433 "pci target",
434 "line / byte",
435 };
436 u32 risc;
437 unsigned int i,j,n;
438
439 printk("%s: %s - dma channel status dump\n",
440 core->name,ch->name);
441 for (i = 0; i < ARRAY_SIZE(name); i++)
442 printk("%s: cmds: %-12s: 0x%08x\n",
443 core->name,name[i],
444 cx_read(ch->cmds_start + 4*i));
Trent Piepho16cf1d02007-08-21 08:19:16 -0300445 for (n = 1, i = 0; i < 4; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 risc = cx_read(ch->cmds_start + 4 * (i+11));
447 printk("%s: risc%d: ", core->name, i);
Trent Piepho16cf1d02007-08-21 08:19:16 -0300448 if (--n)
449 printk("0x%08x [ arg #%d ]\n", risc, n);
450 else
451 n = cx88_risc_decode(risc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 }
453 for (i = 0; i < 16; i += n) {
454 risc = cx_read(ch->ctrl_start + 4 * i);
455 printk("%s: iq %x: ", core->name, i);
456 n = cx88_risc_decode(risc);
457 for (j = 1; j < n; j++) {
458 risc = cx_read(ch->ctrl_start + 4 * (i+j));
459 printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
460 core->name, i+j, risc, j);
461 }
462 }
463
464 printk("%s: fifo: 0x%08x -> 0x%x\n",
465 core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
466 printk("%s: ctrl: 0x%08x -> 0x%x\n",
467 core->name, ch->ctrl_start, ch->ctrl_start+6*16);
468 printk("%s: ptr1_reg: 0x%08x\n",
469 core->name,cx_read(ch->ptr1_reg));
470 printk("%s: ptr2_reg: 0x%08x\n",
471 core->name,cx_read(ch->ptr2_reg));
472 printk("%s: cnt1_reg: 0x%08x\n",
473 core->name,cx_read(ch->cnt1_reg));
474 printk("%s: cnt2_reg: 0x%08x\n",
475 core->name,cx_read(ch->cnt2_reg));
476}
477
Adrian Bunk408b6642005-05-01 08:59:29 -0700478static char *cx88_pci_irqs[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479 "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
480 "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
481 "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
482 "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
483};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
485void cx88_print_irqbits(char *name, char *tag, char **strings,
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300486 int len, u32 bits, u32 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487{
488 unsigned int i;
489
490 printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300491 for (i = 0; i < len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 if (!(bits & (1 << i)))
493 continue;
494 if (strings[i])
495 printk(" %s", strings[i]);
496 else
497 printk(" %d", i);
498 if (!(mask & (1 << i)))
499 continue;
500 printk("*");
501 }
502 printk("\n");
503}
504
505/* ------------------------------------------------------------------ */
506
507int cx88_core_irq(struct cx88_core *core, u32 status)
508{
509 int handled = 0;
510
Trent Piepho8ddac9e2007-08-18 06:57:55 -0300511 if (status & PCI_INT_IR_SMPINT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 cx88_ir_irq(core);
513 handled++;
514 }
515 if (!handled)
516 cx88_print_irqbits(core->name, "irq pci",
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300517 cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
518 status, core->pci_irqmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 return handled;
520}
521
522void cx88_wakeup(struct cx88_core *core,
523 struct cx88_dmaqueue *q, u32 count)
524{
525 struct cx88_buffer *buf;
526 int bc;
527
528 for (bc = 0;; bc++) {
529 if (list_empty(&q->active))
530 break;
531 buf = list_entry(q->active.next,
532 struct cx88_buffer, vb.queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 /* count comes from the hw and is is 16bit wide --
534 * this trick handles wrap-arounds correctly for
535 * up to 32767 buffers in flight... */
536 if ((s16) (count - buf->count) < 0)
537 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 do_gettimeofday(&buf->vb.ts);
539 dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
540 count, buf->count);
541 buf->vb.state = STATE_DONE;
542 list_del(&buf->vb.queue);
543 wake_up(&buf->vb.done);
544 }
545 if (list_empty(&q->active)) {
546 del_timer(&q->timeout);
547 } else {
548 mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
549 }
550 if (bc != 1)
551 printk("%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc);
552}
553
554void cx88_shutdown(struct cx88_core *core)
555{
556 /* disable RISC controller + IRQs */
557 cx_write(MO_DEV_CNTRL2, 0);
558
559 /* stop dma transfers */
560 cx_write(MO_VID_DMACNTRL, 0x0);
561 cx_write(MO_AUD_DMACNTRL, 0x0);
562 cx_write(MO_TS_DMACNTRL, 0x0);
563 cx_write(MO_VIP_DMACNTRL, 0x0);
564 cx_write(MO_GPHST_DMACNTRL, 0x0);
565
566 /* stop interrupts */
567 cx_write(MO_PCI_INTMSK, 0x0);
568 cx_write(MO_VID_INTMSK, 0x0);
569 cx_write(MO_AUD_INTMSK, 0x0);
570 cx_write(MO_TS_INTMSK, 0x0);
571 cx_write(MO_VIP_INTMSK, 0x0);
572 cx_write(MO_GPHST_INTMSK, 0x0);
573
574 /* stop capturing */
575 cx_write(VID_CAPTURE_CONTROL, 0);
576}
577
578int cx88_reset(struct cx88_core *core)
579{
580 dprintk(1,"%s\n",__FUNCTION__);
581 cx88_shutdown(core);
582
583 /* clear irq status */
584 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
585 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
586 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
587
588 /* wait a bit */
589 msleep(100);
590
591 /* init sram */
592 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
593 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
594 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
595 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
596 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
597 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
598 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
599
600 /* misc init ... */
601 cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
602 (1 << 12) | // agc gain
603 (1 << 11) | // adaptibe agc
604 (0 << 10) | // chroma agc
605 (0 << 9) | // ckillen
606 (7)));
607
608 /* setup image format */
609 cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
610
611 /* setup FIFO Threshholds */
612 cx_write(MO_PDMA_STHRSH, 0x0807);
613 cx_write(MO_PDMA_DTHRSH, 0x0807);
614
615 /* fixes flashing of image */
616 cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
617 cx_write(MO_AGC_BACK_VBI, 0x00E00555);
618
619 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
620 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
621 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
622
623 /* Reset on-board parts */
624 cx_write(MO_SRST_IO, 0);
625 msleep(10);
626 cx_write(MO_SRST_IO, 1);
627
628 return 0;
629}
630
631/* ------------------------------------------------------------------ */
632
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300633static unsigned int inline norm_swidth(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_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636}
637
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300638static unsigned int inline norm_hdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300640 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641}
642
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300643static unsigned int inline norm_vdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300645 return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646}
647
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300648static unsigned int inline norm_fsc8(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300650 if (norm & V4L2_STD_PAL_M)
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300651 return 28604892; // 3.575611 MHz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300653 if (norm & (V4L2_STD_PAL_Nc))
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300654 return 28656448; // 3.582056 MHz
655
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300656 if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300657 return 28636360; // 3.57954545 MHz +/- 10 Hz
658
659 /* SECAM have also different sub carrier for chroma,
660 but step_db and step_dr, at cx88_set_tvnorm already handles that.
661
662 The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
663 */
664
665 return 35468950; // 4.43361875 MHz +/- 5 Hz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666}
667
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300668static unsigned int inline norm_htotal(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669{
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700670
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300671 unsigned int fsc4=norm_fsc8(norm)/2;
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700672
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300673 /* returns 4*FSC / vtotal / frames per seconds */
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300674 return (norm & V4L2_STD_625_50) ?
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300675 ((fsc4+312)/625+12)/25 :
676 ((fsc4+262)/525*1001+15000)/30000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677}
678
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300679static unsigned int inline norm_vbipack(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300681 return (norm & V4L2_STD_625_50) ? 511 : 400;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682}
683
684int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
685 enum v4l2_field field)
686{
687 unsigned int swidth = norm_swidth(core->tvnorm);
688 unsigned int sheight = norm_maxh(core->tvnorm);
689 u32 value;
690
691 dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
692 V4L2_FIELD_HAS_TOP(field) ? "T" : "",
693 V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300694 v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 if (!V4L2_FIELD_HAS_BOTH(field))
696 height *= 2;
697
698 // recalc H delay and scale registers
699 value = (width * norm_hdelay(core->tvnorm)) / swidth;
700 value &= 0x3fe;
701 cx_write(MO_HDELAY_EVEN, value);
702 cx_write(MO_HDELAY_ODD, value);
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300703 dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700704
705 value = (swidth * 4096 / width) - 4096;
706 cx_write(MO_HSCALE_EVEN, value);
707 cx_write(MO_HSCALE_ODD, value);
708 dprintk(1,"set_scale: hscale 0x%04x\n", value);
709
710 cx_write(MO_HACTIVE_EVEN, width);
711 cx_write(MO_HACTIVE_ODD, width);
712 dprintk(1,"set_scale: hactive 0x%04x\n", width);
713
714 // recalc V scale Register (delay is constant)
715 cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
716 cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
717 dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
718
719 value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
720 cx_write(MO_VSCALE_EVEN, value);
721 cx_write(MO_VSCALE_ODD, value);
722 dprintk(1,"set_scale: vscale 0x%04x\n", value);
723
724 cx_write(MO_VACTIVE_EVEN, sheight);
725 cx_write(MO_VACTIVE_ODD, sheight);
726 dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
727
728 // setup filters
729 value = 0;
730 value |= (1 << 19); // CFILT (default)
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300731 if (core->tvnorm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 value |= (1 << 15);
733 value |= (1 << 16);
734 }
Trent Piepho6a59d642007-08-15 14:41:57 -0300735 if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736 value |= (1 << 13) | (1 << 5);
737 if (V4L2_FIELD_INTERLACED == field)
738 value |= (1 << 3); // VINT (interlaced vertical scaling)
739 if (width < 385)
740 value |= (1 << 0); // 3-tap interpolation
741 if (width < 193)
742 value |= (1 << 1); // 5-tap interpolation
743 if (nocomb)
744 value |= (3 << 5); // disable comb filter
745
746 cx_write(MO_FILTER_EVEN, value);
747 cx_write(MO_FILTER_ODD, value);
748 dprintk(1,"set_scale: filter 0x%04x\n", value);
749
750 return 0;
751}
752
753static const u32 xtal = 28636363;
754
755static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
756{
757 static u32 pre[] = { 0, 0, 0, 3, 2, 1 };
758 u64 pll;
759 u32 reg;
760 int i;
761
762 if (prescale < 2)
763 prescale = 2;
764 if (prescale > 5)
765 prescale = 5;
766
767 pll = ofreq * 8 * prescale * (u64)(1 << 20);
768 do_div(pll,xtal);
769 reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
770 if (((reg >> 20) & 0x3f) < 14) {
771 printk("%s/0: pll out of range\n",core->name);
772 return -1;
773 }
774
775 dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
776 reg, cx_read(MO_PLL_REG), ofreq);
777 cx_write(MO_PLL_REG, reg);
778 for (i = 0; i < 100; i++) {
779 reg = cx_read(MO_DEVICE_STATUS);
780 if (reg & (1<<2)) {
781 dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
782 prescale,ofreq);
783 return 0;
784 }
785 dprintk(1,"pll not locked yet, waiting ...\n");
786 msleep(10);
787 }
788 dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
789 return -1;
790}
791
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800792int cx88_start_audio_dma(struct cx88_core *core)
793{
Marcin Rudowski17801f52006-02-06 09:15:14 -0200794 /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
795 int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300796
797 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
798 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
799 return 0;
800
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800801 /* setup fifo + format */
Marcin Rudowski17801f52006-02-06 09:15:14 -0200802 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
803 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800804
Marcin Rudowski17801f52006-02-06 09:15:14 -0200805 cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
806 cx_write(MO_AUDR_LNGTH, bpl); /* fifo bpl size */
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800807
808 /* start dma */
809 cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300810
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800811 return 0;
812}
813
814int cx88_stop_audio_dma(struct cx88_core *core)
815{
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300816 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
817 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
818 return 0;
819
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800820 /* stop dma */
821 cx_write(MO_AUD_DMACNTRL, 0x0000);
822
823 return 0;
824}
825
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826static int set_tvaudio(struct cx88_core *core)
827{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300828 v4l2_std_id norm = core->tvnorm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Trent Piepho6a59d642007-08-15 14:41:57 -0300830 if (CX88_VMUX_TELEVISION != INPUT(core->input).type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 return 0;
832
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300833 if (V4L2_STD_PAL_BG & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800834 core->tvaudio = WW_BG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300836 } else if (V4L2_STD_PAL_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800837 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300839 } else if (V4L2_STD_PAL_I & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800840 core->tvaudio = WW_I;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300842 } else if (V4L2_STD_SECAM_L & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800843 core->tvaudio = WW_L;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300845 } else if (V4L2_STD_SECAM_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800846 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300848 } else if ((V4L2_STD_NTSC_M & norm) ||
849 (V4L2_STD_PAL_M & norm)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 core->tvaudio = WW_BTSC;
851
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300852 } else if (V4L2_STD_NTSC_M_JP & norm) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 core->tvaudio = WW_EIAJ;
854
855 } else {
856 printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300857 core->name, v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 core->tvaudio = 0;
859 return 0;
860 }
861
862 cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
863 cx88_set_tvaudio(core);
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -0700864 /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800866/*
867 This should be needed only on cx88-alsa. It seems that some cx88 chips have
868 bugs and does require DMA enabled for it to work.
869 */
870 cx88_start_audio_dma(core);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 return 0;
872}
873
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800874
875
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300876int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877{
878 u32 fsc8;
879 u32 adc_clock;
880 u32 vdec_clock;
881 u32 step_db,step_dr;
882 u64 tmp64;
883 u32 bdelay,agcdelay,htotal;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300884 u32 cxiformat, cxoformat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
886 core->tvnorm = norm;
887 fsc8 = norm_fsc8(norm);
888 adc_clock = xtal;
889 vdec_clock = fsc8;
890 step_db = fsc8;
891 step_dr = fsc8;
892
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300893 if (norm & V4L2_STD_NTSC_M_JP) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300894 cxiformat = VideoFormatNTSCJapan;
895 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300896 } else if (norm & V4L2_STD_NTSC_443) {
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300897 cxiformat = VideoFormatNTSC443;
898 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300899 } else if (norm & V4L2_STD_PAL_M) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300900 cxiformat = VideoFormatPALM;
901 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300902 } else if (norm & V4L2_STD_PAL_N) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300903 cxiformat = VideoFormatPALN;
904 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300905 } else if (norm & V4L2_STD_PAL_Nc) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300906 cxiformat = VideoFormatPALNC;
907 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300908 } else if (norm & V4L2_STD_PAL_60) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300909 cxiformat = VideoFormatPAL60;
910 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300911 } else if (norm & V4L2_STD_NTSC) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300912 cxiformat = VideoFormatNTSC;
913 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300914 } else if (norm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 step_db = 4250000 * 8;
916 step_dr = 4406250 * 8;
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300917
918 cxiformat = VideoFormatSECAM;
919 cxoformat = 0x181f0008;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300920 } else { /* PAL */
921 cxiformat = VideoFormatPAL;
922 cxoformat = 0x181f0008;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923 }
924
925 dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300926 v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
927 step_db, step_dr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 set_pll(core,2,vdec_clock);
929
930 dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300931 cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
932 cx_andor(MO_INPUT_FORMAT, 0xf, cxiformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 // FIXME: as-is from DScaler
935 dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300936 cxoformat, cx_read(MO_OUTPUT_FORMAT));
937 cx_write(MO_OUTPUT_FORMAT, cxoformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
939 // MO_SCONV_REG = adc clock / video dec clock * 2^17
940 tmp64 = adc_clock * (u64)(1 << 17);
941 do_div(tmp64, vdec_clock);
942 dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
943 (u32)tmp64, cx_read(MO_SCONV_REG));
944 cx_write(MO_SCONV_REG, (u32)tmp64);
945
946 // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
947 tmp64 = step_db * (u64)(1 << 22);
948 do_div(tmp64, vdec_clock);
949 dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
950 (u32)tmp64, cx_read(MO_SUB_STEP));
951 cx_write(MO_SUB_STEP, (u32)tmp64);
952
953 // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
954 tmp64 = step_dr * (u64)(1 << 22);
955 do_div(tmp64, vdec_clock);
956 dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
957 (u32)tmp64, cx_read(MO_SUB_STEP_DR));
958 cx_write(MO_SUB_STEP_DR, (u32)tmp64);
959
960 // bdelay + agcdelay
961 bdelay = vdec_clock * 65 / 20000000 + 21;
962 agcdelay = vdec_clock * 68 / 20000000 + 15;
963 dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
964 (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
965 cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
966
967 // htotal
968 tmp64 = norm_htotal(norm) * (u64)vdec_clock;
969 do_div(tmp64, fsc8);
Mauro Carvalho Chehabccbf64b2006-09-29 12:39:36 -0300970 htotal = (u32)tmp64 | (HLNotchFilter4xFsc << 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
972 htotal, cx_read(MO_HTOTAL), (u32)tmp64);
973 cx_write(MO_HTOTAL, htotal);
974
Trent Piepho3eb73172006-05-23 23:54:44 -0300975 // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
976 // the effective vbi offset ~244 samples, the same as the Bt8x8
977 cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978
979 // this is needed as well to set all tvnorm parameter
980 cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
981
982 // audio
983 set_tvaudio(core);
984
985 // tell i2c chips
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300986 cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
988 // done
989 return 0;
990}
991
992/* ------------------------------------------------------------------ */
993
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994struct video_device *cx88_vdev_init(struct cx88_core *core,
995 struct pci_dev *pci,
996 struct video_device *template,
997 char *type)
998{
999 struct video_device *vfd;
1000
1001 vfd = video_device_alloc();
1002 if (NULL == vfd)
1003 return NULL;
1004 *vfd = *template;
1005 vfd->minor = -1;
1006 vfd->dev = &pci->dev;
1007 vfd->release = video_device_release;
1008 snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
Trent Piepho6a59d642007-08-15 14:41:57 -03001009 core->name, type, core->board.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 return vfd;
1011}
1012
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013struct cx88_core* cx88_core_get(struct pci_dev *pci)
1014{
1015 struct cx88_core *core;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001017 mutex_lock(&devlist);
Trent Piepho8bb629e22007-10-10 05:37:40 -03001018 list_for_each_entry(core, &cx88_devlist, devlist) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 if (pci->bus->number != core->pci_bus)
1020 continue;
1021 if (PCI_SLOT(pci->devfn) != core->pci_slot)
1022 continue;
1023
Trent Piephobbc83592007-08-15 14:41:58 -03001024 if (0 != cx88_get_resources(core, pci)) {
1025 mutex_unlock(&devlist);
1026 return NULL;
1027 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 atomic_inc(&core->refcount);
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001029 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 return core;
1031 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032
Trent Piephobbc83592007-08-15 14:41:58 -03001033 core = cx88_core_create(pci, cx88_devcount);
1034 if (NULL != core) {
1035 cx88_devcount++;
1036 list_add_tail(&core->devlist, &cx88_devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001039 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 return core;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041}
1042
1043void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
1044{
1045 release_mem_region(pci_resource_start(pci,0),
1046 pci_resource_len(pci,0));
1047
1048 if (!atomic_dec_and_test(&core->refcount))
1049 return;
1050
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001051 mutex_lock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 cx88_ir_fini(core);
1053 if (0 == core->i2c_rc)
Jean Delvare32697112006-12-10 21:21:33 +01001054 i2c_del_adapter(&core->i2c_adap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055 list_del(&core->devlist);
1056 iounmap(core->lmmio);
1057 cx88_devcount--;
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001058 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 kfree(core);
1060}
1061
1062/* ------------------------------------------------------------------ */
1063
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064EXPORT_SYMBOL(cx88_print_irqbits);
1065
1066EXPORT_SYMBOL(cx88_core_irq);
1067EXPORT_SYMBOL(cx88_wakeup);
1068EXPORT_SYMBOL(cx88_reset);
1069EXPORT_SYMBOL(cx88_shutdown);
1070
1071EXPORT_SYMBOL(cx88_risc_buffer);
1072EXPORT_SYMBOL(cx88_risc_databuffer);
1073EXPORT_SYMBOL(cx88_risc_stopper);
1074EXPORT_SYMBOL(cx88_free_buffer);
1075
1076EXPORT_SYMBOL(cx88_sram_channels);
1077EXPORT_SYMBOL(cx88_sram_channel_setup);
1078EXPORT_SYMBOL(cx88_sram_channel_dump);
1079
1080EXPORT_SYMBOL(cx88_set_tvnorm);
1081EXPORT_SYMBOL(cx88_set_scale);
1082
1083EXPORT_SYMBOL(cx88_vdev_init);
1084EXPORT_SYMBOL(cx88_core_get);
1085EXPORT_SYMBOL(cx88_core_put);
1086
Mauro Carvalho Chehab13595a52007-10-01 08:51:39 -03001087EXPORT_SYMBOL(cx88_ir_start);
1088EXPORT_SYMBOL(cx88_ir_stop);
1089
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090/*
1091 * Local variables:
1092 * c-basic-offset: 8
1093 * End:
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -07001094 * 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 -07001095 */