blob: b35411160f0456b621649f3873478b6945dbf1f7 [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
Marton Balinte878cf32009-03-31 19:01:51 -0300234 * case most of the time). But that still gives us enough FIFO space
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 * 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
Marton Balinte878cf32009-03-31 19:01:51 -0300244 * channel 27 (audio rds)- 3.0k
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200245 * TOTAL = 29.0k
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 *
247 * Every channel has 160 bytes control data (64 bytes instruction
248 * queue and 6 CDT entries), which is close to 2k total.
249 *
250 * Address layout:
251 * 0x0000 - 0x03ff CMDs / reserved
252 * 0x0400 - 0x0bff instruction queues + CDs
253 * 0x0c00 - FIFOs
254 */
255
256struct sram_channel cx88_sram_channels[] = {
257 [SRAM_CH21] = {
258 .name = "video y / packed",
259 .cmds_start = 0x180040,
260 .ctrl_start = 0x180400,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800261 .cdt = 0x180400 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 .fifo_start = 0x180c00,
263 .fifo_size = 0x002800,
264 .ptr1_reg = MO_DMA21_PTR1,
265 .ptr2_reg = MO_DMA21_PTR2,
266 .cnt1_reg = MO_DMA21_CNT1,
267 .cnt2_reg = MO_DMA21_CNT2,
268 },
269 [SRAM_CH22] = {
270 .name = "video u",
271 .cmds_start = 0x180080,
272 .ctrl_start = 0x1804a0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800273 .cdt = 0x1804a0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 .fifo_start = 0x183400,
275 .fifo_size = 0x000800,
276 .ptr1_reg = MO_DMA22_PTR1,
277 .ptr2_reg = MO_DMA22_PTR2,
278 .cnt1_reg = MO_DMA22_CNT1,
279 .cnt2_reg = MO_DMA22_CNT2,
280 },
281 [SRAM_CH23] = {
282 .name = "video v",
283 .cmds_start = 0x1800c0,
284 .ctrl_start = 0x180540,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800285 .cdt = 0x180540 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 .fifo_start = 0x183c00,
287 .fifo_size = 0x000800,
288 .ptr1_reg = MO_DMA23_PTR1,
289 .ptr2_reg = MO_DMA23_PTR2,
290 .cnt1_reg = MO_DMA23_CNT1,
291 .cnt2_reg = MO_DMA23_CNT2,
292 },
293 [SRAM_CH24] = {
294 .name = "vbi",
295 .cmds_start = 0x180100,
296 .ctrl_start = 0x1805e0,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800297 .cdt = 0x1805e0 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 .fifo_start = 0x184400,
299 .fifo_size = 0x001000,
300 .ptr1_reg = MO_DMA24_PTR1,
301 .ptr2_reg = MO_DMA24_PTR2,
302 .cnt1_reg = MO_DMA24_CNT1,
303 .cnt2_reg = MO_DMA24_CNT2,
304 },
305 [SRAM_CH25] = {
306 .name = "audio from",
307 .cmds_start = 0x180140,
308 .ctrl_start = 0x180680,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800309 .cdt = 0x180680 + 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 .fifo_start = 0x185400,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200311 .fifo_size = 0x001000,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 .ptr1_reg = MO_DMA25_PTR1,
313 .ptr2_reg = MO_DMA25_PTR2,
314 .cnt1_reg = MO_DMA25_CNT1,
315 .cnt2_reg = MO_DMA25_CNT2,
316 },
317 [SRAM_CH26] = {
318 .name = "audio to",
319 .cmds_start = 0x180180,
320 .ctrl_start = 0x180720,
Mauro Carvalho Chehab4ac97912005-11-08 21:37:43 -0800321 .cdt = 0x180680 + 64, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 .fifo_start = 0x185400, /* same as audio IN */
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200323 .fifo_size = 0x001000, /* same as audio IN */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 .ptr1_reg = MO_DMA26_PTR1,
325 .ptr2_reg = MO_DMA26_PTR2,
326 .cnt1_reg = MO_DMA26_CNT1,
327 .cnt2_reg = MO_DMA26_CNT2,
328 },
329 [SRAM_CH28] = {
330 .name = "mpeg",
331 .cmds_start = 0x180200,
332 .ctrl_start = 0x1807C0,
333 .cdt = 0x1807C0 + 64,
Mauro Carvalho Chehabb7f355d2006-01-09 15:32:44 -0200334 .fifo_start = 0x186400,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 .fifo_size = 0x001000,
336 .ptr1_reg = MO_DMA28_PTR1,
337 .ptr2_reg = MO_DMA28_PTR2,
338 .cnt1_reg = MO_DMA28_CNT1,
339 .cnt2_reg = MO_DMA28_CNT2,
340 },
Marton Balinte878cf32009-03-31 19:01:51 -0300341 [SRAM_CH27] = {
342 .name = "audio rds",
343 .cmds_start = 0x1801C0,
344 .ctrl_start = 0x180860,
345 .cdt = 0x180860 + 64,
346 .fifo_start = 0x187400,
347 .fifo_size = 0x000C00,
348 .ptr1_reg = MO_DMA27_PTR1,
349 .ptr2_reg = MO_DMA27_PTR2,
350 .cnt1_reg = MO_DMA27_CNT1,
351 .cnt2_reg = MO_DMA27_CNT2,
352 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353};
354
355int cx88_sram_channel_setup(struct cx88_core *core,
356 struct sram_channel *ch,
357 unsigned int bpl, u32 risc)
358{
359 unsigned int i,lines;
360 u32 cdt;
361
362 bpl = (bpl + 7) & ~7; /* alignment */
363 cdt = ch->cdt;
364 lines = ch->fifo_size / bpl;
365 if (lines > 6)
366 lines = 6;
367 BUG_ON(lines < 2);
368
369 /* write CDT */
370 for (i = 0; i < lines; i++)
371 cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
372
373 /* write CMDS */
374 cx_write(ch->cmds_start + 0, risc);
375 cx_write(ch->cmds_start + 4, cdt);
376 cx_write(ch->cmds_start + 8, (lines*16) >> 3);
377 cx_write(ch->cmds_start + 12, ch->ctrl_start);
378 cx_write(ch->cmds_start + 16, 64 >> 2);
379 for (i = 20; i < 64; i += 4)
380 cx_write(ch->cmds_start + i, 0);
381
382 /* fill registers */
383 cx_write(ch->ptr1_reg, ch->fifo_start);
384 cx_write(ch->ptr2_reg, cdt);
385 cx_write(ch->cnt1_reg, (bpl >> 3) -1);
386 cx_write(ch->cnt2_reg, (lines*16) >> 3);
387
388 dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
389 return 0;
390}
391
392/* ------------------------------------------------------------------ */
393/* debug helper code */
394
Peter Hagervallf9e7a022005-11-08 21:36:29 -0800395static int cx88_risc_decode(u32 risc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396{
397 static char *instr[16] = {
398 [ RISC_SYNC >> 28 ] = "sync",
399 [ RISC_WRITE >> 28 ] = "write",
400 [ RISC_WRITEC >> 28 ] = "writec",
401 [ RISC_READ >> 28 ] = "read",
402 [ RISC_READC >> 28 ] = "readc",
403 [ RISC_JUMP >> 28 ] = "jump",
404 [ RISC_SKIP >> 28 ] = "skip",
405 [ RISC_WRITERM >> 28 ] = "writerm",
406 [ RISC_WRITECM >> 28 ] = "writecm",
407 [ RISC_WRITECR >> 28 ] = "writecr",
408 };
409 static int incr[16] = {
410 [ RISC_WRITE >> 28 ] = 2,
411 [ RISC_JUMP >> 28 ] = 2,
412 [ RISC_WRITERM >> 28 ] = 3,
413 [ RISC_WRITECM >> 28 ] = 3,
414 [ RISC_WRITECR >> 28 ] = 4,
415 };
416 static char *bits[] = {
417 "12", "13", "14", "resync",
418 "cnt0", "cnt1", "18", "19",
419 "20", "21", "22", "23",
420 "irq1", "irq2", "eol", "sol",
421 };
422 int i;
423
424 printk("0x%08x [ %s", risc,
425 instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
426 for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
427 if (risc & (1 << (i + 12)))
428 printk(" %s",bits[i]);
429 printk(" count=%d ]\n", risc & 0xfff);
430 return incr[risc >> 28] ? incr[risc >> 28] : 1;
431}
432
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433
434void cx88_sram_channel_dump(struct cx88_core *core,
435 struct sram_channel *ch)
436{
437 static char *name[] = {
438 "initial risc",
439 "cdt base",
440 "cdt size",
441 "iq base",
442 "iq size",
443 "risc pc",
444 "iq wr ptr",
445 "iq rd ptr",
446 "cdt current",
447 "pci target",
448 "line / byte",
449 };
450 u32 risc;
451 unsigned int i,j,n;
452
453 printk("%s: %s - dma channel status dump\n",
454 core->name,ch->name);
455 for (i = 0; i < ARRAY_SIZE(name); i++)
456 printk("%s: cmds: %-12s: 0x%08x\n",
457 core->name,name[i],
458 cx_read(ch->cmds_start + 4*i));
Trent Piepho16cf1d02007-08-21 08:19:16 -0300459 for (n = 1, i = 0; i < 4; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 risc = cx_read(ch->cmds_start + 4 * (i+11));
461 printk("%s: risc%d: ", core->name, i);
Trent Piepho16cf1d02007-08-21 08:19:16 -0300462 if (--n)
463 printk("0x%08x [ arg #%d ]\n", risc, n);
464 else
465 n = cx88_risc_decode(risc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 }
467 for (i = 0; i < 16; i += n) {
468 risc = cx_read(ch->ctrl_start + 4 * i);
469 printk("%s: iq %x: ", core->name, i);
470 n = cx88_risc_decode(risc);
471 for (j = 1; j < n; j++) {
472 risc = cx_read(ch->ctrl_start + 4 * (i+j));
473 printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
474 core->name, i+j, risc, j);
475 }
476 }
477
478 printk("%s: fifo: 0x%08x -> 0x%x\n",
479 core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
480 printk("%s: ctrl: 0x%08x -> 0x%x\n",
481 core->name, ch->ctrl_start, ch->ctrl_start+6*16);
482 printk("%s: ptr1_reg: 0x%08x\n",
483 core->name,cx_read(ch->ptr1_reg));
484 printk("%s: ptr2_reg: 0x%08x\n",
485 core->name,cx_read(ch->ptr2_reg));
486 printk("%s: cnt1_reg: 0x%08x\n",
487 core->name,cx_read(ch->cnt1_reg));
488 printk("%s: cnt2_reg: 0x%08x\n",
489 core->name,cx_read(ch->cnt2_reg));
490}
491
Adrian Bunk408b6642005-05-01 08:59:29 -0700492static char *cx88_pci_irqs[32] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 "vid", "aud", "ts", "vip", "hst", "5", "6", "tm1",
494 "src_dma", "dst_dma", "risc_rd_err", "risc_wr_err",
495 "brdg_err", "src_dma_err", "dst_dma_err", "ipb_dma_err",
496 "i2c", "i2c_rack", "ir_smp", "gpio0", "gpio1"
497};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
499void cx88_print_irqbits(char *name, char *tag, char **strings,
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300500 int len, u32 bits, u32 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501{
502 unsigned int i;
503
504 printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits);
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300505 for (i = 0; i < len; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 if (!(bits & (1 << i)))
507 continue;
508 if (strings[i])
509 printk(" %s", strings[i]);
510 else
511 printk(" %d", i);
512 if (!(mask & (1 << i)))
513 continue;
514 printk("*");
515 }
516 printk("\n");
517}
518
519/* ------------------------------------------------------------------ */
520
521int cx88_core_irq(struct cx88_core *core, u32 status)
522{
523 int handled = 0;
524
Trent Piepho8ddac9e2007-08-18 06:57:55 -0300525 if (status & PCI_INT_IR_SMPINT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 cx88_ir_irq(core);
527 handled++;
528 }
529 if (!handled)
530 cx88_print_irqbits(core->name, "irq pci",
Mauro Carvalho Chehab66623a02007-03-29 08:47:04 -0300531 cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs),
532 status, core->pci_irqmask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 return handled;
534}
535
536void cx88_wakeup(struct cx88_core *core,
537 struct cx88_dmaqueue *q, u32 count)
538{
539 struct cx88_buffer *buf;
540 int bc;
541
542 for (bc = 0;; bc++) {
543 if (list_empty(&q->active))
544 break;
545 buf = list_entry(q->active.next,
546 struct cx88_buffer, vb.queue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 /* count comes from the hw and is is 16bit wide --
548 * this trick handles wrap-arounds correctly for
549 * up to 32767 buffers in flight... */
550 if ((s16) (count - buf->count) < 0)
551 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 do_gettimeofday(&buf->vb.ts);
553 dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
554 count, buf->count);
Brandon Philips0fc06862007-11-06 20:02:36 -0300555 buf->vb.state = VIDEOBUF_DONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 list_del(&buf->vb.queue);
557 wake_up(&buf->vb.done);
558 }
559 if (list_empty(&q->active)) {
560 del_timer(&q->timeout);
561 } else {
562 mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
563 }
564 if (bc != 1)
Steven Toth376a8412008-10-16 20:30:45 -0300565 dprintk(2, "%s: %d buffers handled (should be 1)\n",
566 __func__, bc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567}
568
569void cx88_shutdown(struct cx88_core *core)
570{
571 /* disable RISC controller + IRQs */
572 cx_write(MO_DEV_CNTRL2, 0);
573
574 /* stop dma transfers */
575 cx_write(MO_VID_DMACNTRL, 0x0);
576 cx_write(MO_AUD_DMACNTRL, 0x0);
577 cx_write(MO_TS_DMACNTRL, 0x0);
578 cx_write(MO_VIP_DMACNTRL, 0x0);
579 cx_write(MO_GPHST_DMACNTRL, 0x0);
580
581 /* stop interrupts */
582 cx_write(MO_PCI_INTMSK, 0x0);
583 cx_write(MO_VID_INTMSK, 0x0);
584 cx_write(MO_AUD_INTMSK, 0x0);
585 cx_write(MO_TS_INTMSK, 0x0);
586 cx_write(MO_VIP_INTMSK, 0x0);
587 cx_write(MO_GPHST_INTMSK, 0x0);
588
589 /* stop capturing */
590 cx_write(VID_CAPTURE_CONTROL, 0);
591}
592
593int cx88_reset(struct cx88_core *core)
594{
Harvey Harrison32d83ef2008-04-08 23:20:00 -0300595 dprintk(1,"%s\n",__func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 cx88_shutdown(core);
597
598 /* clear irq status */
599 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
600 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
601 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
602
603 /* wait a bit */
604 msleep(100);
605
606 /* init sram */
607 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
608 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
609 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
610 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
611 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
612 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
613 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
Marton Balinte878cf32009-03-31 19:01:51 -0300614 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], 128, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
616 /* misc init ... */
617 cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
618 (1 << 12) | // agc gain
619 (1 << 11) | // adaptibe agc
620 (0 << 10) | // chroma agc
621 (0 << 9) | // ckillen
622 (7)));
623
624 /* setup image format */
625 cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
626
André Goddard Rosaaf901ca2009-11-14 13:09:05 -0200627 /* setup FIFO Thresholds */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 cx_write(MO_PDMA_STHRSH, 0x0807);
629 cx_write(MO_PDMA_DTHRSH, 0x0807);
630
631 /* fixes flashing of image */
632 cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
633 cx_write(MO_AGC_BACK_VBI, 0x00E00555);
634
635 cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
636 cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
637 cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
638
639 /* Reset on-board parts */
640 cx_write(MO_SRST_IO, 0);
641 msleep(10);
642 cx_write(MO_SRST_IO, 1);
643
644 return 0;
645}
646
647/* ------------------------------------------------------------------ */
648
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300649static unsigned int inline norm_swidth(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300651 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652}
653
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300654static unsigned int inline norm_hdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300656 return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657}
658
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300659static unsigned int inline norm_vdelay(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300661 return (norm & V4L2_STD_625_50) ? 0x24 : 0x18;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662}
663
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300664static unsigned int inline norm_fsc8(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300666 if (norm & V4L2_STD_PAL_M)
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300667 return 28604892; // 3.575611 MHz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300669 if (norm & (V4L2_STD_PAL_Nc))
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300670 return 28656448; // 3.582056 MHz
671
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300672 if (norm & V4L2_STD_NTSC) // All NTSC/M and variants
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300673 return 28636360; // 3.57954545 MHz +/- 10 Hz
674
675 /* SECAM have also different sub carrier for chroma,
676 but step_db and step_dr, at cx88_set_tvnorm already handles that.
677
678 The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N
679 */
680
681 return 35468950; // 4.43361875 MHz +/- 5 Hz
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682}
683
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300684static unsigned int inline norm_htotal(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700685{
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700686
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300687 unsigned int fsc4=norm_fsc8(norm)/2;
Mauro Carvalho Chehab59dcd942005-06-23 22:04:50 -0700688
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300689 /* returns 4*FSC / vtotal / frames per seconds */
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300690 return (norm & V4L2_STD_625_50) ?
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300691 ((fsc4+312)/625+12)/25 :
692 ((fsc4+262)/525*1001+15000)/30000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693}
694
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300695static unsigned int inline norm_vbipack(v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300697 return (norm & V4L2_STD_625_50) ? 511 : 400;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698}
699
700int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
701 enum v4l2_field field)
702{
703 unsigned int swidth = norm_swidth(core->tvnorm);
704 unsigned int sheight = norm_maxh(core->tvnorm);
705 u32 value;
706
707 dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
708 V4L2_FIELD_HAS_TOP(field) ? "T" : "",
709 V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300710 v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 if (!V4L2_FIELD_HAS_BOTH(field))
712 height *= 2;
713
714 // recalc H delay and scale registers
715 value = (width * norm_hdelay(core->tvnorm)) / swidth;
716 value &= 0x3fe;
717 cx_write(MO_HDELAY_EVEN, value);
718 cx_write(MO_HDELAY_ODD, value);
Mauro Carvalho Chehab315eb9622006-12-17 23:30:47 -0300719 dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720
721 value = (swidth * 4096 / width) - 4096;
722 cx_write(MO_HSCALE_EVEN, value);
723 cx_write(MO_HSCALE_ODD, value);
724 dprintk(1,"set_scale: hscale 0x%04x\n", value);
725
726 cx_write(MO_HACTIVE_EVEN, width);
727 cx_write(MO_HACTIVE_ODD, width);
728 dprintk(1,"set_scale: hactive 0x%04x\n", width);
729
730 // recalc V scale Register (delay is constant)
731 cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
732 cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
733 dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
734
735 value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
736 cx_write(MO_VSCALE_EVEN, value);
737 cx_write(MO_VSCALE_ODD, value);
738 dprintk(1,"set_scale: vscale 0x%04x\n", value);
739
740 cx_write(MO_VACTIVE_EVEN, sheight);
741 cx_write(MO_VACTIVE_ODD, sheight);
742 dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
743
744 // setup filters
745 value = 0;
746 value |= (1 << 19); // CFILT (default)
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300747 if (core->tvnorm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 value |= (1 << 15);
749 value |= (1 << 16);
750 }
Trent Piepho6a59d642007-08-15 14:41:57 -0300751 if (INPUT(core->input).type == CX88_VMUX_SVIDEO)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752 value |= (1 << 13) | (1 << 5);
753 if (V4L2_FIELD_INTERLACED == field)
754 value |= (1 << 3); // VINT (interlaced vertical scaling)
755 if (width < 385)
756 value |= (1 << 0); // 3-tap interpolation
757 if (width < 193)
758 value |= (1 << 1); // 5-tap interpolation
759 if (nocomb)
760 value |= (3 << 5); // disable comb filter
761
762 cx_write(MO_FILTER_EVEN, value);
763 cx_write(MO_FILTER_ODD, value);
764 dprintk(1,"set_scale: filter 0x%04x\n", value);
765
766 return 0;
767}
768
769static const u32 xtal = 28636363;
770
771static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
772{
773 static u32 pre[] = { 0, 0, 0, 3, 2, 1 };
774 u64 pll;
775 u32 reg;
776 int i;
777
778 if (prescale < 2)
779 prescale = 2;
780 if (prescale > 5)
781 prescale = 5;
782
783 pll = ofreq * 8 * prescale * (u64)(1 << 20);
784 do_div(pll,xtal);
785 reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
786 if (((reg >> 20) & 0x3f) < 14) {
787 printk("%s/0: pll out of range\n",core->name);
788 return -1;
789 }
790
791 dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
792 reg, cx_read(MO_PLL_REG), ofreq);
793 cx_write(MO_PLL_REG, reg);
794 for (i = 0; i < 100; i++) {
795 reg = cx_read(MO_DEVICE_STATUS);
796 if (reg & (1<<2)) {
797 dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
798 prescale,ofreq);
799 return 0;
800 }
801 dprintk(1,"pll not locked yet, waiting ...\n");
802 msleep(10);
803 }
804 dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
805 return -1;
806}
807
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800808int cx88_start_audio_dma(struct cx88_core *core)
809{
Marcin Rudowski17801f52006-02-06 09:15:14 -0200810 /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
811 int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300812
Marton Balinte878cf32009-03-31 19:01:51 -0300813 int rds_bpl = cx88_sram_channels[SRAM_CH27].fifo_size/AUD_RDS_LINES;
814
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300815 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
816 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
817 return 0;
818
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800819 /* setup fifo + format */
Marcin Rudowski17801f52006-02-06 09:15:14 -0200820 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
821 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
Marton Balinte878cf32009-03-31 19:01:51 -0300822 cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27],
823 rds_bpl, 0);
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800824
Marcin Rudowski17801f52006-02-06 09:15:14 -0200825 cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */
Marton Balinte878cf32009-03-31 19:01:51 -0300826 cx_write(MO_AUDR_LNGTH, rds_bpl); /* fifo bpl size */
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800827
Marton Balinte878cf32009-03-31 19:01:51 -0300828 /* enable Up, Down and Audio RDS fifo */
829 cx_write(MO_AUD_DMACNTRL, 0x0007);
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300830
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800831 return 0;
832}
833
834int cx88_stop_audio_dma(struct cx88_core *core)
835{
Ricardo Cerqueirae738e352006-08-17 18:40:28 -0300836 /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
837 if (cx_read(MO_AUD_DMACNTRL) & 0x10)
838 return 0;
839
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800840 /* stop dma */
841 cx_write(MO_AUD_DMACNTRL, 0x0000);
842
843 return 0;
844}
845
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846static int set_tvaudio(struct cx88_core *core)
847{
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300848 v4l2_std_id norm = core->tvnorm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849
Trent Piepho6a59d642007-08-15 14:41:57 -0300850 if (CX88_VMUX_TELEVISION != INPUT(core->input).type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 return 0;
852
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300853 if (V4L2_STD_PAL_BG & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800854 core->tvaudio = WW_BG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300856 } else if (V4L2_STD_PAL_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800857 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300859 } else if (V4L2_STD_PAL_I & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800860 core->tvaudio = WW_I;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300862 } else if (V4L2_STD_SECAM_L & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800863 core->tvaudio = WW_L;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
Frederic CANDb84ca9f2008-10-30 04:53:07 -0300865 } else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) {
866 core->tvaudio = WW_BG;
867
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300868 } else if (V4L2_STD_SECAM_DK & norm) {
Torsten Seebothb1706b92005-11-08 21:36:27 -0800869 core->tvaudio = WW_DK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300871 } else if ((V4L2_STD_NTSC_M & norm) ||
872 (V4L2_STD_PAL_M & norm)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 core->tvaudio = WW_BTSC;
874
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300875 } else if (V4L2_STD_NTSC_M_JP & norm) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 core->tvaudio = WW_EIAJ;
877
878 } else {
879 printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300880 core->name, v4l2_norm_to_name(core->tvnorm));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 core->tvaudio = 0;
882 return 0;
883 }
884
885 cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
886 cx88_set_tvaudio(core);
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -0700887 /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800889/*
890 This should be needed only on cx88-alsa. It seems that some cx88 chips have
891 bugs and does require DMA enabled for it to work.
892 */
893 cx88_start_audio_dma(core);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 return 0;
895}
896
Mauro Carvalho Chehab6f502b82005-12-01 00:51:34 -0800897
898
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300899int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900{
901 u32 fsc8;
902 u32 adc_clock;
903 u32 vdec_clock;
904 u32 step_db,step_dr;
905 u64 tmp64;
906 u32 bdelay,agcdelay,htotal;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300907 u32 cxiformat, cxoformat;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908
909 core->tvnorm = norm;
910 fsc8 = norm_fsc8(norm);
911 adc_clock = xtal;
912 vdec_clock = fsc8;
913 step_db = fsc8;
914 step_dr = fsc8;
915
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300916 if (norm & V4L2_STD_NTSC_M_JP) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300917 cxiformat = VideoFormatNTSCJapan;
918 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300919 } else if (norm & V4L2_STD_NTSC_443) {
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300920 cxiformat = VideoFormatNTSC443;
921 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300922 } else if (norm & V4L2_STD_PAL_M) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300923 cxiformat = VideoFormatPALM;
924 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300925 } else if (norm & V4L2_STD_PAL_N) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300926 cxiformat = VideoFormatPALN;
927 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300928 } else if (norm & V4L2_STD_PAL_Nc) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300929 cxiformat = VideoFormatPALNC;
930 cxoformat = 0x1c1f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300931 } else if (norm & V4L2_STD_PAL_60) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300932 cxiformat = VideoFormatPAL60;
933 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300934 } else if (norm & V4L2_STD_NTSC) {
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300935 cxiformat = VideoFormatNTSC;
936 cxoformat = 0x181f0008;
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300937 } else if (norm & V4L2_STD_SECAM) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 step_db = 4250000 * 8;
939 step_dr = 4406250 * 8;
Mauro Carvalho Chehab1427f6b2007-01-20 13:58:20 -0300940
941 cxiformat = VideoFormatSECAM;
942 cxoformat = 0x181f0008;
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300943 } else { /* PAL */
944 cxiformat = VideoFormatPAL;
945 cxoformat = 0x181f0008;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 }
947
948 dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
Mauro Carvalho Chehab63ab1bd2007-01-20 13:58:33 -0300949 v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock,
950 step_db, step_dr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951 set_pll(core,2,vdec_clock);
952
953 dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300954 cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
Frej Drejhammar87a17382008-03-23 22:43:22 -0300955 /* Chroma AGC must be disabled if SECAM is used, we enable it
956 by default on PAL and NTSC */
957 cx_andor(MO_INPUT_FORMAT, 0x40f,
958 norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700959
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 // FIXME: as-is from DScaler
961 dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
Mauro Carvalho Chehab8d87cb92007-01-20 13:58:17 -0300962 cxoformat, cx_read(MO_OUTPUT_FORMAT));
963 cx_write(MO_OUTPUT_FORMAT, cxoformat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
965 // MO_SCONV_REG = adc clock / video dec clock * 2^17
966 tmp64 = adc_clock * (u64)(1 << 17);
967 do_div(tmp64, vdec_clock);
968 dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
969 (u32)tmp64, cx_read(MO_SCONV_REG));
970 cx_write(MO_SCONV_REG, (u32)tmp64);
971
972 // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
973 tmp64 = step_db * (u64)(1 << 22);
974 do_div(tmp64, vdec_clock);
975 dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
976 (u32)tmp64, cx_read(MO_SUB_STEP));
977 cx_write(MO_SUB_STEP, (u32)tmp64);
978
979 // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
980 tmp64 = step_dr * (u64)(1 << 22);
981 do_div(tmp64, vdec_clock);
982 dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
983 (u32)tmp64, cx_read(MO_SUB_STEP_DR));
984 cx_write(MO_SUB_STEP_DR, (u32)tmp64);
985
986 // bdelay + agcdelay
987 bdelay = vdec_clock * 65 / 20000000 + 21;
988 agcdelay = vdec_clock * 68 / 20000000 + 15;
989 dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
990 (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
991 cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
992
993 // htotal
994 tmp64 = norm_htotal(norm) * (u64)vdec_clock;
995 do_div(tmp64, fsc8);
Mauro Carvalho Chehabccbf64b2006-09-29 12:39:36 -0300996 htotal = (u32)tmp64 | (HLNotchFilter4xFsc << 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
998 htotal, cx_read(MO_HTOTAL), (u32)tmp64);
999 cx_write(MO_HTOTAL, htotal);
1000
Trent Piepho3eb73172006-05-23 23:54:44 -03001001 // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes
1002 // the effective vbi offset ~244 samples, the same as the Bt8x8
1003 cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004
1005 // this is needed as well to set all tvnorm parameter
1006 cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED);
1007
1008 // audio
1009 set_tvaudio(core);
1010
1011 // tell i2c chips
Hans Verkuilf41737e2009-04-01 03:52:39 -03001012 call_all(core, core, s_std, norm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013
1014 // done
1015 return 0;
1016}
1017
1018/* ------------------------------------------------------------------ */
1019
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020struct video_device *cx88_vdev_init(struct cx88_core *core,
1021 struct pci_dev *pci,
1022 struct video_device *template,
1023 char *type)
1024{
1025 struct video_device *vfd;
1026
1027 vfd = video_device_alloc();
1028 if (NULL == vfd)
1029 return NULL;
1030 *vfd = *template;
Hans Verkuil9467fe12009-03-14 12:40:51 -03001031 vfd->v4l2_dev = &core->v4l2_dev;
1032 vfd->parent = &pci->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 vfd->release = video_device_release;
1034 snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
Trent Piepho6a59d642007-08-15 14:41:57 -03001035 core->name, type, core->board.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 return vfd;
1037}
1038
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039struct cx88_core* cx88_core_get(struct pci_dev *pci)
1040{
1041 struct cx88_core *core;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001043 mutex_lock(&devlist);
Trent Piepho8bb629e22007-10-10 05:37:40 -03001044 list_for_each_entry(core, &cx88_devlist, devlist) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 if (pci->bus->number != core->pci_bus)
1046 continue;
1047 if (PCI_SLOT(pci->devfn) != core->pci_slot)
1048 continue;
1049
Trent Piephobbc83592007-08-15 14:41:58 -03001050 if (0 != cx88_get_resources(core, pci)) {
1051 mutex_unlock(&devlist);
1052 return NULL;
1053 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 atomic_inc(&core->refcount);
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001055 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 return core;
1057 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001058
Trent Piephobbc83592007-08-15 14:41:58 -03001059 core = cx88_core_create(pci, cx88_devcount);
1060 if (NULL != core) {
1061 cx88_devcount++;
1062 list_add_tail(&core->devlist, &cx88_devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001065 mutex_unlock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 return core;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067}
1068
1069void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
1070{
1071 release_mem_region(pci_resource_start(pci,0),
1072 pci_resource_len(pci,0));
1073
1074 if (!atomic_dec_and_test(&core->refcount))
1075 return;
1076
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001077 mutex_lock(&devlist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 cx88_ir_fini(core);
Hans Verkuilb8341e12009-03-29 08:26:01 -03001079 if (0 == core->i2c_rc) {
1080 if (core->i2c_rtc)
1081 i2c_unregister_device(core->i2c_rtc);
Jean Delvare32697112006-12-10 21:21:33 +01001082 i2c_del_adapter(&core->i2c_adap);
Hans Verkuilb8341e12009-03-29 08:26:01 -03001083 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 list_del(&core->devlist);
1085 iounmap(core->lmmio);
1086 cx88_devcount--;
Ingo Molnar1e4baed2006-01-15 07:52:23 -02001087 mutex_unlock(&devlist);
Hans Verkuil9467fe12009-03-14 12:40:51 -03001088 v4l2_device_unregister(&core->v4l2_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 kfree(core);
1090}
1091
1092/* ------------------------------------------------------------------ */
1093
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094EXPORT_SYMBOL(cx88_print_irqbits);
1095
1096EXPORT_SYMBOL(cx88_core_irq);
1097EXPORT_SYMBOL(cx88_wakeup);
1098EXPORT_SYMBOL(cx88_reset);
1099EXPORT_SYMBOL(cx88_shutdown);
1100
1101EXPORT_SYMBOL(cx88_risc_buffer);
1102EXPORT_SYMBOL(cx88_risc_databuffer);
1103EXPORT_SYMBOL(cx88_risc_stopper);
1104EXPORT_SYMBOL(cx88_free_buffer);
1105
1106EXPORT_SYMBOL(cx88_sram_channels);
1107EXPORT_SYMBOL(cx88_sram_channel_setup);
1108EXPORT_SYMBOL(cx88_sram_channel_dump);
1109
1110EXPORT_SYMBOL(cx88_set_tvnorm);
1111EXPORT_SYMBOL(cx88_set_scale);
1112
1113EXPORT_SYMBOL(cx88_vdev_init);
1114EXPORT_SYMBOL(cx88_core_get);
1115EXPORT_SYMBOL(cx88_core_put);
1116
Mauro Carvalho Chehab13595a52007-10-01 08:51:39 -03001117EXPORT_SYMBOL(cx88_ir_start);
1118EXPORT_SYMBOL(cx88_ir_stop);
1119
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120/*
1121 * Local variables:
1122 * c-basic-offset: 8
1123 * End:
Mauro Carvalho Chehabe52e98a2005-09-09 13:03:41 -07001124 * 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 -07001125 */