blob: f2fb9f30bfc185c025a06793294adb19bc06ad48 [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>
Hans Verkuil35ea11f2008-07-20 08:12:02 -030043#include <media/v4l2-ioctl.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
46MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
47MODULE_LICENSE("GPL");
48
49/* ------------------------------------------------------------------ */
50
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030051static unsigned int core_debug;
Linus Torvalds1da177e2005-04-16 15:20:36 -070052module_param(core_debug,int,0644);
53MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
54
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030055static unsigned int nicam;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056module_param(nicam,int,0644);
57MODULE_PARM_DESC(nicam,"tv audio is nicam");
58
Douglas Schilling Landgrafff699e62008-04-22 14:41:48 -030059static unsigned int nocomb;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060module_param(nocomb,int,0644);
61MODULE_PARM_DESC(nocomb,"disable comb filter");
62
63#define dprintk(level,fmt, arg...) if (core_debug >= level) \
64 printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
65
66static unsigned int cx88_devcount;
67static LIST_HEAD(cx88_devlist);
Ingo Molnar1e4baed2006-01-15 07:52:23 -020068static DEFINE_MUTEX(devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#define NO_SYNC_LINE (-1U)
71
Trent Piepho05b27232007-08-24 01:06:34 -030072/* @lpi: lines per IRQ, or 0 to not generate irqs. Note: IRQ to be
73 generated _after_ lpi lines are transferred. */
Al Virod8eaa582008-05-21 00:31:51 -030074static __le32* cx88_risc_field(__le32 *rp, struct scatterlist *sglist,
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 unsigned int offset, u32 sync_line,
76 unsigned int bpl, unsigned int padding,
Trent Piepho05b27232007-08-24 01:06:34 -030077 unsigned int lines, unsigned int lpi)
Linus Torvalds1da177e2005-04-16 15:20:36 -070078{
79 struct scatterlist *sg;
Trent Piepho05b27232007-08-24 01:06:34 -030080 unsigned int line,todo,sol;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
82 /* sync instruction */
83 if (sync_line != NO_SYNC_LINE)
84 *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
85
86 /* scan lines */
87 sg = sglist;
88 for (line = 0; line < lines; line++) {
89 while (offset && offset >= sg_dma_len(sg)) {
90 offset -= sg_dma_len(sg);
91 sg++;
92 }
Trent Piepho05b27232007-08-24 01:06:34 -030093 if (lpi && line>0 && !(line % lpi))
94 sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC;
95 else
96 sol = RISC_SOL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 if (bpl <= sg_dma_len(sg)-offset) {
98 /* fits into current chunk */
Trent Piepho05b27232007-08-24 01:06:34 -030099 *(rp++)=cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl);
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800100 *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
101 offset+=bpl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 } else {
Peter Naullsd1009bd2006-08-08 09:10:05 -0300103 /* scanline needs to be split */
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800104 todo = bpl;
Trent Piepho05b27232007-08-24 01:06:34 -0300105 *(rp++)=cpu_to_le32(RISC_WRITE|sol|
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 (sg_dma_len(sg)-offset));
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800107 *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
108 todo -= (sg_dma_len(sg)-offset);
109 offset = 0;
110 sg++;
111 while (todo > sg_dma_len(sg)) {
Mauro Carvalho Chehabf2421ca2005-11-08 21:37:45 -0800112 *(rp++)=cpu_to_le32(RISC_WRITE|
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 sg_dma_len(sg));
Mauro Carvalho Chehabf2421ca2005-11-08 21:37:45 -0800114 *(rp++)=cpu_to_le32(sg_dma_address(sg));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 todo -= sg_dma_len(sg);
116 sg++;
117 }
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800118 *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 *(rp++)=cpu_to_le32(sg_dma_address(sg));
120 offset += todo;
121 }
122 offset += padding;
123 }
124
125 return rp;
126}
127
128int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
129 struct scatterlist *sglist,
130 unsigned int top_offset, unsigned int bottom_offset,
131 unsigned int bpl, unsigned int padding, unsigned int lines)
132{
133 u32 instructions,fields;
Al Virod8eaa582008-05-21 00:31:51 -0300134 __le32 *rp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 int rc;
136
137 fields = 0;
138 if (UNSET != top_offset)
139 fields++;
140 if (UNSET != bottom_offset)
141 fields++;
142
143 /* estimate risc mem: worst case is one write per page border +
Duncan Sandsbba3ad72006-04-11 10:18:57 -0300144 one write per scan line + syncs + jump (all 2 dwords). Padding
145 can cause next bpl to start close to a page border. First DMA
146 region may be smaller than PAGE_SIZE */
147 instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines);
148 instructions += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
150 return rc;
151
152 /* write risc instructions */
153 rp = risc->cpu;
154 if (UNSET != top_offset)
155 rp = cx88_risc_field(rp, sglist, top_offset, 0,
Trent Piepho05b27232007-08-24 01:06:34 -0300156 bpl, padding, lines, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 if (UNSET != bottom_offset)
158 rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200,
Trent Piepho05b27232007-08-24 01:06:34 -0300159 bpl, padding, lines, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
161 /* save pointer to jmp instruction address */
162 risc->jmp = rp;
Duncan Sands4a287cf2006-02-27 00:09:48 -0300163 BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 return 0;
165}
166
167int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
168 struct scatterlist *sglist, unsigned int bpl,
Trent Piepho05b27232007-08-24 01:06:34 -0300169 unsigned int lines, unsigned int lpi)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170{
171 u32 instructions;
Al Virod8eaa582008-05-21 00:31:51 -0300172 __le32 *rp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 int rc;
174
175 /* estimate risc mem: worst case is one write per page border +
Duncan Sandsbba3ad72006-04-11 10:18:57 -0300176 one write per scan line + syncs + jump (all 2 dwords). Here
177 there is no padding and no sync. First DMA region may be smaller
178 than PAGE_SIZE */
179 instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
180 instructions += 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
182 return rc;
183
184 /* write risc instructions */
185 rp = risc->cpu;
Trent Piepho05b27232007-08-24 01:06:34 -0300186 rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines, lpi);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
188 /* save pointer to jmp instruction address */
189 risc->jmp = rp;
Duncan Sands4a287cf2006-02-27 00:09:48 -0300190 BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 return 0;
192}
193
194int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
195 u32 reg, u32 mask, u32 value)
196{
Al Virod8eaa582008-05-21 00:31:51 -0300197 __le32 *rp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 int rc;
199
200 if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0)
201 return rc;
202
203 /* write risc instructions */
204 rp = risc->cpu;
205 *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM);
206 *(rp++) = cpu_to_le32(reg);
207 *(rp++) = cpu_to_le32(value);
208 *(rp++) = cpu_to_le32(mask);
209 *(rp++) = cpu_to_le32(RISC_JUMP);
210 *(rp++) = cpu_to_le32(risc->dma);
211 return 0;
212}
213
214void
Mauro Carvalho Chehabc7b0ac02006-03-10 12:29:15 -0300215cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216{
Mauro Carvalho Chehabc1accaa2007-08-23 16:37:49 -0300217 struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
218
Eric Sesterhennae246012006-03-13 13:17:11 -0300219 BUG_ON(in_interrupt());
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 videobuf_waiton(&buf->vb,0,0);
Mauro Carvalho Chehabc1accaa2007-08-23 16:37:49 -0300221 videobuf_dma_unmap(q, dma);
222 videobuf_dma_free(dma);
Guennadi Liakhovetskia920e422008-04-22 14:46:02 -0300223 btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
Brandon Philips0fc06862007-11-06 20:02:36 -0300224 buf->vb.state = VIDEOBUF_NEEDS_INIT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225}
226
227/* ------------------------------------------------------------------ */
228/* our SRAM memory layout */
229
230/* we are going to put all thr risc programs into host memory, so we
231 * can use the whole SDRAM for the DMA fifos. To simplify things, we
232 * use a static memory layout. That surely will waste memory in case
233 * we don't use all DMA channels at the same time (which will be the
234 * case most of the time). But that still gives us enougth FIFO space
235 * to be able to deal with insane long pci latencies ...
236 *
237 * FIFO space allocations:
238 * channel 21 (y video) - 10.0k
239 * channel 22 (u video) - 2.0k
240 * channel 23 (v video) - 2.0k
241 * channel 24 (vbi) - 4.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200242 * channels 25+26 (audio) - 4.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 * channel 28 (mpeg) - 4.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200244 * TOTAL = 29.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 *
246 * Every channel has 160 bytes control data (64 bytes instruction
247 * queue and 6 CDT entries), which is close to 2k total.
248 *
249 * Address layout:
250 * 0x0000 - 0x03ff CMDs / reserved
251 * 0x0400 - 0x0bff instruction queues + CDs
252 * 0x0c00 - FIFOs
253 */
254
255struct sram_channel cx88_sram_channels[] = {
256 [SRAM_CH21] = {
257 .name = "video y / packed",
258 .cmds_start = 0x180040,
259 .ctrl_start = 0x180400,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800260 .cdt = 0x180400 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 .fifo_start = 0x180c00,
262 .fifo_size = 0x002800,
263 .ptr1_reg = MO_DMA21_PTR1,
264 .ptr2_reg = MO_DMA21_PTR2,
265 .cnt1_reg = MO_DMA21_CNT1,
266 .cnt2_reg = MO_DMA21_CNT2,
267 },
268 [SRAM_CH22] = {
269 .name = "video u",
270 .cmds_start = 0x180080,
271 .ctrl_start = 0x1804a0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800272 .cdt = 0x1804a0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 .fifo_start = 0x183400,
274 .fifo_size = 0x000800,
275 .ptr1_reg = MO_DMA22_PTR1,
276 .ptr2_reg = MO_DMA22_PTR2,
277 .cnt1_reg = MO_DMA22_CNT1,
278 .cnt2_reg = MO_DMA22_CNT2,
279 },
280 [SRAM_CH23] = {
281 .name = "video v",
282 .cmds_start = 0x1800c0,
283 .ctrl_start = 0x180540,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800284 .cdt = 0x180540 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 .fifo_start = 0x183c00,
286 .fifo_size = 0x000800,
287 .ptr1_reg = MO_DMA23_PTR1,
288 .ptr2_reg = MO_DMA23_PTR2,
289 .cnt1_reg = MO_DMA23_CNT1,
290 .cnt2_reg = MO_DMA23_CNT2,
291 },
292 [SRAM_CH24] = {
293 .name = "vbi",
294 .cmds_start = 0x180100,
295 .ctrl_start = 0x1805e0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800296 .cdt = 0x1805e0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 .fifo_start = 0x184400,
298 .fifo_size = 0x001000,
299 .ptr1_reg = MO_DMA24_PTR1,
300 .ptr2_reg = MO_DMA24_PTR2,
301 .cnt1_reg = MO_DMA24_CNT1,
302 .cnt2_reg = MO_DMA24_CNT2,
303 },
304 [SRAM_CH25] = {
305 .name = "audio from",
306 .cmds_start = 0x180140,
307 .ctrl_start = 0x180680,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800308 .cdt = 0x180680 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 .fifo_start = 0x185400,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200310 .fifo_size = 0x001000,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 .ptr1_reg = MO_DMA25_PTR1,
312 .ptr2_reg = MO_DMA25_PTR2,
313 .cnt1_reg = MO_DMA25_CNT1,
314 .cnt2_reg = MO_DMA25_CNT2,
315 },
316 [SRAM_CH26] = {
317 .name = "audio to",
318 .cmds_start = 0x180180,
319 .ctrl_start = 0x180720,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800320 .cdt = 0x180680 + 64, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321 .fifo_start = 0x185400, /* same as audio IN */
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200322 .fifo_size = 0x001000, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 .ptr1_reg = MO_DMA26_PTR1,
324 .ptr2_reg = MO_DMA26_PTR2,
325 .cnt1_reg = MO_DMA26_CNT1,
326 .cnt2_reg = MO_DMA26_CNT2,
327 },
328 [SRAM_CH28] = {
329 .name = "mpeg",
330 .cmds_start = 0x180200,
331 .ctrl_start = 0x1807C0,
332 .cdt = 0x1807C0 + 64,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200333 .fifo_start = 0x186400,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 .fifo_size = 0x001000,
335 .ptr1_reg = MO_DMA28_PTR1,
336 .ptr2_reg = MO_DMA28_PTR2,
337 .cnt1_reg = MO_DMA28_CNT1,
338 .cnt2_reg = MO_DMA28_CNT2,
339 },
340};
341
342int cx88_sram_channel_setup(struct cx88_core *core,
343 struct sram_channel *ch,
344 unsigned int bpl, u32 risc)
345{
346 unsigned int i,lines;
347 u32 cdt;
348
349 bpl = (bpl + 7) & ~7; /* alignment */
350 cdt = ch->cdt;
351 lines = ch->fifo_size / bpl;
352 if (lines > 6)
353 lines = 6;
354 BUG_ON(lines < 2);
355
356 /* write CDT */
357 for (i = 0; i < lines; i++)
358 cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
359
360 /* write CMDS */
361 cx_write(ch->cmds_start + 0, risc);
362 cx_write(ch->cmds_start + 4, cdt);
363 cx_write(ch->cmds_start + 8, (lines*16) >> 3);
364 cx_write(ch->cmds_start + 12, ch->ctrl_start);
365 cx_write(ch->cmds_start + 16, 64 >> 2);
366 for (i = 20; i < 64; i += 4)
367 cx_write(ch->cmds_start + i, 0);
368
369 /* fill registers */
370 cx_write(ch->ptr1_reg, ch->fifo_start);
371 cx_write(ch->ptr2_reg, cdt);
372 cx_write(ch->cnt1_reg, (bpl >> 3) -1);
373 cx_write(ch->cnt2_reg, (lines*16) >> 3);
374
375 dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
376 return 0;
377}
378
379/* ------------------------------------------------------------------ */
380/* debug helper code */
381
Peter Hagervallf9e7a022005-11-08 21:36:29 -0800382static int cx88_risc_decode(u32 risc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383{
384 static char *instr[16] = {
385 [ RISC_SYNC >> 28 ] = "sync",
386 [ RISC_WRITE >> 28 ] = "write",
387 [ RISC_WRITEC >> 28 ] = "writec",
388 [ RISC_READ >> 28 ] = "read",
389 [ RISC_READC >> 28 ] = "readc",
390 [ RISC_JUMP >> 28 ] = "jump",
391 [ RISC_SKIP >> 28 ] = "skip",
392 [ RISC_WRITERM >> 28 ] = "writerm",
393 [ RISC_WRITECM >> 28 ] = "writecm",
394 [ RISC_WRITECR >> 28 ] = "writecr",
395 };
396 static int incr[16] = {
397 [ RISC_WRITE >> 28 ] = 2,
398 [ RISC_JUMP >> 28 ] = 2,
399 [ RISC_WRITERM >> 28 ] = 3,
400 [ RISC_WRITECM >> 28 ] = 3,
401 [ RISC_WRITECR >> 28 ] = 4,
402 };
403 static char *bits[] = {
404 "12", "13", "14", "resync",
405 "cnt0", "cnt1", "18", "19",
406 "20", "21", "22", "23",
407 "irq1", "irq2", "eol", "sol",
408 };
409 int i;
410
411 printk("0x%08x [ %s", risc,
412 instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
413 for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
414 if (risc & (1 << (i + 12)))
415 printk(" %s",bits[i]);
416 printk(" count=%d ]\n", risc & 0xfff);
417 return incr[risc >> 28] ? incr[risc >> 28] : 1;
418}
419
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
421void cx88_sram_channel_dump(struct cx88_core *core,
422 struct sram_channel *ch)
423{
424 static char *name[] = {
425 "initial risc",
426 "cdt base",
427 "cdt size",
428 "iq base",
429 "iq size",
430 "risc pc",
431 "iq wr ptr",
432 "iq rd ptr",
433 "cdt current",
434 "pci target",
435 "line / byte",
436 };
437 u32 risc;
438 unsigned int i,j,n;
439
440 printk("%s: %s - dma channel status dump\n",
441 core->name,ch->name);
442 for (i = 0; i < ARRAY_SIZE(name); i++)
443 printk("%s: cmds: %-12s: 0x%08x\n",
444 core->name,name[i],
445 cx_read(ch->cmds_start + 4*i));
Trent Piepho16cf1d02007-08-21 08:19:16 -0300446 for (n = 1, i = 0; i < 4; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 risc = cx_read(ch->cmds_start + 4 * (i+11));
448 printk("%s: risc%d: ", core->name, i);
Trent Piepho16cf1d02007-08-21 08:19:16 -0300449 if (--n)
450 printk("0x%08x [ arg #%d ]\n", risc, n);
451 else
452 n = cx88_risc_decode(risc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 }
454 for (i = 0; i < 16; i += n) {
455 risc = cx_read(ch->ctrl_start + 4 * i);
456 printk("%s: iq %x: ", core->name, i);
457 n = cx88_risc_decode(risc);
458 for (j = 1; j < n; j++) {
459 risc = cx_read(ch->ctrl_start + 4 * (i+j));
460 printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
461 core->name, i+j, risc, j);
462 }
463 }
464
465 printk("%s: fifo: 0x%08x -> 0x%x\n",
466 core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
467 printk("%s: ctrl: 0x%08x -> 0x%x\n",
468 core->name, ch->ctrl_start, ch->ctrl_start+6*16);
469 printk("%s: ptr1_reg: 0x%08x\n",
470 core->name,cx_read(ch->ptr1_reg));
471 printk("%s: ptr2_reg: 0x%08x\n",
472 core->name,cx_read(ch->ptr2_reg));
473 printk("%s: cnt1_reg: 0x%08x\n",
474 core->name,cx_read(ch->cnt1_reg));
475 printk("%s: cnt2_reg: 0x%08x\n",
476 core->name,cx_read(ch->cnt2_reg));
477}
478
Adrian Bunk408b6642005-05-01 08:59:29 -0700479static char *cx88_pci_irqs[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
481 "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
482 "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
483 "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
484};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486void cx88_print_irqbits(char *name, char *tag, char **strings,
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300487 int len, u32 bits, u32 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488{
489 unsigned int i;
490
491 printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300492 for (i = 0; i < len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 if (!(bits & (1 << i)))
494 continue;
495 if (strings[i])
496 printk(" %s", strings[i]);
497 else
498 printk(" %d", i);
499 if (!(mask & (1 << i)))
500 continue;
501 printk("*");
502 }
503 printk("\n");
504}
505
506/* ------------------------------------------------------------------ */
507
508int cx88_core_irq(struct cx88_core *core, u32 status)
509{
510 int handled = 0;
511
Trent Piepho8ddac9e2007-08-18 06:57:55 -0300512 if (status & PCI_INT_IR_SMPINT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513 cx88_ir_irq(core);
514 handled++;
515 }
516 if (!handled)
517 cx88_print_irqbits(core->name, "irq pci",
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300518 cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
519 status, core->pci_irqmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 return handled;
521}
522
523void cx88_wakeup(struct cx88_core *core,
524 struct cx88_dmaqueue *q, u32 count)
525{
526 struct cx88_buffer *buf;
527 int bc;
528
529 for (bc = 0;; bc++) {
530 if (list_empty(&q->active))
531 break;
532 buf = list_entry(q->active.next,
533 struct cx88_buffer, vb.queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 /* count comes from the hw and is is 16bit wide --
535 * this trick handles wrap-arounds correctly for
536 * up to 32767 buffers in flight... */
537 if ((s16) (count - buf->count) < 0)
538 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 do_gettimeofday(&buf->vb.ts);
540 dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
541 count, buf->count);
Brandon Philips0fc06862007-11-06 20:02:36 -0300542 buf->vb.state = VIDEOBUF_DONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 list_del(&buf->vb.queue);
544 wake_up(&buf->vb.done);
545 }
546 if (list_empty(&q->active)) {
547 del_timer(&q->timeout);
548 } else {
549 mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
550 }
551 if (bc != 1)
Steven Toth376a8412008-10-16 20:30:45 -0300552 dprintk(2, "%s: %d buffers handled (should be 1)\n",
553 __func__, bc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554}
555
556void cx88_shutdown(struct cx88_core *core)
557{
558 /* disable RISC controller + IRQs */
559 cx_write(MO_DEV_CNTRL2, 0);
560
561 /* stop dma transfers */
562 cx_write(MO_VID_DMACNTRL, 0x0);
563 cx_write(MO_AUD_DMACNTRL, 0x0);
564 cx_write(MO_TS_DMACNTRL, 0x0);
565 cx_write(MO_VIP_DMACNTRL, 0x0);
566 cx_write(MO_GPHST_DMACNTRL, 0x0);
567
568 /* stop interrupts */
569 cx_write(MO_PCI_INTMSK, 0x0);
570 cx_write(MO_VID_INTMSK, 0x0);
571 cx_write(MO_AUD_INTMSK, 0x0);
572 cx_write(MO_TS_INTMSK, 0x0);
573 cx_write(MO_VIP_INTMSK, 0x0);
574 cx_write(MO_GPHST_INTMSK, 0x0);
575
576 /* stop capturing */
577 cx_write(VID_CAPTURE_CONTROL, 0);
578}
579
580int cx88_reset(struct cx88_core *core)
581{
Harvey Harrison32d83ef2008-04-08 23:20:00 -0300582 dprintk(1,"%s\n",__func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 cx88_shutdown(core);
584
585 /* clear irq status */
586 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
587 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
588 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
589
590 /* wait a bit */
591 msleep(100);
592
593 /* init sram */
594 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
595 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
596 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
597 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
598 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
599 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
600 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
601
602 /* misc init ... */
603 cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
604 (1 << 12) | // agc gain
605 (1 << 11) | // adaptibe agc
606 (0 << 10) | // chroma agc
607 (0 << 9) | // ckillen
608 (7)));
609
610 /* setup image format */
611 cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
612
613 /* setup FIFO Threshholds */
614 cx_write(MO_PDMA_STHRSH, 0x0807);
615 cx_write(MO_PDMA_DTHRSH, 0x0807);
616
617 /* fixes flashing of image */
618 cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
619 cx_write(MO_AGC_BACK_VBI, 0x00E00555);
620
621 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
622 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
623 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
624
625 /* Reset on-board parts */
626 cx_write(MO_SRST_IO, 0);
627 msleep(10);
628 cx_write(MO_SRST_IO, 1);
629
630 return 0;
631}
632
633/* ------------------------------------------------------------------ */
634
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300635static unsigned int inline norm_swidth(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_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638}
639
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300640static unsigned int inline norm_hdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300642 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643}
644
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300645static unsigned int inline norm_vdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300647 return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648}
649
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300650static unsigned int inline norm_fsc8(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300652 if (norm & V4L2_STD_PAL_M)
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300653 return 28604892; // 3.575611 MHz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300655 if (norm & (V4L2_STD_PAL_Nc))
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300656 return 28656448; // 3.582056 MHz
657
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300658 if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300659 return 28636360; // 3.57954545 MHz +/- 10 Hz
660
661 /* SECAM have also different sub carrier for chroma,
662 but step_db and step_dr, at cx88_set_tvnorm already handles that.
663
664 The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
665 */
666
667 return 35468950; // 4.43361875 MHz +/- 5 Hz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668}
669
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300670static unsigned int inline norm_htotal(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671{
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700672
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300673 unsigned int fsc4=norm_fsc8(norm)/2;
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700674
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300675 /* returns 4*FSC / vtotal / frames per seconds */
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300676 return (norm & V4L2_STD_625_50) ?
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300677 ((fsc4+312)/625+12)/25 :
678 ((fsc4+262)/525*1001+15000)/30000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679}
680
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300681static unsigned int inline norm_vbipack(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300683 return (norm & V4L2_STD_625_50) ? 511 : 400;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684}
685
686int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
687 enum v4l2_field field)
688{
689 unsigned int swidth = norm_swidth(core->tvnorm);
690 unsigned int sheight = norm_maxh(core->tvnorm);
691 u32 value;
692
693 dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
694 V4L2_FIELD_HAS_TOP(field) ? "T" : "",
695 V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300696 v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 if (!V4L2_FIELD_HAS_BOTH(field))
698 height *= 2;
699
700 // recalc H delay and scale registers
701 value = (width * norm_hdelay(core->tvnorm)) / swidth;
702 value &= 0x3fe;
703 cx_write(MO_HDELAY_EVEN, value);
704 cx_write(MO_HDELAY_ODD, value);
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300705 dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706
707 value = (swidth * 4096 / width) - 4096;
708 cx_write(MO_HSCALE_EVEN, value);
709 cx_write(MO_HSCALE_ODD, value);
710 dprintk(1,"set_scale: hscale 0x%04x\n", value);
711
712 cx_write(MO_HACTIVE_EVEN, width);
713 cx_write(MO_HACTIVE_ODD, width);
714 dprintk(1,"set_scale: hactive 0x%04x\n", width);
715
716 // recalc V scale Register (delay is constant)
717 cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
718 cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
719 dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
720
721 value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
722 cx_write(MO_VSCALE_EVEN, value);
723 cx_write(MO_VSCALE_ODD, value);
724 dprintk(1,"set_scale: vscale 0x%04x\n", value);
725
726 cx_write(MO_VACTIVE_EVEN, sheight);
727 cx_write(MO_VACTIVE_ODD, sheight);
728 dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
729
730 // setup filters
731 value = 0;
732 value |= (1 << 19); // CFILT (default)
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300733 if (core->tvnorm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 value |= (1 << 15);
735 value |= (1 << 16);
736 }
Trent Piepho6a59d642007-08-15 14:41:57 -0300737 if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 value |= (1 << 13) | (1 << 5);
739 if (V4L2_FIELD_INTERLACED == field)
740 value |= (1 << 3); // VINT (interlaced vertical scaling)
741 if (width < 385)
742 value |= (1 << 0); // 3-tap interpolation
743 if (width < 193)
744 value |= (1 << 1); // 5-tap interpolation
745 if (nocomb)
746 value |= (3 << 5); // disable comb filter
747
748 cx_write(MO_FILTER_EVEN, value);
749 cx_write(MO_FILTER_ODD, value);
750 dprintk(1,"set_scale: filter 0x%04x\n", value);
751
752 return 0;
753}
754
755static const u32 xtal = 28636363;
756
757static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
758{
759 static u32 pre[] = { 0, 0, 0, 3, 2, 1 };
760 u64 pll;
761 u32 reg;
762 int i;
763
764 if (prescale < 2)
765 prescale = 2;
766 if (prescale > 5)
767 prescale = 5;
768
769 pll = ofreq * 8 * prescale * (u64)(1 << 20);
770 do_div(pll,xtal);
771 reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
772 if (((reg >> 20) & 0x3f) < 14) {
773 printk("%s/0: pll out of range\n",core->name);
774 return -1;
775 }
776
777 dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
778 reg, cx_read(MO_PLL_REG), ofreq);
779 cx_write(MO_PLL_REG, reg);
780 for (i = 0; i < 100; i++) {
781 reg = cx_read(MO_DEVICE_STATUS);
782 if (reg & (1<<2)) {
783 dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
784 prescale,ofreq);
785 return 0;
786 }
787 dprintk(1,"pll not locked yet, waiting ...\n");
788 msleep(10);
789 }
790 dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
791 return -1;
792}
793
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800794int cx88_start_audio_dma(struct cx88_core *core)
795{
Marcin Rudowski17801f52006-02-06 09:15:14 -0200796 /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
797 int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300798
799 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
800 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
801 return 0;
802
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800803 /* setup fifo + format */
Marcin Rudowski17801f52006-02-06 09:15:14 -0200804 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
805 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800806
Marcin Rudowski17801f52006-02-06 09:15:14 -0200807 cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
808 cx_write(MO_AUDR_LNGTH, bpl); /* fifo bpl size */
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800809
810 /* start dma */
811 cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300812
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800813 return 0;
814}
815
816int cx88_stop_audio_dma(struct cx88_core *core)
817{
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300818 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
819 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
820 return 0;
821
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800822 /* stop dma */
823 cx_write(MO_AUD_DMACNTRL, 0x0000);
824
825 return 0;
826}
827
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828static int set_tvaudio(struct cx88_core *core)
829{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300830 v4l2_std_id norm = core->tvnorm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Trent Piepho6a59d642007-08-15 14:41:57 -0300832 if (CX88_VMUX_TELEVISION != INPUT(core->input).type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 return 0;
834
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300835 if (V4L2_STD_PAL_BG & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800836 core->tvaudio = WW_BG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300838 } else if (V4L2_STD_PAL_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800839 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300841 } else if (V4L2_STD_PAL_I & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800842 core->tvaudio = WW_I;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300844 } else if (V4L2_STD_SECAM_L & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800845 core->tvaudio = WW_L;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
Frederic CANDb84ca9f2008-10-30 04:53:07 -0300847 } else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) {
848 core->tvaudio = WW_BG;
849
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300850 } else if (V4L2_STD_SECAM_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800851 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300853 } else if ((V4L2_STD_NTSC_M & norm) ||
854 (V4L2_STD_PAL_M & norm)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 core->tvaudio = WW_BTSC;
856
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300857 } else if (V4L2_STD_NTSC_M_JP & norm) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 core->tvaudio = WW_EIAJ;
859
860 } else {
861 printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300862 core->name, v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 core->tvaudio = 0;
864 return 0;
865 }
866
867 cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
868 cx88_set_tvaudio(core);
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -0700869 /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800871/*
872 This should be needed only on cx88-alsa. It seems that some cx88 chips have
873 bugs and does require DMA enabled for it to work.
874 */
875 cx88_start_audio_dma(core);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 return 0;
877}
878
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800879
880
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300881int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882{
883 u32 fsc8;
884 u32 adc_clock;
885 u32 vdec_clock;
886 u32 step_db,step_dr;
887 u64 tmp64;
888 u32 bdelay,agcdelay,htotal;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300889 u32 cxiformat, cxoformat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890
891 core->tvnorm = norm;
892 fsc8 = norm_fsc8(norm);
893 adc_clock = xtal;
894 vdec_clock = fsc8;
895 step_db = fsc8;
896 step_dr = fsc8;
897
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300898 if (norm & V4L2_STD_NTSC_M_JP) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300899 cxiformat = VideoFormatNTSCJapan;
900 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300901 } else if (norm & V4L2_STD_NTSC_443) {
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300902 cxiformat = VideoFormatNTSC443;
903 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300904 } else if (norm & V4L2_STD_PAL_M) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300905 cxiformat = VideoFormatPALM;
906 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300907 } else if (norm & V4L2_STD_PAL_N) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300908 cxiformat = VideoFormatPALN;
909 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300910 } else if (norm & V4L2_STD_PAL_Nc) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300911 cxiformat = VideoFormatPALNC;
912 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300913 } else if (norm & V4L2_STD_PAL_60) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300914 cxiformat = VideoFormatPAL60;
915 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300916 } else if (norm & V4L2_STD_NTSC) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300917 cxiformat = VideoFormatNTSC;
918 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300919 } else if (norm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 step_db = 4250000 * 8;
921 step_dr = 4406250 * 8;
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300922
923 cxiformat = VideoFormatSECAM;
924 cxoformat = 0x181f0008;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300925 } else { /* PAL */
926 cxiformat = VideoFormatPAL;
927 cxoformat = 0x181f0008;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 }
929
930 dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300931 v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
932 step_db, step_dr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 set_pll(core,2,vdec_clock);
934
935 dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300936 cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
Frej Drejhammar87a17382008-03-23 22:43:22 -0300937 /* Chroma AGC must be disabled if SECAM is used, we enable it
938 by default on PAL and NTSC */
939 cx_andor(MO_INPUT_FORMAT, 0x40f,
940 norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 // FIXME: as-is from DScaler
943 dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300944 cxoformat, cx_read(MO_OUTPUT_FORMAT));
945 cx_write(MO_OUTPUT_FORMAT, cxoformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
947 // MO_SCONV_REG = adc clock / video dec clock * 2^17
948 tmp64 = adc_clock * (u64)(1 << 17);
949 do_div(tmp64, vdec_clock);
950 dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
951 (u32)tmp64, cx_read(MO_SCONV_REG));
952 cx_write(MO_SCONV_REG, (u32)tmp64);
953
954 // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
955 tmp64 = step_db * (u64)(1 << 22);
956 do_div(tmp64, vdec_clock);
957 dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
958 (u32)tmp64, cx_read(MO_SUB_STEP));
959 cx_write(MO_SUB_STEP, (u32)tmp64);
960
961 // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
962 tmp64 = step_dr * (u64)(1 << 22);
963 do_div(tmp64, vdec_clock);
964 dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
965 (u32)tmp64, cx_read(MO_SUB_STEP_DR));
966 cx_write(MO_SUB_STEP_DR, (u32)tmp64);
967
968 // bdelay + agcdelay
969 bdelay = vdec_clock * 65 / 20000000 + 21;
970 agcdelay = vdec_clock * 68 / 20000000 + 15;
971 dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
972 (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
973 cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
974
975 // htotal
976 tmp64 = norm_htotal(norm) * (u64)vdec_clock;
977 do_div(tmp64, fsc8);
Mauro Carvalho Chehabccbf64b2006-09-29 12:39:36 -0300978 htotal = (u32)tmp64 | (HLNotchFilter4xFsc << 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
980 htotal, cx_read(MO_HTOTAL), (u32)tmp64);
981 cx_write(MO_HTOTAL, htotal);
982
Trent Piepho3eb73172006-05-23 23:54:44 -0300983 // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
984 // the effective vbi offset ~244 samples, the same as the Bt8x8
985 cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
987 // this is needed as well to set all tvnorm parameter
988 cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
989
990 // audio
991 set_tvaudio(core);
992
993 // tell i2c chips
Hans Verkuilb8341e12009-03-29 08:26:01 -0300994 call_all(core, tuner, s_std, norm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
996 // done
997 return 0;
998}
999
1000/* ------------------------------------------------------------------ */
1001
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002struct video_device *cx88_vdev_init(struct cx88_core *core,
1003 struct pci_dev *pci,
1004 struct video_device *template,
1005 char *type)
1006{
1007 struct video_device *vfd;
1008
1009 vfd = video_device_alloc();
1010 if (NULL == vfd)
1011 return NULL;
1012 *vfd = *template;
1013 vfd->minor = -1;
Hans Verkuil9467fe12009-03-14 12:40:51 -03001014 vfd->v4l2_dev = &core->v4l2_dev;
1015 vfd->parent = &pci->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 vfd->release = video_device_release;
1017 snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
Trent Piepho6a59d642007-08-15 14:41:57 -03001018 core->name, type, core->board.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 return vfd;
1020}
1021
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022struct cx88_core* cx88_core_get(struct pci_dev *pci)
1023{
1024 struct cx88_core *core;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001026 mutex_lock(&devlist);
Trent Piepho8bb629e22007-10-10 05:37:40 -03001027 list_for_each_entry(core, &cx88_devlist, devlist) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 if (pci->bus->number != core->pci_bus)
1029 continue;
1030 if (PCI_SLOT(pci->devfn) != core->pci_slot)
1031 continue;
1032
Trent Piephobbc83592007-08-15 14:41:58 -03001033 if (0 != cx88_get_resources(core, pci)) {
1034 mutex_unlock(&devlist);
1035 return NULL;
1036 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037 atomic_inc(&core->refcount);
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001038 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 return core;
1040 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041
Trent Piephobbc83592007-08-15 14:41:58 -03001042 core = cx88_core_create(pci, cx88_devcount);
1043 if (NULL != core) {
1044 cx88_devcount++;
1045 list_add_tail(&core->devlist, &cx88_devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001048 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 return core;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050}
1051
1052void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
1053{
1054 release_mem_region(pci_resource_start(pci,0),
1055 pci_resource_len(pci,0));
1056
1057 if (!atomic_dec_and_test(&core->refcount))
1058 return;
1059
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001060 mutex_lock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 cx88_ir_fini(core);
Hans Verkuilb8341e12009-03-29 08:26:01 -03001062 if (0 == core->i2c_rc) {
1063 if (core->i2c_rtc)
1064 i2c_unregister_device(core->i2c_rtc);
Jean Delvare32697112006-12-10 21:21:33 +01001065 i2c_del_adapter(&core->i2c_adap);
Hans Verkuilb8341e12009-03-29 08:26:01 -03001066 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 list_del(&core->devlist);
1068 iounmap(core->lmmio);
1069 cx88_devcount--;
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001070 mutex_unlock(&devlist);
Hans Verkuil9467fe12009-03-14 12:40:51 -03001071 v4l2_device_unregister(&core->v4l2_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 kfree(core);
1073}
1074
1075/* ------------------------------------------------------------------ */
1076
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077EXPORT_SYMBOL(cx88_print_irqbits);
1078
1079EXPORT_SYMBOL(cx88_core_irq);
1080EXPORT_SYMBOL(cx88_wakeup);
1081EXPORT_SYMBOL(cx88_reset);
1082EXPORT_SYMBOL(cx88_shutdown);
1083
1084EXPORT_SYMBOL(cx88_risc_buffer);
1085EXPORT_SYMBOL(cx88_risc_databuffer);
1086EXPORT_SYMBOL(cx88_risc_stopper);
1087EXPORT_SYMBOL(cx88_free_buffer);
1088
1089EXPORT_SYMBOL(cx88_sram_channels);
1090EXPORT_SYMBOL(cx88_sram_channel_setup);
1091EXPORT_SYMBOL(cx88_sram_channel_dump);
1092
1093EXPORT_SYMBOL(cx88_set_tvnorm);
1094EXPORT_SYMBOL(cx88_set_scale);
1095
1096EXPORT_SYMBOL(cx88_vdev_init);
1097EXPORT_SYMBOL(cx88_core_get);
1098EXPORT_SYMBOL(cx88_core_put);
1099
Mauro Carvalho Chehab13595a52007-10-01 08:51:39 -03001100EXPORT_SYMBOL(cx88_ir_start);
1101EXPORT_SYMBOL(cx88_ir_stop);
1102
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103/*
1104 * Local variables:
1105 * c-basic-offset: 8
1106 * End:
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -07001107 * 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 -07001108 */