blob: 716154828ff0b2b3b3cbae5c2eee06271d952314 [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{
Eric Sesterhennae246012006-03-13 13:17:11 -0300216 BUG_ON(in_interrupt());
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 videobuf_waiton(&buf->vb,0,0);
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300218 videobuf_dma_unmap(q, &buf->vb.dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 videobuf_dma_free(&buf->vb.dma);
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300220 btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 buf->vb.state = STATE_NEEDS_INIT;
222}
223
224/* ------------------------------------------------------------------ */
225/* our SRAM memory layout */
226
227/* we are going to put all thr risc programs into host memory, so we
228 * can use the whole SDRAM for the DMA fifos. To simplify things, we
229 * use a static memory layout. That surely will waste memory in case
230 * we don't use all DMA channels at the same time (which will be the
231 * case most of the time). But that still gives us enougth FIFO space
232 * to be able to deal with insane long pci latencies ...
233 *
234 * FIFO space allocations:
235 * channel 21 (y video) - 10.0k
236 * channel 22 (u video) - 2.0k
237 * channel 23 (v video) - 2.0k
238 * channel 24 (vbi) - 4.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200239 * channels 25+26 (audio) - 4.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 * channel 28 (mpeg) - 4.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200241 * TOTAL = 29.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 *
243 * Every channel has 160 bytes control data (64 bytes instruction
244 * queue and 6 CDT entries), which is close to 2k total.
245 *
246 * Address layout:
247 * 0x0000 - 0x03ff CMDs / reserved
248 * 0x0400 - 0x0bff instruction queues + CDs
249 * 0x0c00 - FIFOs
250 */
251
252struct sram_channel cx88_sram_channels[] = {
253 [SRAM_CH21] = {
254 .name = "video y / packed",
255 .cmds_start = 0x180040,
256 .ctrl_start = 0x180400,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800257 .cdt = 0x180400 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 .fifo_start = 0x180c00,
259 .fifo_size = 0x002800,
260 .ptr1_reg = MO_DMA21_PTR1,
261 .ptr2_reg = MO_DMA21_PTR2,
262 .cnt1_reg = MO_DMA21_CNT1,
263 .cnt2_reg = MO_DMA21_CNT2,
264 },
265 [SRAM_CH22] = {
266 .name = "video u",
267 .cmds_start = 0x180080,
268 .ctrl_start = 0x1804a0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800269 .cdt = 0x1804a0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 .fifo_start = 0x183400,
271 .fifo_size = 0x000800,
272 .ptr1_reg = MO_DMA22_PTR1,
273 .ptr2_reg = MO_DMA22_PTR2,
274 .cnt1_reg = MO_DMA22_CNT1,
275 .cnt2_reg = MO_DMA22_CNT2,
276 },
277 [SRAM_CH23] = {
278 .name = "video v",
279 .cmds_start = 0x1800c0,
280 .ctrl_start = 0x180540,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800281 .cdt = 0x180540 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 .fifo_start = 0x183c00,
283 .fifo_size = 0x000800,
284 .ptr1_reg = MO_DMA23_PTR1,
285 .ptr2_reg = MO_DMA23_PTR2,
286 .cnt1_reg = MO_DMA23_CNT1,
287 .cnt2_reg = MO_DMA23_CNT2,
288 },
289 [SRAM_CH24] = {
290 .name = "vbi",
291 .cmds_start = 0x180100,
292 .ctrl_start = 0x1805e0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800293 .cdt = 0x1805e0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 .fifo_start = 0x184400,
295 .fifo_size = 0x001000,
296 .ptr1_reg = MO_DMA24_PTR1,
297 .ptr2_reg = MO_DMA24_PTR2,
298 .cnt1_reg = MO_DMA24_CNT1,
299 .cnt2_reg = MO_DMA24_CNT2,
300 },
301 [SRAM_CH25] = {
302 .name = "audio from",
303 .cmds_start = 0x180140,
304 .ctrl_start = 0x180680,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800305 .cdt = 0x180680 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 .fifo_start = 0x185400,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200307 .fifo_size = 0x001000,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 .ptr1_reg = MO_DMA25_PTR1,
309 .ptr2_reg = MO_DMA25_PTR2,
310 .cnt1_reg = MO_DMA25_CNT1,
311 .cnt2_reg = MO_DMA25_CNT2,
312 },
313 [SRAM_CH26] = {
314 .name = "audio to",
315 .cmds_start = 0x180180,
316 .ctrl_start = 0x180720,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800317 .cdt = 0x180680 + 64, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 .fifo_start = 0x185400, /* same as audio IN */
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200319 .fifo_size = 0x001000, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320 .ptr1_reg = MO_DMA26_PTR1,
321 .ptr2_reg = MO_DMA26_PTR2,
322 .cnt1_reg = MO_DMA26_CNT1,
323 .cnt2_reg = MO_DMA26_CNT2,
324 },
325 [SRAM_CH28] = {
326 .name = "mpeg",
327 .cmds_start = 0x180200,
328 .ctrl_start = 0x1807C0,
329 .cdt = 0x1807C0 + 64,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200330 .fifo_start = 0x186400,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 .fifo_size = 0x001000,
332 .ptr1_reg = MO_DMA28_PTR1,
333 .ptr2_reg = MO_DMA28_PTR2,
334 .cnt1_reg = MO_DMA28_CNT1,
335 .cnt2_reg = MO_DMA28_CNT2,
336 },
337};
338
339int cx88_sram_channel_setup(struct cx88_core *core,
340 struct sram_channel *ch,
341 unsigned int bpl, u32 risc)
342{
343 unsigned int i,lines;
344 u32 cdt;
345
346 bpl = (bpl + 7) & ~7; /* alignment */
347 cdt = ch->cdt;
348 lines = ch->fifo_size / bpl;
349 if (lines > 6)
350 lines = 6;
351 BUG_ON(lines < 2);
352
353 /* write CDT */
354 for (i = 0; i < lines; i++)
355 cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
356
357 /* write CMDS */
358 cx_write(ch->cmds_start + 0, risc);
359 cx_write(ch->cmds_start + 4, cdt);
360 cx_write(ch->cmds_start + 8, (lines*16) >> 3);
361 cx_write(ch->cmds_start + 12, ch->ctrl_start);
362 cx_write(ch->cmds_start + 16, 64 >> 2);
363 for (i = 20; i < 64; i += 4)
364 cx_write(ch->cmds_start + i, 0);
365
366 /* fill registers */
367 cx_write(ch->ptr1_reg, ch->fifo_start);
368 cx_write(ch->ptr2_reg, cdt);
369 cx_write(ch->cnt1_reg, (bpl >> 3) -1);
370 cx_write(ch->cnt2_reg, (lines*16) >> 3);
371
372 dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
373 return 0;
374}
375
376/* ------------------------------------------------------------------ */
377/* debug helper code */
378
Peter Hagervallf9e7a022005-11-08 21:36:29 -0800379static int cx88_risc_decode(u32 risc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380{
381 static char *instr[16] = {
382 [ RISC_SYNC >> 28 ] = "sync",
383 [ RISC_WRITE >> 28 ] = "write",
384 [ RISC_WRITEC >> 28 ] = "writec",
385 [ RISC_READ >> 28 ] = "read",
386 [ RISC_READC >> 28 ] = "readc",
387 [ RISC_JUMP >> 28 ] = "jump",
388 [ RISC_SKIP >> 28 ] = "skip",
389 [ RISC_WRITERM >> 28 ] = "writerm",
390 [ RISC_WRITECM >> 28 ] = "writecm",
391 [ RISC_WRITECR >> 28 ] = "writecr",
392 };
393 static int incr[16] = {
394 [ RISC_WRITE >> 28 ] = 2,
395 [ RISC_JUMP >> 28 ] = 2,
396 [ RISC_WRITERM >> 28 ] = 3,
397 [ RISC_WRITECM >> 28 ] = 3,
398 [ RISC_WRITECR >> 28 ] = 4,
399 };
400 static char *bits[] = {
401 "12", "13", "14", "resync",
402 "cnt0", "cnt1", "18", "19",
403 "20", "21", "22", "23",
404 "irq1", "irq2", "eol", "sol",
405 };
406 int i;
407
408 printk("0x%08x [ %s", risc,
409 instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
410 for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
411 if (risc & (1 << (i + 12)))
412 printk(" %s",bits[i]);
413 printk(" count=%d ]\n", risc & 0xfff);
414 return incr[risc >> 28] ? incr[risc >> 28] : 1;
415}
416
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
418void cx88_sram_channel_dump(struct cx88_core *core,
419 struct sram_channel *ch)
420{
421 static char *name[] = {
422 "initial risc",
423 "cdt base",
424 "cdt size",
425 "iq base",
426 "iq size",
427 "risc pc",
428 "iq wr ptr",
429 "iq rd ptr",
430 "cdt current",
431 "pci target",
432 "line / byte",
433 };
434 u32 risc;
435 unsigned int i,j,n;
436
437 printk("%s: %s - dma channel status dump\n",
438 core->name,ch->name);
439 for (i = 0; i < ARRAY_SIZE(name); i++)
440 printk("%s: cmds: %-12s: 0x%08x\n",
441 core->name,name[i],
442 cx_read(ch->cmds_start + 4*i));
Trent Piepho16cf1d02007-08-21 08:19:16 -0300443 for (n = 1, i = 0; i < 4; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 risc = cx_read(ch->cmds_start + 4 * (i+11));
445 printk("%s: risc%d: ", core->name, i);
Trent Piepho16cf1d02007-08-21 08:19:16 -0300446 if (--n)
447 printk("0x%08x [ arg #%d ]\n", risc, n);
448 else
449 n = cx88_risc_decode(risc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 }
451 for (i = 0; i < 16; i += n) {
452 risc = cx_read(ch->ctrl_start + 4 * i);
453 printk("%s: iq %x: ", core->name, i);
454 n = cx88_risc_decode(risc);
455 for (j = 1; j < n; j++) {
456 risc = cx_read(ch->ctrl_start + 4 * (i+j));
457 printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
458 core->name, i+j, risc, j);
459 }
460 }
461
462 printk("%s: fifo: 0x%08x -> 0x%x\n",
463 core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
464 printk("%s: ctrl: 0x%08x -> 0x%x\n",
465 core->name, ch->ctrl_start, ch->ctrl_start+6*16);
466 printk("%s: ptr1_reg: 0x%08x\n",
467 core->name,cx_read(ch->ptr1_reg));
468 printk("%s: ptr2_reg: 0x%08x\n",
469 core->name,cx_read(ch->ptr2_reg));
470 printk("%s: cnt1_reg: 0x%08x\n",
471 core->name,cx_read(ch->cnt1_reg));
472 printk("%s: cnt2_reg: 0x%08x\n",
473 core->name,cx_read(ch->cnt2_reg));
474}
475
Adrian Bunk408b6642005-05-01 08:59:29 -0700476static char *cx88_pci_irqs[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477 "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
478 "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
479 "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
480 "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
481};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
483void cx88_print_irqbits(char *name, char *tag, char **strings,
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300484 int len, u32 bits, u32 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485{
486 unsigned int i;
487
488 printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300489 for (i = 0; i < len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 if (!(bits & (1 << i)))
491 continue;
492 if (strings[i])
493 printk(" %s", strings[i]);
494 else
495 printk(" %d", i);
496 if (!(mask & (1 << i)))
497 continue;
498 printk("*");
499 }
500 printk("\n");
501}
502
503/* ------------------------------------------------------------------ */
504
505int cx88_core_irq(struct cx88_core *core, u32 status)
506{
507 int handled = 0;
508
Trent Piepho8ddac9e2007-08-18 06:57:55 -0300509 if (status & PCI_INT_IR_SMPINT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 cx88_ir_irq(core);
511 handled++;
512 }
513 if (!handled)
514 cx88_print_irqbits(core->name, "irq pci",
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300515 cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
516 status, core->pci_irqmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 return handled;
518}
519
520void cx88_wakeup(struct cx88_core *core,
521 struct cx88_dmaqueue *q, u32 count)
522{
523 struct cx88_buffer *buf;
524 int bc;
525
526 for (bc = 0;; bc++) {
527 if (list_empty(&q->active))
528 break;
529 buf = list_entry(q->active.next,
530 struct cx88_buffer, vb.queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 /* count comes from the hw and is is 16bit wide --
532 * this trick handles wrap-arounds correctly for
533 * up to 32767 buffers in flight... */
534 if ((s16) (count - buf->count) < 0)
535 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 do_gettimeofday(&buf->vb.ts);
537 dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
538 count, buf->count);
539 buf->vb.state = STATE_DONE;
540 list_del(&buf->vb.queue);
541 wake_up(&buf->vb.done);
542 }
543 if (list_empty(&q->active)) {
544 del_timer(&q->timeout);
545 } else {
546 mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
547 }
548 if (bc != 1)
549 printk("%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc);
550}
551
552void cx88_shutdown(struct cx88_core *core)
553{
554 /* disable RISC controller + IRQs */
555 cx_write(MO_DEV_CNTRL2, 0);
556
557 /* stop dma transfers */
558 cx_write(MO_VID_DMACNTRL, 0x0);
559 cx_write(MO_AUD_DMACNTRL, 0x0);
560 cx_write(MO_TS_DMACNTRL, 0x0);
561 cx_write(MO_VIP_DMACNTRL, 0x0);
562 cx_write(MO_GPHST_DMACNTRL, 0x0);
563
564 /* stop interrupts */
565 cx_write(MO_PCI_INTMSK, 0x0);
566 cx_write(MO_VID_INTMSK, 0x0);
567 cx_write(MO_AUD_INTMSK, 0x0);
568 cx_write(MO_TS_INTMSK, 0x0);
569 cx_write(MO_VIP_INTMSK, 0x0);
570 cx_write(MO_GPHST_INTMSK, 0x0);
571
572 /* stop capturing */
573 cx_write(VID_CAPTURE_CONTROL, 0);
574}
575
576int cx88_reset(struct cx88_core *core)
577{
578 dprintk(1,"%s\n",__FUNCTION__);
579 cx88_shutdown(core);
580
581 /* clear irq status */
582 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
583 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
584 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
585
586 /* wait a bit */
587 msleep(100);
588
589 /* init sram */
590 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
591 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
592 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
593 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
594 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
595 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
596 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
597
598 /* misc init ... */
599 cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
600 (1 << 12) | // agc gain
601 (1 << 11) | // adaptibe agc
602 (0 << 10) | // chroma agc
603 (0 << 9) | // ckillen
604 (7)));
605
606 /* setup image format */
607 cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
608
609 /* setup FIFO Threshholds */
610 cx_write(MO_PDMA_STHRSH, 0x0807);
611 cx_write(MO_PDMA_DTHRSH, 0x0807);
612
613 /* fixes flashing of image */
614 cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
615 cx_write(MO_AGC_BACK_VBI, 0x00E00555);
616
617 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
618 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
619 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
620
621 /* Reset on-board parts */
622 cx_write(MO_SRST_IO, 0);
623 msleep(10);
624 cx_write(MO_SRST_IO, 1);
625
626 return 0;
627}
628
629/* ------------------------------------------------------------------ */
630
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300631static unsigned int inline norm_swidth(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300633 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634}
635
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300636static unsigned int inline norm_hdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300638 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639}
640
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300641static unsigned int inline norm_vdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300643 return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644}
645
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300646static unsigned int inline norm_fsc8(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300648 if (norm & V4L2_STD_PAL_M)
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300649 return 28604892; // 3.575611 MHz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300651 if (norm & (V4L2_STD_PAL_Nc))
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300652 return 28656448; // 3.582056 MHz
653
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300654 if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300655 return 28636360; // 3.57954545 MHz +/- 10 Hz
656
657 /* SECAM have also different sub carrier for chroma,
658 but step_db and step_dr, at cx88_set_tvnorm already handles that.
659
660 The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
661 */
662
663 return 35468950; // 4.43361875 MHz +/- 5 Hz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664}
665
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300666static unsigned int inline norm_htotal(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667{
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700668
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300669 unsigned int fsc4=norm_fsc8(norm)/2;
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700670
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300671 /* returns 4*FSC / vtotal / frames per seconds */
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300672 return (norm & V4L2_STD_625_50) ?
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300673 ((fsc4+312)/625+12)/25 :
674 ((fsc4+262)/525*1001+15000)/30000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675}
676
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300677static unsigned int inline norm_vbipack(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300679 return (norm & V4L2_STD_625_50) ? 511 : 400;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680}
681
682int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
683 enum v4l2_field field)
684{
685 unsigned int swidth = norm_swidth(core->tvnorm);
686 unsigned int sheight = norm_maxh(core->tvnorm);
687 u32 value;
688
689 dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
690 V4L2_FIELD_HAS_TOP(field) ? "T" : "",
691 V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300692 v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 if (!V4L2_FIELD_HAS_BOTH(field))
694 height *= 2;
695
696 // recalc H delay and scale registers
697 value = (width * norm_hdelay(core->tvnorm)) / swidth;
698 value &= 0x3fe;
699 cx_write(MO_HDELAY_EVEN, value);
700 cx_write(MO_HDELAY_ODD, value);
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300701 dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702
703 value = (swidth * 4096 / width) - 4096;
704 cx_write(MO_HSCALE_EVEN, value);
705 cx_write(MO_HSCALE_ODD, value);
706 dprintk(1,"set_scale: hscale 0x%04x\n", value);
707
708 cx_write(MO_HACTIVE_EVEN, width);
709 cx_write(MO_HACTIVE_ODD, width);
710 dprintk(1,"set_scale: hactive 0x%04x\n", width);
711
712 // recalc V scale Register (delay is constant)
713 cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
714 cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
715 dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
716
717 value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
718 cx_write(MO_VSCALE_EVEN, value);
719 cx_write(MO_VSCALE_ODD, value);
720 dprintk(1,"set_scale: vscale 0x%04x\n", value);
721
722 cx_write(MO_VACTIVE_EVEN, sheight);
723 cx_write(MO_VACTIVE_ODD, sheight);
724 dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
725
726 // setup filters
727 value = 0;
728 value |= (1 << 19); // CFILT (default)
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300729 if (core->tvnorm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 value |= (1 << 15);
731 value |= (1 << 16);
732 }
Trent Piepho6a59d642007-08-15 14:41:57 -0300733 if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 value |= (1 << 13) | (1 << 5);
735 if (V4L2_FIELD_INTERLACED == field)
736 value |= (1 << 3); // VINT (interlaced vertical scaling)
737 if (width < 385)
738 value |= (1 << 0); // 3-tap interpolation
739 if (width < 193)
740 value |= (1 << 1); // 5-tap interpolation
741 if (nocomb)
742 value |= (3 << 5); // disable comb filter
743
744 cx_write(MO_FILTER_EVEN, value);
745 cx_write(MO_FILTER_ODD, value);
746 dprintk(1,"set_scale: filter 0x%04x\n", value);
747
748 return 0;
749}
750
751static const u32 xtal = 28636363;
752
753static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
754{
755 static u32 pre[] = { 0, 0, 0, 3, 2, 1 };
756 u64 pll;
757 u32 reg;
758 int i;
759
760 if (prescale < 2)
761 prescale = 2;
762 if (prescale > 5)
763 prescale = 5;
764
765 pll = ofreq * 8 * prescale * (u64)(1 << 20);
766 do_div(pll,xtal);
767 reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
768 if (((reg >> 20) & 0x3f) < 14) {
769 printk("%s/0: pll out of range\n",core->name);
770 return -1;
771 }
772
773 dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
774 reg, cx_read(MO_PLL_REG), ofreq);
775 cx_write(MO_PLL_REG, reg);
776 for (i = 0; i < 100; i++) {
777 reg = cx_read(MO_DEVICE_STATUS);
778 if (reg & (1<<2)) {
779 dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
780 prescale,ofreq);
781 return 0;
782 }
783 dprintk(1,"pll not locked yet, waiting ...\n");
784 msleep(10);
785 }
786 dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
787 return -1;
788}
789
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800790int cx88_start_audio_dma(struct cx88_core *core)
791{
Marcin Rudowski17801f52006-02-06 09:15:14 -0200792 /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
793 int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300794
795 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
796 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
797 return 0;
798
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800799 /* setup fifo + format */
Marcin Rudowski17801f52006-02-06 09:15:14 -0200800 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
801 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800802
Marcin Rudowski17801f52006-02-06 09:15:14 -0200803 cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
804 cx_write(MO_AUDR_LNGTH, bpl); /* fifo bpl size */
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800805
806 /* start dma */
807 cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300808
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800809 return 0;
810}
811
812int cx88_stop_audio_dma(struct cx88_core *core)
813{
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300814 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
815 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
816 return 0;
817
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800818 /* stop dma */
819 cx_write(MO_AUD_DMACNTRL, 0x0000);
820
821 return 0;
822}
823
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824static int set_tvaudio(struct cx88_core *core)
825{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300826 v4l2_std_id norm = core->tvnorm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Trent Piepho6a59d642007-08-15 14:41:57 -0300828 if (CX88_VMUX_TELEVISION != INPUT(core->input).type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 return 0;
830
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300831 if (V4L2_STD_PAL_BG & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800832 core->tvaudio = WW_BG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300834 } else if (V4L2_STD_PAL_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800835 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300837 } else if (V4L2_STD_PAL_I & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800838 core->tvaudio = WW_I;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300840 } else if (V4L2_STD_SECAM_L & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800841 core->tvaudio = WW_L;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300843 } else if (V4L2_STD_SECAM_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800844 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300846 } else if ((V4L2_STD_NTSC_M & norm) ||
847 (V4L2_STD_PAL_M & norm)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 core->tvaudio = WW_BTSC;
849
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300850 } else if (V4L2_STD_NTSC_M_JP & norm) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 core->tvaudio = WW_EIAJ;
852
853 } else {
854 printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300855 core->name, v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 core->tvaudio = 0;
857 return 0;
858 }
859
860 cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
861 cx88_set_tvaudio(core);
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -0700862 /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800864/*
865 This should be needed only on cx88-alsa. It seems that some cx88 chips have
866 bugs and does require DMA enabled for it to work.
867 */
868 cx88_start_audio_dma(core);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869 return 0;
870}
871
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800872
873
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300874int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875{
876 u32 fsc8;
877 u32 adc_clock;
878 u32 vdec_clock;
879 u32 step_db,step_dr;
880 u64 tmp64;
881 u32 bdelay,agcdelay,htotal;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300882 u32 cxiformat, cxoformat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
884 core->tvnorm = norm;
885 fsc8 = norm_fsc8(norm);
886 adc_clock = xtal;
887 vdec_clock = fsc8;
888 step_db = fsc8;
889 step_dr = fsc8;
890
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300891 if (norm & V4L2_STD_NTSC_M_JP) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300892 cxiformat = VideoFormatNTSCJapan;
893 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300894 } else if (norm & V4L2_STD_NTSC_443) {
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300895 cxiformat = VideoFormatNTSC443;
896 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300897 } else if (norm & V4L2_STD_PAL_M) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300898 cxiformat = VideoFormatPALM;
899 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300900 } else if (norm & V4L2_STD_PAL_N) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300901 cxiformat = VideoFormatPALN;
902 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300903 } else if (norm & V4L2_STD_PAL_Nc) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300904 cxiformat = VideoFormatPALNC;
905 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300906 } else if (norm & V4L2_STD_PAL_60) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300907 cxiformat = VideoFormatPAL60;
908 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300909 } else if (norm & V4L2_STD_NTSC) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300910 cxiformat = VideoFormatNTSC;
911 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300912 } else if (norm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 step_db = 4250000 * 8;
914 step_dr = 4406250 * 8;
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300915
916 cxiformat = VideoFormatSECAM;
917 cxoformat = 0x181f0008;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300918 } else { /* PAL */
919 cxiformat = VideoFormatPAL;
920 cxoformat = 0x181f0008;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 }
922
923 dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300924 v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
925 step_db, step_dr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 set_pll(core,2,vdec_clock);
927
928 dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300929 cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
930 cx_andor(MO_INPUT_FORMAT, 0xf, cxiformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 // FIXME: as-is from DScaler
933 dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300934 cxoformat, cx_read(MO_OUTPUT_FORMAT));
935 cx_write(MO_OUTPUT_FORMAT, cxoformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936
937 // MO_SCONV_REG = adc clock / video dec clock * 2^17
938 tmp64 = adc_clock * (u64)(1 << 17);
939 do_div(tmp64, vdec_clock);
940 dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
941 (u32)tmp64, cx_read(MO_SCONV_REG));
942 cx_write(MO_SCONV_REG, (u32)tmp64);
943
944 // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
945 tmp64 = step_db * (u64)(1 << 22);
946 do_div(tmp64, vdec_clock);
947 dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
948 (u32)tmp64, cx_read(MO_SUB_STEP));
949 cx_write(MO_SUB_STEP, (u32)tmp64);
950
951 // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
952 tmp64 = step_dr * (u64)(1 << 22);
953 do_div(tmp64, vdec_clock);
954 dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
955 (u32)tmp64, cx_read(MO_SUB_STEP_DR));
956 cx_write(MO_SUB_STEP_DR, (u32)tmp64);
957
958 // bdelay + agcdelay
959 bdelay = vdec_clock * 65 / 20000000 + 21;
960 agcdelay = vdec_clock * 68 / 20000000 + 15;
961 dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
962 (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
963 cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
964
965 // htotal
966 tmp64 = norm_htotal(norm) * (u64)vdec_clock;
967 do_div(tmp64, fsc8);
Mauro Carvalho Chehabccbf64b2006-09-29 12:39:36 -0300968 htotal = (u32)tmp64 | (HLNotchFilter4xFsc << 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
970 htotal, cx_read(MO_HTOTAL), (u32)tmp64);
971 cx_write(MO_HTOTAL, htotal);
972
Trent Piepho3eb73172006-05-23 23:54:44 -0300973 // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
974 // the effective vbi offset ~244 samples, the same as the Bt8x8
975 cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
977 // this is needed as well to set all tvnorm parameter
978 cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
979
980 // audio
981 set_tvaudio(core);
982
983 // tell i2c chips
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300984 cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
986 // done
987 return 0;
988}
989
990/* ------------------------------------------------------------------ */
991
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992struct video_device *cx88_vdev_init(struct cx88_core *core,
993 struct pci_dev *pci,
994 struct video_device *template,
995 char *type)
996{
997 struct video_device *vfd;
998
999 vfd = video_device_alloc();
1000 if (NULL == vfd)
1001 return NULL;
1002 *vfd = *template;
1003 vfd->minor = -1;
1004 vfd->dev = &pci->dev;
1005 vfd->release = video_device_release;
1006 snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
Trent Piepho6a59d642007-08-15 14:41:57 -03001007 core->name, type, core->board.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 return vfd;
1009}
1010
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011struct cx88_core* cx88_core_get(struct pci_dev *pci)
1012{
1013 struct cx88_core *core;
1014 struct list_head *item;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001016 mutex_lock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 list_for_each(item,&cx88_devlist) {
1018 core = list_entry(item, struct cx88_core, devlist);
1019 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 */