blob: d656fec5901086469fb750eeced37588b7702ccc [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)
Harvey Harrison32d83ef2008-04-08 23:20:00 -0300552 printk("%s: %d buffers handled (should be 1)\n",__func__,bc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553}
554
555void cx88_shutdown(struct cx88_core *core)
556{
557 /* disable RISC controller + IRQs */
558 cx_write(MO_DEV_CNTRL2, 0);
559
560 /* stop dma transfers */
561 cx_write(MO_VID_DMACNTRL, 0x0);
562 cx_write(MO_AUD_DMACNTRL, 0x0);
563 cx_write(MO_TS_DMACNTRL, 0x0);
564 cx_write(MO_VIP_DMACNTRL, 0x0);
565 cx_write(MO_GPHST_DMACNTRL, 0x0);
566
567 /* stop interrupts */
568 cx_write(MO_PCI_INTMSK, 0x0);
569 cx_write(MO_VID_INTMSK, 0x0);
570 cx_write(MO_AUD_INTMSK, 0x0);
571 cx_write(MO_TS_INTMSK, 0x0);
572 cx_write(MO_VIP_INTMSK, 0x0);
573 cx_write(MO_GPHST_INTMSK, 0x0);
574
575 /* stop capturing */
576 cx_write(VID_CAPTURE_CONTROL, 0);
577}
578
579int cx88_reset(struct cx88_core *core)
580{
Harvey Harrison32d83ef2008-04-08 23:20:00 -0300581 dprintk(1,"%s\n",__func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 cx88_shutdown(core);
583
584 /* clear irq status */
585 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
586 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
587 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
588
589 /* wait a bit */
590 msleep(100);
591
592 /* init sram */
593 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
594 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
595 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
596 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
597 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
598 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
599 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
600
601 /* misc init ... */
602 cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
603 (1 << 12) | // agc gain
604 (1 << 11) | // adaptibe agc
605 (0 << 10) | // chroma agc
606 (0 << 9) | // ckillen
607 (7)));
608
609 /* setup image format */
610 cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
611
612 /* setup FIFO Threshholds */
613 cx_write(MO_PDMA_STHRSH, 0x0807);
614 cx_write(MO_PDMA_DTHRSH, 0x0807);
615
616 /* fixes flashing of image */
617 cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
618 cx_write(MO_AGC_BACK_VBI, 0x00E00555);
619
620 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
621 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
622 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
623
624 /* Reset on-board parts */
625 cx_write(MO_SRST_IO, 0);
626 msleep(10);
627 cx_write(MO_SRST_IO, 1);
628
629 return 0;
630}
631
632/* ------------------------------------------------------------------ */
633
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300634static unsigned int inline norm_swidth(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300636 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637}
638
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300639static unsigned int inline norm_hdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300641 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642}
643
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300644static unsigned int inline norm_vdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300646 return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647}
648
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300649static unsigned int inline norm_fsc8(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300651 if (norm & V4L2_STD_PAL_M)
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300652 return 28604892; // 3.575611 MHz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300654 if (norm & (V4L2_STD_PAL_Nc))
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300655 return 28656448; // 3.582056 MHz
656
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300657 if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300658 return 28636360; // 3.57954545 MHz +/- 10 Hz
659
660 /* SECAM have also different sub carrier for chroma,
661 but step_db and step_dr, at cx88_set_tvnorm already handles that.
662
663 The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
664 */
665
666 return 35468950; // 4.43361875 MHz +/- 5 Hz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667}
668
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300669static unsigned int inline norm_htotal(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670{
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700671
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300672 unsigned int fsc4=norm_fsc8(norm)/2;
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700673
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300674 /* returns 4*FSC / vtotal / frames per seconds */
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300675 return (norm & V4L2_STD_625_50) ?
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300676 ((fsc4+312)/625+12)/25 :
677 ((fsc4+262)/525*1001+15000)/30000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678}
679
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300680static unsigned int inline norm_vbipack(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300682 return (norm & V4L2_STD_625_50) ? 511 : 400;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683}
684
685int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
686 enum v4l2_field field)
687{
688 unsigned int swidth = norm_swidth(core->tvnorm);
689 unsigned int sheight = norm_maxh(core->tvnorm);
690 u32 value;
691
692 dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
693 V4L2_FIELD_HAS_TOP(field) ? "T" : "",
694 V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300695 v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 if (!V4L2_FIELD_HAS_BOTH(field))
697 height *= 2;
698
699 // recalc H delay and scale registers
700 value = (width * norm_hdelay(core->tvnorm)) / swidth;
701 value &= 0x3fe;
702 cx_write(MO_HDELAY_EVEN, value);
703 cx_write(MO_HDELAY_ODD, value);
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300704 dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705
706 value = (swidth * 4096 / width) - 4096;
707 cx_write(MO_HSCALE_EVEN, value);
708 cx_write(MO_HSCALE_ODD, value);
709 dprintk(1,"set_scale: hscale 0x%04x\n", value);
710
711 cx_write(MO_HACTIVE_EVEN, width);
712 cx_write(MO_HACTIVE_ODD, width);
713 dprintk(1,"set_scale: hactive 0x%04x\n", width);
714
715 // recalc V scale Register (delay is constant)
716 cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
717 cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
718 dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
719
720 value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
721 cx_write(MO_VSCALE_EVEN, value);
722 cx_write(MO_VSCALE_ODD, value);
723 dprintk(1,"set_scale: vscale 0x%04x\n", value);
724
725 cx_write(MO_VACTIVE_EVEN, sheight);
726 cx_write(MO_VACTIVE_ODD, sheight);
727 dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
728
729 // setup filters
730 value = 0;
731 value |= (1 << 19); // CFILT (default)
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300732 if (core->tvnorm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 value |= (1 << 15);
734 value |= (1 << 16);
735 }
Trent Piepho6a59d642007-08-15 14:41:57 -0300736 if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 value |= (1 << 13) | (1 << 5);
738 if (V4L2_FIELD_INTERLACED == field)
739 value |= (1 << 3); // VINT (interlaced vertical scaling)
740 if (width < 385)
741 value |= (1 << 0); // 3-tap interpolation
742 if (width < 193)
743 value |= (1 << 1); // 5-tap interpolation
744 if (nocomb)
745 value |= (3 << 5); // disable comb filter
746
747 cx_write(MO_FILTER_EVEN, value);
748 cx_write(MO_FILTER_ODD, value);
749 dprintk(1,"set_scale: filter 0x%04x\n", value);
750
751 return 0;
752}
753
754static const u32 xtal = 28636363;
755
756static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
757{
758 static u32 pre[] = { 0, 0, 0, 3, 2, 1 };
759 u64 pll;
760 u32 reg;
761 int i;
762
763 if (prescale < 2)
764 prescale = 2;
765 if (prescale > 5)
766 prescale = 5;
767
768 pll = ofreq * 8 * prescale * (u64)(1 << 20);
769 do_div(pll,xtal);
770 reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
771 if (((reg >> 20) & 0x3f) < 14) {
772 printk("%s/0: pll out of range\n",core->name);
773 return -1;
774 }
775
776 dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
777 reg, cx_read(MO_PLL_REG), ofreq);
778 cx_write(MO_PLL_REG, reg);
779 for (i = 0; i < 100; i++) {
780 reg = cx_read(MO_DEVICE_STATUS);
781 if (reg & (1<<2)) {
782 dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
783 prescale,ofreq);
784 return 0;
785 }
786 dprintk(1,"pll not locked yet, waiting ...\n");
787 msleep(10);
788 }
789 dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
790 return -1;
791}
792
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800793int cx88_start_audio_dma(struct cx88_core *core)
794{
Marcin Rudowski17801f52006-02-06 09:15:14 -0200795 /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
796 int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300797
798 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
799 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
800 return 0;
801
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800802 /* setup fifo + format */
Marcin Rudowski17801f52006-02-06 09:15:14 -0200803 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
804 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800805
Marcin Rudowski17801f52006-02-06 09:15:14 -0200806 cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
807 cx_write(MO_AUDR_LNGTH, bpl); /* fifo bpl size */
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800808
809 /* start dma */
810 cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300811
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800812 return 0;
813}
814
815int cx88_stop_audio_dma(struct cx88_core *core)
816{
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300817 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
818 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
819 return 0;
820
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800821 /* stop dma */
822 cx_write(MO_AUD_DMACNTRL, 0x0000);
823
824 return 0;
825}
826
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827static int set_tvaudio(struct cx88_core *core)
828{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300829 v4l2_std_id norm = core->tvnorm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Trent Piepho6a59d642007-08-15 14:41:57 -0300831 if (CX88_VMUX_TELEVISION != INPUT(core->input).type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 return 0;
833
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300834 if (V4L2_STD_PAL_BG & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800835 core->tvaudio = WW_BG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300837 } else if (V4L2_STD_PAL_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800838 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300840 } else if (V4L2_STD_PAL_I & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800841 core->tvaudio = WW_I;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300843 } else if (V4L2_STD_SECAM_L & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800844 core->tvaudio = WW_L;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300846 } else if (V4L2_STD_SECAM_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800847 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300849 } else if ((V4L2_STD_NTSC_M & norm) ||
850 (V4L2_STD_PAL_M & norm)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 core->tvaudio = WW_BTSC;
852
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300853 } else if (V4L2_STD_NTSC_M_JP & norm) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 core->tvaudio = WW_EIAJ;
855
856 } else {
857 printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300858 core->name, v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 core->tvaudio = 0;
860 return 0;
861 }
862
863 cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
864 cx88_set_tvaudio(core);
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -0700865 /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800867/*
868 This should be needed only on cx88-alsa. It seems that some cx88 chips have
869 bugs and does require DMA enabled for it to work.
870 */
871 cx88_start_audio_dma(core);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 return 0;
873}
874
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800875
876
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300877int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878{
879 u32 fsc8;
880 u32 adc_clock;
881 u32 vdec_clock;
882 u32 step_db,step_dr;
883 u64 tmp64;
884 u32 bdelay,agcdelay,htotal;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300885 u32 cxiformat, cxoformat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
887 core->tvnorm = norm;
888 fsc8 = norm_fsc8(norm);
889 adc_clock = xtal;
890 vdec_clock = fsc8;
891 step_db = fsc8;
892 step_dr = fsc8;
893
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300894 if (norm & V4L2_STD_NTSC_M_JP) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300895 cxiformat = VideoFormatNTSCJapan;
896 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300897 } else if (norm & V4L2_STD_NTSC_443) {
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300898 cxiformat = VideoFormatNTSC443;
899 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300900 } else if (norm & V4L2_STD_PAL_M) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300901 cxiformat = VideoFormatPALM;
902 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300903 } else if (norm & V4L2_STD_PAL_N) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300904 cxiformat = VideoFormatPALN;
905 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300906 } else if (norm & V4L2_STD_PAL_Nc) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300907 cxiformat = VideoFormatPALNC;
908 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300909 } else if (norm & V4L2_STD_PAL_60) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300910 cxiformat = VideoFormatPAL60;
911 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300912 } else if (norm & V4L2_STD_NTSC) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300913 cxiformat = VideoFormatNTSC;
914 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300915 } else if (norm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 step_db = 4250000 * 8;
917 step_dr = 4406250 * 8;
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300918
919 cxiformat = VideoFormatSECAM;
920 cxoformat = 0x181f0008;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300921 } else { /* PAL */
922 cxiformat = VideoFormatPAL;
923 cxoformat = 0x181f0008;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 }
925
926 dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300927 v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
928 step_db, step_dr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 set_pll(core,2,vdec_clock);
930
931 dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300932 cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
Frej Drejhammar87a17382008-03-23 22:43:22 -0300933 /* Chroma AGC must be disabled if SECAM is used, we enable it
934 by default on PAL and NTSC */
935 cx_andor(MO_INPUT_FORMAT, 0x40f,
936 norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 // FIXME: as-is from DScaler
939 dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300940 cxoformat, cx_read(MO_OUTPUT_FORMAT));
941 cx_write(MO_OUTPUT_FORMAT, cxoformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942
943 // MO_SCONV_REG = adc clock / video dec clock * 2^17
944 tmp64 = adc_clock * (u64)(1 << 17);
945 do_div(tmp64, vdec_clock);
946 dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
947 (u32)tmp64, cx_read(MO_SCONV_REG));
948 cx_write(MO_SCONV_REG, (u32)tmp64);
949
950 // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
951 tmp64 = step_db * (u64)(1 << 22);
952 do_div(tmp64, vdec_clock);
953 dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
954 (u32)tmp64, cx_read(MO_SUB_STEP));
955 cx_write(MO_SUB_STEP, (u32)tmp64);
956
957 // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
958 tmp64 = step_dr * (u64)(1 << 22);
959 do_div(tmp64, vdec_clock);
960 dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
961 (u32)tmp64, cx_read(MO_SUB_STEP_DR));
962 cx_write(MO_SUB_STEP_DR, (u32)tmp64);
963
964 // bdelay + agcdelay
965 bdelay = vdec_clock * 65 / 20000000 + 21;
966 agcdelay = vdec_clock * 68 / 20000000 + 15;
967 dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
968 (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
969 cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
970
971 // htotal
972 tmp64 = norm_htotal(norm) * (u64)vdec_clock;
973 do_div(tmp64, fsc8);
Mauro Carvalho Chehabccbf64b2006-09-29 12:39:36 -0300974 htotal = (u32)tmp64 | (HLNotchFilter4xFsc << 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
976 htotal, cx_read(MO_HTOTAL), (u32)tmp64);
977 cx_write(MO_HTOTAL, htotal);
978
Trent Piepho3eb73172006-05-23 23:54:44 -0300979 // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
980 // the effective vbi offset ~244 samples, the same as the Bt8x8
981 cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982
983 // this is needed as well to set all tvnorm parameter
984 cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
985
986 // audio
987 set_tvaudio(core);
988
989 // tell i2c chips
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300990 cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991
992 // done
993 return 0;
994}
995
996/* ------------------------------------------------------------------ */
997
Linus Torvalds1da177e2005-04-16 15:20:36 -0700998struct video_device *cx88_vdev_init(struct cx88_core *core,
999 struct pci_dev *pci,
1000 struct video_device *template,
1001 char *type)
1002{
1003 struct video_device *vfd;
1004
1005 vfd = video_device_alloc();
1006 if (NULL == vfd)
1007 return NULL;
1008 *vfd = *template;
1009 vfd->minor = -1;
Hans Verkuil5e85e732008-07-20 06:31:39 -03001010 vfd->parent = &pci->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 vfd->release = video_device_release;
1012 snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
Trent Piepho6a59d642007-08-15 14:41:57 -03001013 core->name, type, core->board.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014 return vfd;
1015}
1016
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017struct cx88_core* cx88_core_get(struct pci_dev *pci)
1018{
1019 struct cx88_core *core;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001021 mutex_lock(&devlist);
Trent Piepho8bb629e22007-10-10 05:37:40 -03001022 list_for_each_entry(core, &cx88_devlist, devlist) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 if (pci->bus->number != core->pci_bus)
1024 continue;
1025 if (PCI_SLOT(pci->devfn) != core->pci_slot)
1026 continue;
1027
Trent Piephobbc83592007-08-15 14:41:58 -03001028 if (0 != cx88_get_resources(core, pci)) {
1029 mutex_unlock(&devlist);
1030 return NULL;
1031 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 atomic_inc(&core->refcount);
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001033 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 return core;
1035 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036
Trent Piephobbc83592007-08-15 14:41:58 -03001037 core = cx88_core_create(pci, cx88_devcount);
1038 if (NULL != core) {
1039 cx88_devcount++;
1040 list_add_tail(&core->devlist, &cx88_devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001043 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 return core;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045}
1046
1047void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
1048{
1049 release_mem_region(pci_resource_start(pci,0),
1050 pci_resource_len(pci,0));
1051
1052 if (!atomic_dec_and_test(&core->refcount))
1053 return;
1054
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001055 mutex_lock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 cx88_ir_fini(core);
1057 if (0 == core->i2c_rc)
Jean Delvare32697112006-12-10 21:21:33 +01001058 i2c_del_adapter(&core->i2c_adap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 list_del(&core->devlist);
1060 iounmap(core->lmmio);
1061 cx88_devcount--;
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001062 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 kfree(core);
1064}
1065
1066/* ------------------------------------------------------------------ */
1067
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068EXPORT_SYMBOL(cx88_print_irqbits);
1069
1070EXPORT_SYMBOL(cx88_core_irq);
1071EXPORT_SYMBOL(cx88_wakeup);
1072EXPORT_SYMBOL(cx88_reset);
1073EXPORT_SYMBOL(cx88_shutdown);
1074
1075EXPORT_SYMBOL(cx88_risc_buffer);
1076EXPORT_SYMBOL(cx88_risc_databuffer);
1077EXPORT_SYMBOL(cx88_risc_stopper);
1078EXPORT_SYMBOL(cx88_free_buffer);
1079
1080EXPORT_SYMBOL(cx88_sram_channels);
1081EXPORT_SYMBOL(cx88_sram_channel_setup);
1082EXPORT_SYMBOL(cx88_sram_channel_dump);
1083
1084EXPORT_SYMBOL(cx88_set_tvnorm);
1085EXPORT_SYMBOL(cx88_set_scale);
1086
1087EXPORT_SYMBOL(cx88_vdev_init);
1088EXPORT_SYMBOL(cx88_core_get);
1089EXPORT_SYMBOL(cx88_core_put);
1090
Mauro Carvalho Chehab13595a52007-10-01 08:51:39 -03001091EXPORT_SYMBOL(cx88_ir_start);
1092EXPORT_SYMBOL(cx88_ir_stop);
1093
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094/*
1095 * Local variables:
1096 * c-basic-offset: 8
1097 * End:
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -07001098 * 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 -07001099 */