diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/media/video/cx88 | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/cx88')
-rw-r--r-- | drivers/media/video/cx88/Kconfig | 86 | ||||
-rw-r--r-- | drivers/media/video/cx88/Makefile | 16 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-alsa.c | 992 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-blackbird.c | 1385 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-cards.c | 3699 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-core.c | 1129 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-dsp.c | 322 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-dvb.c | 1764 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-i2c.c | 184 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-input.c | 631 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-mpeg.c | 929 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-reg.h | 836 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-tvaudio.c | 1059 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-vbi.c | 245 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-video.c | 2194 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-vp3054-i2c.c | 159 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88-vp3054-i2c.h | 41 | ||||
-rw-r--r-- | drivers/media/video/cx88/cx88.h | 730 |
18 files changed, 16401 insertions, 0 deletions
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig new file mode 100644 index 00000000000..5c42abdf422 --- /dev/null +++ b/drivers/media/video/cx88/Kconfig | |||
@@ -0,0 +1,86 @@ | |||
1 | config VIDEO_CX88 | ||
2 | tristate "Conexant 2388x (bt878 successor) support" | ||
3 | depends on VIDEO_DEV && PCI && I2C && RC_CORE | ||
4 | select I2C_ALGOBIT | ||
5 | select VIDEO_BTCX | ||
6 | select VIDEOBUF_DMA_SG | ||
7 | select VIDEO_TUNER | ||
8 | select VIDEO_TVEEPROM | ||
9 | select VIDEO_WM8775 if VIDEO_HELPER_CHIPS_AUTO | ||
10 | ---help--- | ||
11 | This is a video4linux driver for Conexant 2388x based | ||
12 | TV cards. | ||
13 | |||
14 | To compile this driver as a module, choose M here: the | ||
15 | module will be called cx8800 | ||
16 | |||
17 | config VIDEO_CX88_ALSA | ||
18 | tristate "Conexant 2388x DMA audio support" | ||
19 | depends on VIDEO_CX88 && SND | ||
20 | select SND_PCM | ||
21 | ---help--- | ||
22 | This is a video4linux driver for direct (DMA) audio on | ||
23 | Conexant 2388x based TV cards using ALSA. | ||
24 | |||
25 | It only works with boards with function 01 enabled. | ||
26 | To check if your board supports, use lspci -n. | ||
27 | If supported, you should see 14f1:8801 or 14f1:8811 | ||
28 | PCI device. | ||
29 | |||
30 | To compile this driver as a module, choose M here: the | ||
31 | module will be called cx88-alsa. | ||
32 | |||
33 | config VIDEO_CX88_BLACKBIRD | ||
34 | tristate "Blackbird MPEG encoder support (cx2388x + cx23416)" | ||
35 | depends on VIDEO_CX88 | ||
36 | select VIDEO_CX2341X | ||
37 | ---help--- | ||
38 | This adds support for MPEG encoder cards based on the | ||
39 | Blackbird reference design, using the Conexant 2388x | ||
40 | and 23416 chips. | ||
41 | |||
42 | To compile this driver as a module, choose M here: the | ||
43 | module will be called cx88-blackbird. | ||
44 | |||
45 | config VIDEO_CX88_DVB | ||
46 | tristate "DVB/ATSC Support for cx2388x based TV cards" | ||
47 | depends on VIDEO_CX88 && DVB_CORE | ||
48 | select VIDEOBUF_DVB | ||
49 | select DVB_PLL if !DVB_FE_CUSTOMISE | ||
50 | select DVB_MT352 if !DVB_FE_CUSTOMISE | ||
51 | select DVB_ZL10353 if !DVB_FE_CUSTOMISE | ||
52 | select DVB_OR51132 if !DVB_FE_CUSTOMISE | ||
53 | select DVB_CX22702 if !DVB_FE_CUSTOMISE | ||
54 | select DVB_LGDT330X if !DVB_FE_CUSTOMISE | ||
55 | select DVB_NXT200X if !DVB_FE_CUSTOMISE | ||
56 | select DVB_CX24123 if !DVB_FE_CUSTOMISE | ||
57 | select DVB_ISL6421 if !DVB_FE_CUSTOMISE | ||
58 | select DVB_S5H1411 if !DVB_FE_CUSTOMISE | ||
59 | select DVB_CX24116 if !DVB_FE_CUSTOMISE | ||
60 | select DVB_STV0299 if !DVB_FE_CUSTOMISE | ||
61 | select DVB_STV0288 if !DVB_FE_CUSTOMISE | ||
62 | select DVB_STB6000 if !DVB_FE_CUSTOMISE | ||
63 | select DVB_STV0900 if !DVB_FE_CUSTOMISE | ||
64 | select DVB_STB6100 if !DVB_FE_CUSTOMISE | ||
65 | select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE | ||
66 | ---help--- | ||
67 | This adds support for DVB/ATSC cards based on the | ||
68 | Conexant 2388x chip. | ||
69 | |||
70 | To compile this driver as a module, choose M here: the | ||
71 | module will be called cx88-dvb. | ||
72 | |||
73 | config VIDEO_CX88_MPEG | ||
74 | tristate | ||
75 | depends on VIDEO_CX88_DVB || VIDEO_CX88_BLACKBIRD | ||
76 | default y | ||
77 | |||
78 | config VIDEO_CX88_VP3054 | ||
79 | tristate "VP-3054 Secondary I2C Bus Support" | ||
80 | default m | ||
81 | depends on VIDEO_CX88_DVB && DVB_MT352 | ||
82 | ---help--- | ||
83 | This adds DVB-T support for cards based on the | ||
84 | Conexant 2388x chip and the MT352 demodulator, | ||
85 | which also require support for the VP-3054 | ||
86 | Secondary I2C bus, such at DNTV Live! DVB-T Pro. | ||
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile new file mode 100644 index 00000000000..5b7e26761f0 --- /dev/null +++ b/drivers/media/video/cx88/Makefile | |||
@@ -0,0 +1,16 @@ | |||
1 | cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \ | ||
2 | cx88-dsp.o cx88-input.o | ||
3 | cx8800-objs := cx88-video.o cx88-vbi.o | ||
4 | cx8802-objs := cx88-mpeg.o | ||
5 | |||
6 | obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o | ||
7 | obj-$(CONFIG_VIDEO_CX88_MPEG) += cx8802.o | ||
8 | obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o | ||
9 | obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o | ||
10 | obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o | ||
11 | obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o | ||
12 | |||
13 | EXTRA_CFLAGS += -Idrivers/media/video | ||
14 | EXTRA_CFLAGS += -Idrivers/media/common/tuners | ||
15 | EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core | ||
16 | EXTRA_CFLAGS += -Idrivers/media/dvb/frontends | ||
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c new file mode 100644 index 00000000000..68d1240f493 --- /dev/null +++ b/drivers/media/video/cx88/cx88-alsa.c | |||
@@ -0,0 +1,992 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Support for audio capture | ||
4 | * PCI function #1 of the cx2388x. | ||
5 | * | ||
6 | * (c) 2007 Trent Piepho <xyzzy@speakeasy.org> | ||
7 | * (c) 2005,2006 Ricardo Cerqueira <v4l@cerqueira.org> | ||
8 | * (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org> | ||
9 | * Based on a dummy cx88 module by Gerd Knorr <kraxel@bytesex.org> | ||
10 | * Based on dummy.c by Jaroslav Kysela <perex@perex.cz> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
20 | * GNU General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/device.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/vmalloc.h> | ||
32 | #include <linux/dma-mapping.h> | ||
33 | #include <linux/pci.h> | ||
34 | #include <linux/slab.h> | ||
35 | |||
36 | #include <asm/delay.h> | ||
37 | #include <sound/core.h> | ||
38 | #include <sound/pcm.h> | ||
39 | #include <sound/pcm_params.h> | ||
40 | #include <sound/control.h> | ||
41 | #include <sound/initval.h> | ||
42 | #include <sound/tlv.h> | ||
43 | #include <media/wm8775.h> | ||
44 | |||
45 | #include "cx88.h" | ||
46 | #include "cx88-reg.h" | ||
47 | |||
48 | #define dprintk(level,fmt, arg...) if (debug >= level) \ | ||
49 | printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg) | ||
50 | |||
51 | #define dprintk_core(level,fmt, arg...) if (debug >= level) \ | ||
52 | printk(KERN_DEBUG "%s/1: " fmt, chip->core->name , ## arg) | ||
53 | |||
54 | /**************************************************************************** | ||
55 | Data type declarations - Can be moded to a header file later | ||
56 | ****************************************************************************/ | ||
57 | |||
58 | struct cx88_audio_buffer { | ||
59 | unsigned int bpl; | ||
60 | struct btcx_riscmem risc; | ||
61 | struct videobuf_dmabuf dma; | ||
62 | }; | ||
63 | |||
64 | struct cx88_audio_dev { | ||
65 | struct cx88_core *core; | ||
66 | struct cx88_dmaqueue q; | ||
67 | |||
68 | /* pci i/o */ | ||
69 | struct pci_dev *pci; | ||
70 | |||
71 | /* audio controls */ | ||
72 | int irq; | ||
73 | |||
74 | struct snd_card *card; | ||
75 | |||
76 | spinlock_t reg_lock; | ||
77 | atomic_t count; | ||
78 | |||
79 | unsigned int dma_size; | ||
80 | unsigned int period_size; | ||
81 | unsigned int num_periods; | ||
82 | |||
83 | struct videobuf_dmabuf *dma_risc; | ||
84 | |||
85 | struct cx88_audio_buffer *buf; | ||
86 | |||
87 | struct snd_pcm_substream *substream; | ||
88 | }; | ||
89 | typedef struct cx88_audio_dev snd_cx88_card_t; | ||
90 | |||
91 | |||
92 | |||
93 | /**************************************************************************** | ||
94 | Module global static vars | ||
95 | ****************************************************************************/ | ||
96 | |||
97 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ | ||
98 | static const char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ | ||
99 | static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; | ||
100 | |||
101 | module_param_array(enable, bool, NULL, 0444); | ||
102 | MODULE_PARM_DESC(enable, "Enable cx88x soundcard. default enabled."); | ||
103 | |||
104 | module_param_array(index, int, NULL, 0444); | ||
105 | MODULE_PARM_DESC(index, "Index value for cx88x capture interface(s)."); | ||
106 | |||
107 | |||
108 | /**************************************************************************** | ||
109 | Module macros | ||
110 | ****************************************************************************/ | ||
111 | |||
112 | MODULE_DESCRIPTION("ALSA driver module for cx2388x based TV cards"); | ||
113 | MODULE_AUTHOR("Ricardo Cerqueira"); | ||
114 | MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); | ||
115 | MODULE_LICENSE("GPL"); | ||
116 | MODULE_VERSION(CX88_VERSION); | ||
117 | |||
118 | MODULE_SUPPORTED_DEVICE("{{Conexant,23881}," | ||
119 | "{{Conexant,23882}," | ||
120 | "{{Conexant,23883}"); | ||
121 | static unsigned int debug; | ||
122 | module_param(debug,int,0644); | ||
123 | MODULE_PARM_DESC(debug,"enable debug messages"); | ||
124 | |||
125 | /**************************************************************************** | ||
126 | Module specific funtions | ||
127 | ****************************************************************************/ | ||
128 | |||
129 | /* | ||
130 | * BOARD Specific: Sets audio DMA | ||
131 | */ | ||
132 | |||
133 | static int _cx88_start_audio_dma(snd_cx88_card_t *chip) | ||
134 | { | ||
135 | struct cx88_audio_buffer *buf = chip->buf; | ||
136 | struct cx88_core *core=chip->core; | ||
137 | const struct sram_channel *audio_ch = &cx88_sram_channels[SRAM_CH25]; | ||
138 | |||
139 | /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ | ||
140 | cx_clear(MO_AUD_DMACNTRL, 0x11); | ||
141 | |||
142 | /* setup fifo + format - out channel */ | ||
143 | cx88_sram_channel_setup(chip->core, audio_ch, buf->bpl, buf->risc.dma); | ||
144 | |||
145 | /* sets bpl size */ | ||
146 | cx_write(MO_AUDD_LNGTH, buf->bpl); | ||
147 | |||
148 | /* reset counter */ | ||
149 | cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET); | ||
150 | atomic_set(&chip->count, 0); | ||
151 | |||
152 | dprintk(1, "Start audio DMA, %d B/line, %d lines/FIFO, %d periods, %d " | ||
153 | "byte buffer\n", buf->bpl, cx_read(audio_ch->cmds_start + 8)>>1, | ||
154 | chip->num_periods, buf->bpl * chip->num_periods); | ||
155 | |||
156 | /* Enables corresponding bits at AUD_INT_STAT */ | ||
157 | cx_write(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | | ||
158 | AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1); | ||
159 | |||
160 | /* Clean any pending interrupt bits already set */ | ||
161 | cx_write(MO_AUD_INTSTAT, ~0); | ||
162 | |||
163 | /* enable audio irqs */ | ||
164 | cx_set(MO_PCI_INTMSK, chip->core->pci_irqmask | PCI_INT_AUDINT); | ||
165 | |||
166 | /* start dma */ | ||
167 | cx_set(MO_DEV_CNTRL2, (1<<5)); /* Enables Risc Processor */ | ||
168 | cx_set(MO_AUD_DMACNTRL, 0x11); /* audio downstream FIFO and RISC enable */ | ||
169 | |||
170 | if (debug) | ||
171 | cx88_sram_channel_dump(chip->core, audio_ch); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * BOARD Specific: Resets audio DMA | ||
178 | */ | ||
179 | static int _cx88_stop_audio_dma(snd_cx88_card_t *chip) | ||
180 | { | ||
181 | struct cx88_core *core=chip->core; | ||
182 | dprintk(1, "Stopping audio DMA\n"); | ||
183 | |||
184 | /* stop dma */ | ||
185 | cx_clear(MO_AUD_DMACNTRL, 0x11); | ||
186 | |||
187 | /* disable irqs */ | ||
188 | cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT); | ||
189 | cx_clear(MO_AUD_INTMSK, AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | | ||
190 | AUD_INT_DN_RISCI2 | AUD_INT_DN_RISCI1); | ||
191 | |||
192 | if (debug) | ||
193 | cx88_sram_channel_dump(chip->core, &cx88_sram_channels[SRAM_CH25]); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | #define MAX_IRQ_LOOP 50 | ||
199 | |||
200 | /* | ||
201 | * BOARD Specific: IRQ dma bits | ||
202 | */ | ||
203 | static const char *cx88_aud_irqs[32] = { | ||
204 | "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */ | ||
205 | NULL, /* reserved */ | ||
206 | "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */ | ||
207 | NULL, /* reserved */ | ||
208 | "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */ | ||
209 | NULL, /* reserved */ | ||
210 | "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */ | ||
211 | NULL, /* reserved */ | ||
212 | "opc_err", "par_err", "rip_err", /* 16-18 */ | ||
213 | "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */ | ||
214 | }; | ||
215 | |||
216 | /* | ||
217 | * BOARD Specific: Threats IRQ audio specific calls | ||
218 | */ | ||
219 | static void cx8801_aud_irq(snd_cx88_card_t *chip) | ||
220 | { | ||
221 | struct cx88_core *core = chip->core; | ||
222 | u32 status, mask; | ||
223 | |||
224 | status = cx_read(MO_AUD_INTSTAT); | ||
225 | mask = cx_read(MO_AUD_INTMSK); | ||
226 | if (0 == (status & mask)) | ||
227 | return; | ||
228 | cx_write(MO_AUD_INTSTAT, status); | ||
229 | if (debug > 1 || (status & mask & ~0xff)) | ||
230 | cx88_print_irqbits(core->name, "irq aud", | ||
231 | cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs), | ||
232 | status, mask); | ||
233 | /* risc op code error */ | ||
234 | if (status & AUD_INT_OPC_ERR) { | ||
235 | printk(KERN_WARNING "%s/1: Audio risc op code error\n",core->name); | ||
236 | cx_clear(MO_AUD_DMACNTRL, 0x11); | ||
237 | cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH25]); | ||
238 | } | ||
239 | if (status & AUD_INT_DN_SYNC) { | ||
240 | dprintk(1, "Downstream sync error\n"); | ||
241 | cx_write(MO_AUDD_GPCNTRL, GP_COUNT_CONTROL_RESET); | ||
242 | return; | ||
243 | } | ||
244 | /* risc1 downstream */ | ||
245 | if (status & AUD_INT_DN_RISCI1) { | ||
246 | atomic_set(&chip->count, cx_read(MO_AUDD_GPCNT)); | ||
247 | snd_pcm_period_elapsed(chip->substream); | ||
248 | } | ||
249 | /* FIXME: Any other status should deserve a special handling? */ | ||
250 | } | ||
251 | |||
252 | /* | ||
253 | * BOARD Specific: Handles IRQ calls | ||
254 | */ | ||
255 | static irqreturn_t cx8801_irq(int irq, void *dev_id) | ||
256 | { | ||
257 | snd_cx88_card_t *chip = dev_id; | ||
258 | struct cx88_core *core = chip->core; | ||
259 | u32 status; | ||
260 | int loop, handled = 0; | ||
261 | |||
262 | for (loop = 0; loop < MAX_IRQ_LOOP; loop++) { | ||
263 | status = cx_read(MO_PCI_INTSTAT) & | ||
264 | (core->pci_irqmask | PCI_INT_AUDINT); | ||
265 | if (0 == status) | ||
266 | goto out; | ||
267 | dprintk(3, "cx8801_irq loop %d/%d, status %x\n", | ||
268 | loop, MAX_IRQ_LOOP, status); | ||
269 | handled = 1; | ||
270 | cx_write(MO_PCI_INTSTAT, status); | ||
271 | |||
272 | if (status & core->pci_irqmask) | ||
273 | cx88_core_irq(core, status); | ||
274 | if (status & PCI_INT_AUDINT) | ||
275 | cx8801_aud_irq(chip); | ||
276 | } | ||
277 | |||
278 | if (MAX_IRQ_LOOP == loop) { | ||
279 | printk(KERN_ERR | ||
280 | "%s/1: IRQ loop detected, disabling interrupts\n", | ||
281 | core->name); | ||
282 | cx_clear(MO_PCI_INTMSK, PCI_INT_AUDINT); | ||
283 | } | ||
284 | |||
285 | out: | ||
286 | return IRQ_RETVAL(handled); | ||
287 | } | ||
288 | |||
289 | |||
290 | static int dsp_buffer_free(snd_cx88_card_t *chip) | ||
291 | { | ||
292 | BUG_ON(!chip->dma_size); | ||
293 | |||
294 | dprintk(2,"Freeing buffer\n"); | ||
295 | videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); | ||
296 | videobuf_dma_free(chip->dma_risc); | ||
297 | btcx_riscmem_free(chip->pci,&chip->buf->risc); | ||
298 | kfree(chip->buf); | ||
299 | |||
300 | chip->dma_risc = NULL; | ||
301 | chip->dma_size = 0; | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | /**************************************************************************** | ||
307 | ALSA PCM Interface | ||
308 | ****************************************************************************/ | ||
309 | |||
310 | /* | ||
311 | * Digital hardware definition | ||
312 | */ | ||
313 | #define DEFAULT_FIFO_SIZE 4096 | ||
314 | static const struct snd_pcm_hardware snd_cx88_digital_hw = { | ||
315 | .info = SNDRV_PCM_INFO_MMAP | | ||
316 | SNDRV_PCM_INFO_INTERLEAVED | | ||
317 | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
318 | SNDRV_PCM_INFO_MMAP_VALID, | ||
319 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
320 | |||
321 | .rates = SNDRV_PCM_RATE_48000, | ||
322 | .rate_min = 48000, | ||
323 | .rate_max = 48000, | ||
324 | .channels_min = 2, | ||
325 | .channels_max = 2, | ||
326 | /* Analog audio output will be full of clicks and pops if there | ||
327 | are not exactly four lines in the SRAM FIFO buffer. */ | ||
328 | .period_bytes_min = DEFAULT_FIFO_SIZE/4, | ||
329 | .period_bytes_max = DEFAULT_FIFO_SIZE/4, | ||
330 | .periods_min = 1, | ||
331 | .periods_max = 1024, | ||
332 | .buffer_bytes_max = (1024*1024), | ||
333 | }; | ||
334 | |||
335 | /* | ||
336 | * audio pcm capture open callback | ||
337 | */ | ||
338 | static int snd_cx88_pcm_open(struct snd_pcm_substream *substream) | ||
339 | { | ||
340 | snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); | ||
341 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
342 | int err; | ||
343 | |||
344 | if (!chip) { | ||
345 | printk(KERN_ERR "BUG: cx88 can't find device struct." | ||
346 | " Can't proceed with open\n"); | ||
347 | return -ENODEV; | ||
348 | } | ||
349 | |||
350 | err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS); | ||
351 | if (err < 0) | ||
352 | goto _error; | ||
353 | |||
354 | chip->substream = substream; | ||
355 | |||
356 | runtime->hw = snd_cx88_digital_hw; | ||
357 | |||
358 | if (cx88_sram_channels[SRAM_CH25].fifo_size != DEFAULT_FIFO_SIZE) { | ||
359 | unsigned int bpl = cx88_sram_channels[SRAM_CH25].fifo_size / 4; | ||
360 | bpl &= ~7; /* must be multiple of 8 */ | ||
361 | runtime->hw.period_bytes_min = bpl; | ||
362 | runtime->hw.period_bytes_max = bpl; | ||
363 | } | ||
364 | |||
365 | return 0; | ||
366 | _error: | ||
367 | dprintk(1,"Error opening PCM!\n"); | ||
368 | return err; | ||
369 | } | ||
370 | |||
371 | /* | ||
372 | * audio close callback | ||
373 | */ | ||
374 | static int snd_cx88_close(struct snd_pcm_substream *substream) | ||
375 | { | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | /* | ||
380 | * hw_params callback | ||
381 | */ | ||
382 | static int snd_cx88_hw_params(struct snd_pcm_substream * substream, | ||
383 | struct snd_pcm_hw_params * hw_params) | ||
384 | { | ||
385 | snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); | ||
386 | struct videobuf_dmabuf *dma; | ||
387 | |||
388 | struct cx88_audio_buffer *buf; | ||
389 | int ret; | ||
390 | |||
391 | if (substream->runtime->dma_area) { | ||
392 | dsp_buffer_free(chip); | ||
393 | substream->runtime->dma_area = NULL; | ||
394 | } | ||
395 | |||
396 | chip->period_size = params_period_bytes(hw_params); | ||
397 | chip->num_periods = params_periods(hw_params); | ||
398 | chip->dma_size = chip->period_size * params_periods(hw_params); | ||
399 | |||
400 | BUG_ON(!chip->dma_size); | ||
401 | BUG_ON(chip->num_periods & (chip->num_periods-1)); | ||
402 | |||
403 | buf = kzalloc(sizeof(*buf), GFP_KERNEL); | ||
404 | if (NULL == buf) | ||
405 | return -ENOMEM; | ||
406 | |||
407 | buf->bpl = chip->period_size; | ||
408 | |||
409 | dma = &buf->dma; | ||
410 | videobuf_dma_init(dma); | ||
411 | ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, | ||
412 | (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); | ||
413 | if (ret < 0) | ||
414 | goto error; | ||
415 | |||
416 | ret = videobuf_dma_map(&chip->pci->dev, dma); | ||
417 | if (ret < 0) | ||
418 | goto error; | ||
419 | |||
420 | ret = cx88_risc_databuffer(chip->pci, &buf->risc, dma->sglist, | ||
421 | chip->period_size, chip->num_periods, 1); | ||
422 | if (ret < 0) | ||
423 | goto error; | ||
424 | |||
425 | /* Loop back to start of program */ | ||
426 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP|RISC_IRQ1|RISC_CNT_INC); | ||
427 | buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
428 | |||
429 | chip->buf = buf; | ||
430 | chip->dma_risc = dma; | ||
431 | |||
432 | substream->runtime->dma_area = chip->dma_risc->vaddr; | ||
433 | substream->runtime->dma_bytes = chip->dma_size; | ||
434 | substream->runtime->dma_addr = 0; | ||
435 | return 0; | ||
436 | |||
437 | error: | ||
438 | kfree(buf); | ||
439 | return ret; | ||
440 | } | ||
441 | |||
442 | /* | ||
443 | * hw free callback | ||
444 | */ | ||
445 | static int snd_cx88_hw_free(struct snd_pcm_substream * substream) | ||
446 | { | ||
447 | |||
448 | snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); | ||
449 | |||
450 | if (substream->runtime->dma_area) { | ||
451 | dsp_buffer_free(chip); | ||
452 | substream->runtime->dma_area = NULL; | ||
453 | } | ||
454 | |||
455 | return 0; | ||
456 | } | ||
457 | |||
458 | /* | ||
459 | * prepare callback | ||
460 | */ | ||
461 | static int snd_cx88_prepare(struct snd_pcm_substream *substream) | ||
462 | { | ||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | /* | ||
467 | * trigger callback | ||
468 | */ | ||
469 | static int snd_cx88_card_trigger(struct snd_pcm_substream *substream, int cmd) | ||
470 | { | ||
471 | snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); | ||
472 | int err; | ||
473 | |||
474 | /* Local interrupts are already disabled by ALSA */ | ||
475 | spin_lock(&chip->reg_lock); | ||
476 | |||
477 | switch (cmd) { | ||
478 | case SNDRV_PCM_TRIGGER_START: | ||
479 | err=_cx88_start_audio_dma(chip); | ||
480 | break; | ||
481 | case SNDRV_PCM_TRIGGER_STOP: | ||
482 | err=_cx88_stop_audio_dma(chip); | ||
483 | break; | ||
484 | default: | ||
485 | err=-EINVAL; | ||
486 | break; | ||
487 | } | ||
488 | |||
489 | spin_unlock(&chip->reg_lock); | ||
490 | |||
491 | return err; | ||
492 | } | ||
493 | |||
494 | /* | ||
495 | * pointer callback | ||
496 | */ | ||
497 | static snd_pcm_uframes_t snd_cx88_pointer(struct snd_pcm_substream *substream) | ||
498 | { | ||
499 | snd_cx88_card_t *chip = snd_pcm_substream_chip(substream); | ||
500 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
501 | u16 count; | ||
502 | |||
503 | count = atomic_read(&chip->count); | ||
504 | |||
505 | // dprintk(2, "%s - count %d (+%u), period %d, frame %lu\n", __func__, | ||
506 | // count, new, count & (runtime->periods-1), | ||
507 | // runtime->period_size * (count & (runtime->periods-1))); | ||
508 | return runtime->period_size * (count & (runtime->periods-1)); | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * page callback (needed for mmap) | ||
513 | */ | ||
514 | static struct page *snd_cx88_page(struct snd_pcm_substream *substream, | ||
515 | unsigned long offset) | ||
516 | { | ||
517 | void *pageptr = substream->runtime->dma_area + offset; | ||
518 | return vmalloc_to_page(pageptr); | ||
519 | } | ||
520 | |||
521 | /* | ||
522 | * operators | ||
523 | */ | ||
524 | static struct snd_pcm_ops snd_cx88_pcm_ops = { | ||
525 | .open = snd_cx88_pcm_open, | ||
526 | .close = snd_cx88_close, | ||
527 | .ioctl = snd_pcm_lib_ioctl, | ||
528 | .hw_params = snd_cx88_hw_params, | ||
529 | .hw_free = snd_cx88_hw_free, | ||
530 | .prepare = snd_cx88_prepare, | ||
531 | .trigger = snd_cx88_card_trigger, | ||
532 | .pointer = snd_cx88_pointer, | ||
533 | .page = snd_cx88_page, | ||
534 | }; | ||
535 | |||
536 | /* | ||
537 | * create a PCM device | ||
538 | */ | ||
539 | static int __devinit snd_cx88_pcm(snd_cx88_card_t *chip, int device, const char *name) | ||
540 | { | ||
541 | int err; | ||
542 | struct snd_pcm *pcm; | ||
543 | |||
544 | err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); | ||
545 | if (err < 0) | ||
546 | return err; | ||
547 | pcm->private_data = chip; | ||
548 | strcpy(pcm->name, name); | ||
549 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx88_pcm_ops); | ||
550 | |||
551 | return 0; | ||
552 | } | ||
553 | |||
554 | /**************************************************************************** | ||
555 | CONTROL INTERFACE | ||
556 | ****************************************************************************/ | ||
557 | static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol, | ||
558 | struct snd_ctl_elem_info *info) | ||
559 | { | ||
560 | info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
561 | info->count = 2; | ||
562 | info->value.integer.min = 0; | ||
563 | info->value.integer.max = 0x3f; | ||
564 | |||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol, | ||
569 | struct snd_ctl_elem_value *value) | ||
570 | { | ||
571 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
572 | struct cx88_core *core=chip->core; | ||
573 | int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f), | ||
574 | bal = cx_read(AUD_BAL_CTL); | ||
575 | |||
576 | value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol; | ||
577 | vol -= (bal & 0x3f); | ||
578 | value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol; | ||
579 | |||
580 | return 0; | ||
581 | } | ||
582 | |||
583 | static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol, | ||
584 | struct snd_ctl_elem_value *value) | ||
585 | { | ||
586 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
587 | struct cx88_core *core = chip->core; | ||
588 | struct v4l2_control client_ctl; | ||
589 | int left = value->value.integer.value[0]; | ||
590 | int right = value->value.integer.value[1]; | ||
591 | int v, b; | ||
592 | |||
593 | memset(&client_ctl, 0, sizeof(client_ctl)); | ||
594 | |||
595 | /* Pass volume & balance onto any WM8775 */ | ||
596 | if (left >= right) { | ||
597 | v = left << 10; | ||
598 | b = left ? (0x8000 * right) / left : 0x8000; | ||
599 | } else { | ||
600 | v = right << 10; | ||
601 | b = right ? 0xffff - (0x8000 * left) / right : 0x8000; | ||
602 | } | ||
603 | client_ctl.value = v; | ||
604 | client_ctl.id = V4L2_CID_AUDIO_VOLUME; | ||
605 | call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | ||
606 | |||
607 | client_ctl.value = b; | ||
608 | client_ctl.id = V4L2_CID_AUDIO_BALANCE; | ||
609 | call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | ||
610 | } | ||
611 | |||
612 | /* OK - TODO: test it */ | ||
613 | static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, | ||
614 | struct snd_ctl_elem_value *value) | ||
615 | { | ||
616 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
617 | struct cx88_core *core=chip->core; | ||
618 | int left, right, v, b; | ||
619 | int changed = 0; | ||
620 | u32 old; | ||
621 | |||
622 | if (core->board.audio_chip == V4L2_IDENT_WM8775) | ||
623 | snd_cx88_wm8775_volume_put(kcontrol, value); | ||
624 | |||
625 | left = value->value.integer.value[0] & 0x3f; | ||
626 | right = value->value.integer.value[1] & 0x3f; | ||
627 | b = right - left; | ||
628 | if (b < 0) { | ||
629 | v = 0x3f - left; | ||
630 | b = (-b) | 0x40; | ||
631 | } else { | ||
632 | v = 0x3f - right; | ||
633 | } | ||
634 | /* Do we really know this will always be called with IRQs on? */ | ||
635 | spin_lock_irq(&chip->reg_lock); | ||
636 | old = cx_read(AUD_VOL_CTL); | ||
637 | if (v != (old & 0x3f)) { | ||
638 | cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v); | ||
639 | changed = 1; | ||
640 | } | ||
641 | if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) { | ||
642 | cx_write(AUD_BAL_CTL, b); | ||
643 | changed = 1; | ||
644 | } | ||
645 | spin_unlock_irq(&chip->reg_lock); | ||
646 | |||
647 | return changed; | ||
648 | } | ||
649 | |||
650 | static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0); | ||
651 | |||
652 | static const struct snd_kcontrol_new snd_cx88_volume = { | ||
653 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
654 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
655 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | ||
656 | .name = "Analog-TV Volume", | ||
657 | .info = snd_cx88_volume_info, | ||
658 | .get = snd_cx88_volume_get, | ||
659 | .put = snd_cx88_volume_put, | ||
660 | .tlv.p = snd_cx88_db_scale, | ||
661 | }; | ||
662 | |||
663 | static int snd_cx88_switch_get(struct snd_kcontrol *kcontrol, | ||
664 | struct snd_ctl_elem_value *value) | ||
665 | { | ||
666 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
667 | struct cx88_core *core = chip->core; | ||
668 | u32 bit = kcontrol->private_value; | ||
669 | |||
670 | value->value.integer.value[0] = !(cx_read(AUD_VOL_CTL) & bit); | ||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | static int snd_cx88_switch_put(struct snd_kcontrol *kcontrol, | ||
675 | struct snd_ctl_elem_value *value) | ||
676 | { | ||
677 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
678 | struct cx88_core *core = chip->core; | ||
679 | u32 bit = kcontrol->private_value; | ||
680 | int ret = 0; | ||
681 | u32 vol; | ||
682 | |||
683 | spin_lock_irq(&chip->reg_lock); | ||
684 | vol = cx_read(AUD_VOL_CTL); | ||
685 | if (value->value.integer.value[0] != !(vol & bit)) { | ||
686 | vol ^= bit; | ||
687 | cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, vol); | ||
688 | /* Pass mute onto any WM8775 */ | ||
689 | if ((core->board.audio_chip == V4L2_IDENT_WM8775) && | ||
690 | ((1<<6) == bit)) { | ||
691 | struct v4l2_control client_ctl; | ||
692 | |||
693 | memset(&client_ctl, 0, sizeof(client_ctl)); | ||
694 | client_ctl.value = 0 != (vol & bit); | ||
695 | client_ctl.id = V4L2_CID_AUDIO_MUTE; | ||
696 | call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | ||
697 | } | ||
698 | ret = 1; | ||
699 | } | ||
700 | spin_unlock_irq(&chip->reg_lock); | ||
701 | return ret; | ||
702 | } | ||
703 | |||
704 | static const struct snd_kcontrol_new snd_cx88_dac_switch = { | ||
705 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
706 | .name = "Audio-Out Switch", | ||
707 | .info = snd_ctl_boolean_mono_info, | ||
708 | .get = snd_cx88_switch_get, | ||
709 | .put = snd_cx88_switch_put, | ||
710 | .private_value = (1<<8), | ||
711 | }; | ||
712 | |||
713 | static const struct snd_kcontrol_new snd_cx88_source_switch = { | ||
714 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
715 | .name = "Analog-TV Switch", | ||
716 | .info = snd_ctl_boolean_mono_info, | ||
717 | .get = snd_cx88_switch_get, | ||
718 | .put = snd_cx88_switch_put, | ||
719 | .private_value = (1<<6), | ||
720 | }; | ||
721 | |||
722 | static int snd_cx88_alc_get(struct snd_kcontrol *kcontrol, | ||
723 | struct snd_ctl_elem_value *value) | ||
724 | { | ||
725 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
726 | struct cx88_core *core = chip->core; | ||
727 | struct v4l2_control client_ctl; | ||
728 | |||
729 | memset(&client_ctl, 0, sizeof(client_ctl)); | ||
730 | client_ctl.id = V4L2_CID_AUDIO_LOUDNESS; | ||
731 | call_hw(core, WM8775_GID, core, g_ctrl, &client_ctl); | ||
732 | value->value.integer.value[0] = client_ctl.value ? 1 : 0; | ||
733 | |||
734 | return 0; | ||
735 | } | ||
736 | |||
737 | static int snd_cx88_alc_put(struct snd_kcontrol *kcontrol, | ||
738 | struct snd_ctl_elem_value *value) | ||
739 | { | ||
740 | snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); | ||
741 | struct cx88_core *core = chip->core; | ||
742 | struct v4l2_control client_ctl; | ||
743 | |||
744 | memset(&client_ctl, 0, sizeof(client_ctl)); | ||
745 | client_ctl.value = 0 != value->value.integer.value[0]; | ||
746 | client_ctl.id = V4L2_CID_AUDIO_LOUDNESS; | ||
747 | call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | ||
748 | |||
749 | return 0; | ||
750 | } | ||
751 | |||
752 | static struct snd_kcontrol_new snd_cx88_alc_switch = { | ||
753 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
754 | .name = "Line-In ALC Switch", | ||
755 | .info = snd_ctl_boolean_mono_info, | ||
756 | .get = snd_cx88_alc_get, | ||
757 | .put = snd_cx88_alc_put, | ||
758 | }; | ||
759 | |||
760 | /**************************************************************************** | ||
761 | Basic Flow for Sound Devices | ||
762 | ****************************************************************************/ | ||
763 | |||
764 | /* | ||
765 | * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio | ||
766 | * Only boards with eeprom and byte 1 at eeprom=1 have it | ||
767 | */ | ||
768 | |||
769 | static const struct pci_device_id const cx88_audio_pci_tbl[] __devinitdata = { | ||
770 | {0x14f1,0x8801,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, | ||
771 | {0x14f1,0x8811,PCI_ANY_ID,PCI_ANY_ID,0,0,0}, | ||
772 | {0, } | ||
773 | }; | ||
774 | MODULE_DEVICE_TABLE(pci, cx88_audio_pci_tbl); | ||
775 | |||
776 | /* | ||
777 | * Chip-specific destructor | ||
778 | */ | ||
779 | |||
780 | static int snd_cx88_free(snd_cx88_card_t *chip) | ||
781 | { | ||
782 | |||
783 | if (chip->irq >= 0) | ||
784 | free_irq(chip->irq, chip); | ||
785 | |||
786 | cx88_core_put(chip->core,chip->pci); | ||
787 | |||
788 | pci_disable_device(chip->pci); | ||
789 | return 0; | ||
790 | } | ||
791 | |||
792 | /* | ||
793 | * Component Destructor | ||
794 | */ | ||
795 | static void snd_cx88_dev_free(struct snd_card * card) | ||
796 | { | ||
797 | snd_cx88_card_t *chip = card->private_data; | ||
798 | |||
799 | snd_cx88_free(chip); | ||
800 | } | ||
801 | |||
802 | |||
803 | /* | ||
804 | * Alsa Constructor - Component probe | ||
805 | */ | ||
806 | |||
807 | static int devno; | ||
808 | static int __devinit snd_cx88_create(struct snd_card *card, | ||
809 | struct pci_dev *pci, | ||
810 | snd_cx88_card_t **rchip, | ||
811 | struct cx88_core **core_ptr) | ||
812 | { | ||
813 | snd_cx88_card_t *chip; | ||
814 | struct cx88_core *core; | ||
815 | int err; | ||
816 | unsigned char pci_lat; | ||
817 | |||
818 | *rchip = NULL; | ||
819 | |||
820 | err = pci_enable_device(pci); | ||
821 | if (err < 0) | ||
822 | return err; | ||
823 | |||
824 | pci_set_master(pci); | ||
825 | |||
826 | chip = card->private_data; | ||
827 | |||
828 | core = cx88_core_get(pci); | ||
829 | if (NULL == core) { | ||
830 | err = -EINVAL; | ||
831 | return err; | ||
832 | } | ||
833 | |||
834 | if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) { | ||
835 | dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); | ||
836 | err = -EIO; | ||
837 | cx88_core_put(core, pci); | ||
838 | return err; | ||
839 | } | ||
840 | |||
841 | |||
842 | /* pci init */ | ||
843 | chip->card = card; | ||
844 | chip->pci = pci; | ||
845 | chip->irq = -1; | ||
846 | spin_lock_init(&chip->reg_lock); | ||
847 | |||
848 | chip->core = core; | ||
849 | |||
850 | /* get irq */ | ||
851 | err = request_irq(chip->pci->irq, cx8801_irq, | ||
852 | IRQF_SHARED | IRQF_DISABLED, chip->core->name, chip); | ||
853 | if (err < 0) { | ||
854 | dprintk(0, "%s: can't get IRQ %d\n", | ||
855 | chip->core->name, chip->pci->irq); | ||
856 | return err; | ||
857 | } | ||
858 | |||
859 | /* print pci info */ | ||
860 | pci_read_config_byte(pci, PCI_LATENCY_TIMER, &pci_lat); | ||
861 | |||
862 | dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, " | ||
863 | "latency: %d, mmio: 0x%llx\n", core->name, devno, | ||
864 | pci_name(pci), pci->revision, pci->irq, | ||
865 | pci_lat, (unsigned long long)pci_resource_start(pci,0)); | ||
866 | |||
867 | chip->irq = pci->irq; | ||
868 | synchronize_irq(chip->irq); | ||
869 | |||
870 | snd_card_set_dev(card, &pci->dev); | ||
871 | |||
872 | *rchip = chip; | ||
873 | *core_ptr = core; | ||
874 | |||
875 | return 0; | ||
876 | } | ||
877 | |||
878 | static int __devinit cx88_audio_initdev(struct pci_dev *pci, | ||
879 | const struct pci_device_id *pci_id) | ||
880 | { | ||
881 | struct snd_card *card; | ||
882 | snd_cx88_card_t *chip; | ||
883 | struct cx88_core *core = NULL; | ||
884 | int err; | ||
885 | |||
886 | if (devno >= SNDRV_CARDS) | ||
887 | return (-ENODEV); | ||
888 | |||
889 | if (!enable[devno]) { | ||
890 | ++devno; | ||
891 | return (-ENOENT); | ||
892 | } | ||
893 | |||
894 | err = snd_card_create(index[devno], id[devno], THIS_MODULE, | ||
895 | sizeof(snd_cx88_card_t), &card); | ||
896 | if (err < 0) | ||
897 | return err; | ||
898 | |||
899 | card->private_free = snd_cx88_dev_free; | ||
900 | |||
901 | err = snd_cx88_create(card, pci, &chip, &core); | ||
902 | if (err < 0) | ||
903 | goto error; | ||
904 | |||
905 | err = snd_cx88_pcm(chip, 0, "CX88 Digital"); | ||
906 | if (err < 0) | ||
907 | goto error; | ||
908 | |||
909 | err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_volume, chip)); | ||
910 | if (err < 0) | ||
911 | goto error; | ||
912 | err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_dac_switch, chip)); | ||
913 | if (err < 0) | ||
914 | goto error; | ||
915 | err = snd_ctl_add(card, snd_ctl_new1(&snd_cx88_source_switch, chip)); | ||
916 | if (err < 0) | ||
917 | goto error; | ||
918 | |||
919 | /* If there's a wm8775 then add a Line-In ALC switch */ | ||
920 | if (core->board.audio_chip == V4L2_IDENT_WM8775) | ||
921 | snd_ctl_add(card, snd_ctl_new1(&snd_cx88_alc_switch, chip)); | ||
922 | |||
923 | strcpy (card->driver, "CX88x"); | ||
924 | sprintf(card->shortname, "Conexant CX%x", pci->device); | ||
925 | sprintf(card->longname, "%s at %#llx", | ||
926 | card->shortname,(unsigned long long)pci_resource_start(pci, 0)); | ||
927 | strcpy (card->mixername, "CX88"); | ||
928 | |||
929 | dprintk (0, "%s/%i: ALSA support for cx2388x boards\n", | ||
930 | card->driver,devno); | ||
931 | |||
932 | err = snd_card_register(card); | ||
933 | if (err < 0) | ||
934 | goto error; | ||
935 | pci_set_drvdata(pci,card); | ||
936 | |||
937 | devno++; | ||
938 | return 0; | ||
939 | |||
940 | error: | ||
941 | snd_card_free(card); | ||
942 | return err; | ||
943 | } | ||
944 | /* | ||
945 | * ALSA destructor | ||
946 | */ | ||
947 | static void __devexit cx88_audio_finidev(struct pci_dev *pci) | ||
948 | { | ||
949 | struct cx88_audio_dev *card = pci_get_drvdata(pci); | ||
950 | |||
951 | snd_card_free((void *)card); | ||
952 | |||
953 | pci_set_drvdata(pci, NULL); | ||
954 | |||
955 | devno--; | ||
956 | } | ||
957 | |||
958 | /* | ||
959 | * PCI driver definition | ||
960 | */ | ||
961 | |||
962 | static struct pci_driver cx88_audio_pci_driver = { | ||
963 | .name = "cx88_audio", | ||
964 | .id_table = cx88_audio_pci_tbl, | ||
965 | .probe = cx88_audio_initdev, | ||
966 | .remove = __devexit_p(cx88_audio_finidev), | ||
967 | }; | ||
968 | |||
969 | /**************************************************************************** | ||
970 | LINUX MODULE INIT | ||
971 | ****************************************************************************/ | ||
972 | |||
973 | /* | ||
974 | * module init | ||
975 | */ | ||
976 | static int __init cx88_audio_init(void) | ||
977 | { | ||
978 | printk(KERN_INFO "cx2388x alsa driver version %s loaded\n", | ||
979 | CX88_VERSION); | ||
980 | return pci_register_driver(&cx88_audio_pci_driver); | ||
981 | } | ||
982 | |||
983 | /* | ||
984 | * module remove | ||
985 | */ | ||
986 | static void __exit cx88_audio_fini(void) | ||
987 | { | ||
988 | pci_unregister_driver(&cx88_audio_pci_driver); | ||
989 | } | ||
990 | |||
991 | module_init(cx88_audio_init); | ||
992 | module_exit(cx88_audio_fini); | ||
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c new file mode 100644 index 00000000000..e46446a449c --- /dev/null +++ b/drivers/media/video/cx88/cx88-blackbird.c | |||
@@ -0,0 +1,1385 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Support for a cx23416 mpeg encoder via cx2388x host port. | ||
4 | * "blackbird" reference design. | ||
5 | * | ||
6 | * (c) 2004 Jelle Foks <jelle@foks.us> | ||
7 | * (c) 2004 Gerd Knorr <kraxel@bytesex.org> | ||
8 | * | ||
9 | * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> | ||
10 | * - video_ioctl2 conversion | ||
11 | * | ||
12 | * Includes parts from the ivtv driver <http://sourceforge.net/projects/ivtv/> | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify | ||
15 | * it under the terms of the GNU General Public License as published by | ||
16 | * the Free Software Foundation; either version 2 of the License, or | ||
17 | * (at your option) any later version. | ||
18 | * | ||
19 | * This program is distributed in the hope that it will be useful, | ||
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | * GNU General Public License for more details. | ||
23 | * | ||
24 | * You should have received a copy of the GNU General Public License | ||
25 | * along with this program; if not, write to the Free Software | ||
26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | */ | ||
28 | |||
29 | #include <linux/module.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/fs.h> | ||
33 | #include <linux/delay.h> | ||
34 | #include <linux/device.h> | ||
35 | #include <linux/firmware.h> | ||
36 | #include <media/v4l2-common.h> | ||
37 | #include <media/v4l2-ioctl.h> | ||
38 | #include <media/cx2341x.h> | ||
39 | |||
40 | #include "cx88.h" | ||
41 | |||
42 | MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder cards"); | ||
43 | MODULE_AUTHOR("Jelle Foks <jelle@foks.us>, Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
44 | MODULE_LICENSE("GPL"); | ||
45 | MODULE_VERSION(CX88_VERSION); | ||
46 | |||
47 | static unsigned int mpegbufs = 32; | ||
48 | module_param(mpegbufs,int,0644); | ||
49 | MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32"); | ||
50 | |||
51 | static unsigned int debug; | ||
52 | module_param(debug,int,0644); | ||
53 | MODULE_PARM_DESC(debug,"enable debug messages [blackbird]"); | ||
54 | |||
55 | #define dprintk(level,fmt, arg...) if (debug >= level) \ | ||
56 | printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg) | ||
57 | |||
58 | |||
59 | /* ------------------------------------------------------------------ */ | ||
60 | |||
61 | #define BLACKBIRD_FIRM_IMAGE_SIZE 376836 | ||
62 | |||
63 | /* defines below are from ivtv-driver.h */ | ||
64 | |||
65 | #define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF | ||
66 | |||
67 | /* Firmware API commands */ | ||
68 | #define IVTV_API_STD_TIMEOUT 500 | ||
69 | |||
70 | enum blackbird_capture_type { | ||
71 | BLACKBIRD_MPEG_CAPTURE, | ||
72 | BLACKBIRD_RAW_CAPTURE, | ||
73 | BLACKBIRD_RAW_PASSTHRU_CAPTURE | ||
74 | }; | ||
75 | enum blackbird_capture_bits { | ||
76 | BLACKBIRD_RAW_BITS_NONE = 0x00, | ||
77 | BLACKBIRD_RAW_BITS_YUV_CAPTURE = 0x01, | ||
78 | BLACKBIRD_RAW_BITS_PCM_CAPTURE = 0x02, | ||
79 | BLACKBIRD_RAW_BITS_VBI_CAPTURE = 0x04, | ||
80 | BLACKBIRD_RAW_BITS_PASSTHRU_CAPTURE = 0x08, | ||
81 | BLACKBIRD_RAW_BITS_TO_HOST_CAPTURE = 0x10 | ||
82 | }; | ||
83 | enum blackbird_capture_end { | ||
84 | BLACKBIRD_END_AT_GOP, /* stop at the end of gop, generate irq */ | ||
85 | BLACKBIRD_END_NOW, /* stop immediately, no irq */ | ||
86 | }; | ||
87 | enum blackbird_framerate { | ||
88 | BLACKBIRD_FRAMERATE_NTSC_30, /* NTSC: 30fps */ | ||
89 | BLACKBIRD_FRAMERATE_PAL_25 /* PAL: 25fps */ | ||
90 | }; | ||
91 | enum blackbird_stream_port { | ||
92 | BLACKBIRD_OUTPUT_PORT_MEMORY, | ||
93 | BLACKBIRD_OUTPUT_PORT_STREAMING, | ||
94 | BLACKBIRD_OUTPUT_PORT_SERIAL | ||
95 | }; | ||
96 | enum blackbird_data_xfer_status { | ||
97 | BLACKBIRD_MORE_BUFFERS_FOLLOW, | ||
98 | BLACKBIRD_LAST_BUFFER, | ||
99 | }; | ||
100 | enum blackbird_picture_mask { | ||
101 | BLACKBIRD_PICTURE_MASK_NONE, | ||
102 | BLACKBIRD_PICTURE_MASK_I_FRAMES, | ||
103 | BLACKBIRD_PICTURE_MASK_I_P_FRAMES = 0x3, | ||
104 | BLACKBIRD_PICTURE_MASK_ALL_FRAMES = 0x7, | ||
105 | }; | ||
106 | enum blackbird_vbi_mode_bits { | ||
107 | BLACKBIRD_VBI_BITS_SLICED, | ||
108 | BLACKBIRD_VBI_BITS_RAW, | ||
109 | }; | ||
110 | enum blackbird_vbi_insertion_bits { | ||
111 | BLACKBIRD_VBI_BITS_INSERT_IN_XTENSION_USR_DATA, | ||
112 | BLACKBIRD_VBI_BITS_INSERT_IN_PRIVATE_PACKETS = 0x1 << 1, | ||
113 | BLACKBIRD_VBI_BITS_SEPARATE_STREAM = 0x2 << 1, | ||
114 | BLACKBIRD_VBI_BITS_SEPARATE_STREAM_USR_DATA = 0x4 << 1, | ||
115 | BLACKBIRD_VBI_BITS_SEPARATE_STREAM_PRV_DATA = 0x5 << 1, | ||
116 | }; | ||
117 | enum blackbird_dma_unit { | ||
118 | BLACKBIRD_DMA_BYTES, | ||
119 | BLACKBIRD_DMA_FRAMES, | ||
120 | }; | ||
121 | enum blackbird_dma_transfer_status_bits { | ||
122 | BLACKBIRD_DMA_TRANSFER_BITS_DONE = 0x01, | ||
123 | BLACKBIRD_DMA_TRANSFER_BITS_ERROR = 0x04, | ||
124 | BLACKBIRD_DMA_TRANSFER_BITS_LL_ERROR = 0x10, | ||
125 | }; | ||
126 | enum blackbird_pause { | ||
127 | BLACKBIRD_PAUSE_ENCODING, | ||
128 | BLACKBIRD_RESUME_ENCODING, | ||
129 | }; | ||
130 | enum blackbird_copyright { | ||
131 | BLACKBIRD_COPYRIGHT_OFF, | ||
132 | BLACKBIRD_COPYRIGHT_ON, | ||
133 | }; | ||
134 | enum blackbird_notification_type { | ||
135 | BLACKBIRD_NOTIFICATION_REFRESH, | ||
136 | }; | ||
137 | enum blackbird_notification_status { | ||
138 | BLACKBIRD_NOTIFICATION_OFF, | ||
139 | BLACKBIRD_NOTIFICATION_ON, | ||
140 | }; | ||
141 | enum blackbird_notification_mailbox { | ||
142 | BLACKBIRD_NOTIFICATION_NO_MAILBOX = -1, | ||
143 | }; | ||
144 | enum blackbird_field1_lines { | ||
145 | BLACKBIRD_FIELD1_SAA7114 = 0x00EF, /* 239 */ | ||
146 | BLACKBIRD_FIELD1_SAA7115 = 0x00F0, /* 240 */ | ||
147 | BLACKBIRD_FIELD1_MICRONAS = 0x0105, /* 261 */ | ||
148 | }; | ||
149 | enum blackbird_field2_lines { | ||
150 | BLACKBIRD_FIELD2_SAA7114 = 0x00EF, /* 239 */ | ||
151 | BLACKBIRD_FIELD2_SAA7115 = 0x00F0, /* 240 */ | ||
152 | BLACKBIRD_FIELD2_MICRONAS = 0x0106, /* 262 */ | ||
153 | }; | ||
154 | enum blackbird_custom_data_type { | ||
155 | BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, | ||
156 | BLACKBIRD_CUSTOM_PRIVATE_PACKET, | ||
157 | }; | ||
158 | enum blackbird_mute { | ||
159 | BLACKBIRD_UNMUTE, | ||
160 | BLACKBIRD_MUTE, | ||
161 | }; | ||
162 | enum blackbird_mute_video_mask { | ||
163 | BLACKBIRD_MUTE_VIDEO_V_MASK = 0x0000FF00, | ||
164 | BLACKBIRD_MUTE_VIDEO_U_MASK = 0x00FF0000, | ||
165 | BLACKBIRD_MUTE_VIDEO_Y_MASK = 0xFF000000, | ||
166 | }; | ||
167 | enum blackbird_mute_video_shift { | ||
168 | BLACKBIRD_MUTE_VIDEO_V_SHIFT = 8, | ||
169 | BLACKBIRD_MUTE_VIDEO_U_SHIFT = 16, | ||
170 | BLACKBIRD_MUTE_VIDEO_Y_SHIFT = 24, | ||
171 | }; | ||
172 | |||
173 | /* Registers */ | ||
174 | #define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/) | ||
175 | #define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/) | ||
176 | #define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/) | ||
177 | #define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/) | ||
178 | #define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/) | ||
179 | #define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/) | ||
180 | |||
181 | /* ------------------------------------------------------------------ */ | ||
182 | |||
183 | static void host_setup(struct cx88_core *core) | ||
184 | { | ||
185 | /* toggle reset of the host */ | ||
186 | cx_write(MO_GPHST_SOFT_RST, 1); | ||
187 | udelay(100); | ||
188 | cx_write(MO_GPHST_SOFT_RST, 0); | ||
189 | udelay(100); | ||
190 | |||
191 | /* host port setup */ | ||
192 | cx_write(MO_GPHST_WSC, 0x44444444U); | ||
193 | cx_write(MO_GPHST_XFR, 0); | ||
194 | cx_write(MO_GPHST_WDTH, 15); | ||
195 | cx_write(MO_GPHST_HDSHK, 0); | ||
196 | cx_write(MO_GPHST_MUX16, 0x44448888U); | ||
197 | cx_write(MO_GPHST_MODE, 0); | ||
198 | } | ||
199 | |||
200 | /* ------------------------------------------------------------------ */ | ||
201 | |||
202 | #define P1_MDATA0 0x390000 | ||
203 | #define P1_MDATA1 0x390001 | ||
204 | #define P1_MDATA2 0x390002 | ||
205 | #define P1_MDATA3 0x390003 | ||
206 | #define P1_MADDR2 0x390004 | ||
207 | #define P1_MADDR1 0x390005 | ||
208 | #define P1_MADDR0 0x390006 | ||
209 | #define P1_RDATA0 0x390008 | ||
210 | #define P1_RDATA1 0x390009 | ||
211 | #define P1_RDATA2 0x39000A | ||
212 | #define P1_RDATA3 0x39000B | ||
213 | #define P1_RADDR0 0x39000C | ||
214 | #define P1_RADDR1 0x39000D | ||
215 | #define P1_RRDWR 0x39000E | ||
216 | |||
217 | static int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state) | ||
218 | { | ||
219 | unsigned long timeout = jiffies + msecs_to_jiffies(1); | ||
220 | u32 gpio0,need; | ||
221 | |||
222 | need = state ? 2 : 0; | ||
223 | for (;;) { | ||
224 | gpio0 = cx_read(MO_GP0_IO) & 2; | ||
225 | if (need == gpio0) | ||
226 | return 0; | ||
227 | if (time_after(jiffies,timeout)) | ||
228 | return -1; | ||
229 | udelay(1); | ||
230 | } | ||
231 | } | ||
232 | |||
233 | static int memory_write(struct cx88_core *core, u32 address, u32 value) | ||
234 | { | ||
235 | /* Warning: address is dword address (4 bytes) */ | ||
236 | cx_writeb(P1_MDATA0, (unsigned int)value); | ||
237 | cx_writeb(P1_MDATA1, (unsigned int)(value >> 8)); | ||
238 | cx_writeb(P1_MDATA2, (unsigned int)(value >> 16)); | ||
239 | cx_writeb(P1_MDATA3, (unsigned int)(value >> 24)); | ||
240 | cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40); | ||
241 | cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); | ||
242 | cx_writeb(P1_MADDR0, (unsigned int)address); | ||
243 | cx_read(P1_MDATA0); | ||
244 | cx_read(P1_MADDR0); | ||
245 | |||
246 | return wait_ready_gpio0_bit1(core,1); | ||
247 | } | ||
248 | |||
249 | static int memory_read(struct cx88_core *core, u32 address, u32 *value) | ||
250 | { | ||
251 | int retval; | ||
252 | u32 val; | ||
253 | |||
254 | /* Warning: address is dword address (4 bytes) */ | ||
255 | cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0); | ||
256 | cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); | ||
257 | cx_writeb(P1_MADDR0, (unsigned int)address); | ||
258 | cx_read(P1_MADDR0); | ||
259 | |||
260 | retval = wait_ready_gpio0_bit1(core,1); | ||
261 | |||
262 | cx_writeb(P1_MDATA3, 0); | ||
263 | val = (unsigned char)cx_read(P1_MDATA3) << 24; | ||
264 | cx_writeb(P1_MDATA2, 0); | ||
265 | val |= (unsigned char)cx_read(P1_MDATA2) << 16; | ||
266 | cx_writeb(P1_MDATA1, 0); | ||
267 | val |= (unsigned char)cx_read(P1_MDATA1) << 8; | ||
268 | cx_writeb(P1_MDATA0, 0); | ||
269 | val |= (unsigned char)cx_read(P1_MDATA0); | ||
270 | |||
271 | *value = val; | ||
272 | return retval; | ||
273 | } | ||
274 | |||
275 | static int register_write(struct cx88_core *core, u32 address, u32 value) | ||
276 | { | ||
277 | cx_writeb(P1_RDATA0, (unsigned int)value); | ||
278 | cx_writeb(P1_RDATA1, (unsigned int)(value >> 8)); | ||
279 | cx_writeb(P1_RDATA2, (unsigned int)(value >> 16)); | ||
280 | cx_writeb(P1_RDATA3, (unsigned int)(value >> 24)); | ||
281 | cx_writeb(P1_RADDR0, (unsigned int)address); | ||
282 | cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); | ||
283 | cx_writeb(P1_RRDWR, 1); | ||
284 | cx_read(P1_RDATA0); | ||
285 | cx_read(P1_RADDR0); | ||
286 | |||
287 | return wait_ready_gpio0_bit1(core,1); | ||
288 | } | ||
289 | |||
290 | |||
291 | static int register_read(struct cx88_core *core, u32 address, u32 *value) | ||
292 | { | ||
293 | int retval; | ||
294 | u32 val; | ||
295 | |||
296 | cx_writeb(P1_RADDR0, (unsigned int)address); | ||
297 | cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); | ||
298 | cx_writeb(P1_RRDWR, 0); | ||
299 | cx_read(P1_RADDR0); | ||
300 | |||
301 | retval = wait_ready_gpio0_bit1(core,1); | ||
302 | val = (unsigned char)cx_read(P1_RDATA0); | ||
303 | val |= (unsigned char)cx_read(P1_RDATA1) << 8; | ||
304 | val |= (unsigned char)cx_read(P1_RDATA2) << 16; | ||
305 | val |= (unsigned char)cx_read(P1_RDATA3) << 24; | ||
306 | |||
307 | *value = val; | ||
308 | return retval; | ||
309 | } | ||
310 | |||
311 | /* ------------------------------------------------------------------ */ | ||
312 | |||
313 | static int blackbird_mbox_func(void *priv, u32 command, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) | ||
314 | { | ||
315 | struct cx8802_dev *dev = priv; | ||
316 | unsigned long timeout; | ||
317 | u32 value, flag, retval; | ||
318 | int i; | ||
319 | |||
320 | dprintk(1,"%s: 0x%X\n", __func__, command); | ||
321 | |||
322 | /* this may not be 100% safe if we can't read any memory location | ||
323 | without side effects */ | ||
324 | memory_read(dev->core, dev->mailbox - 4, &value); | ||
325 | if (value != 0x12345678) { | ||
326 | dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n"); | ||
327 | return -1; | ||
328 | } | ||
329 | |||
330 | memory_read(dev->core, dev->mailbox, &flag); | ||
331 | if (flag) { | ||
332 | dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag); | ||
333 | return -1; | ||
334 | } | ||
335 | |||
336 | flag |= 1; /* tell 'em we're working on it */ | ||
337 | memory_write(dev->core, dev->mailbox, flag); | ||
338 | |||
339 | /* write command + args + fill remaining with zeros */ | ||
340 | memory_write(dev->core, dev->mailbox + 1, command); /* command code */ | ||
341 | memory_write(dev->core, dev->mailbox + 3, IVTV_API_STD_TIMEOUT); /* timeout */ | ||
342 | for (i = 0; i < in; i++) { | ||
343 | memory_write(dev->core, dev->mailbox + 4 + i, data[i]); | ||
344 | dprintk(1, "API Input %d = %d\n", i, data[i]); | ||
345 | } | ||
346 | for (; i < CX2341X_MBOX_MAX_DATA; i++) | ||
347 | memory_write(dev->core, dev->mailbox + 4 + i, 0); | ||
348 | |||
349 | flag |= 3; /* tell 'em we're done writing */ | ||
350 | memory_write(dev->core, dev->mailbox, flag); | ||
351 | |||
352 | /* wait for firmware to handle the API command */ | ||
353 | timeout = jiffies + msecs_to_jiffies(10); | ||
354 | for (;;) { | ||
355 | memory_read(dev->core, dev->mailbox, &flag); | ||
356 | if (0 != (flag & 4)) | ||
357 | break; | ||
358 | if (time_after(jiffies,timeout)) { | ||
359 | dprintk(0, "ERROR: API Mailbox timeout\n"); | ||
360 | return -1; | ||
361 | } | ||
362 | udelay(10); | ||
363 | } | ||
364 | |||
365 | /* read output values */ | ||
366 | for (i = 0; i < out; i++) { | ||
367 | memory_read(dev->core, dev->mailbox + 4 + i, data + i); | ||
368 | dprintk(1, "API Output %d = %d\n", i, data[i]); | ||
369 | } | ||
370 | |||
371 | memory_read(dev->core, dev->mailbox + 2, &retval); | ||
372 | dprintk(1, "API result = %d\n",retval); | ||
373 | |||
374 | flag = 0; | ||
375 | memory_write(dev->core, dev->mailbox, flag); | ||
376 | return retval; | ||
377 | } | ||
378 | /* ------------------------------------------------------------------ */ | ||
379 | |||
380 | /* We don't need to call the API often, so using just one mailbox will probably suffice */ | ||
381 | static int blackbird_api_cmd(struct cx8802_dev *dev, u32 command, | ||
382 | u32 inputcnt, u32 outputcnt, ...) | ||
383 | { | ||
384 | u32 data[CX2341X_MBOX_MAX_DATA]; | ||
385 | va_list vargs; | ||
386 | int i, err; | ||
387 | |||
388 | va_start(vargs, outputcnt); | ||
389 | |||
390 | for (i = 0; i < inputcnt; i++) { | ||
391 | data[i] = va_arg(vargs, int); | ||
392 | } | ||
393 | err = blackbird_mbox_func(dev, command, inputcnt, outputcnt, data); | ||
394 | for (i = 0; i < outputcnt; i++) { | ||
395 | int *vptr = va_arg(vargs, int *); | ||
396 | *vptr = data[i]; | ||
397 | } | ||
398 | va_end(vargs); | ||
399 | return err; | ||
400 | } | ||
401 | |||
402 | static int blackbird_find_mailbox(struct cx8802_dev *dev) | ||
403 | { | ||
404 | u32 signature[4]={0x12345678, 0x34567812, 0x56781234, 0x78123456}; | ||
405 | int signaturecnt=0; | ||
406 | u32 value; | ||
407 | int i; | ||
408 | |||
409 | for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) { | ||
410 | memory_read(dev->core, i, &value); | ||
411 | if (value == signature[signaturecnt]) | ||
412 | signaturecnt++; | ||
413 | else | ||
414 | signaturecnt = 0; | ||
415 | if (4 == signaturecnt) { | ||
416 | dprintk(1, "Mailbox signature found\n"); | ||
417 | return i+1; | ||
418 | } | ||
419 | } | ||
420 | dprintk(0, "Mailbox signature values not found!\n"); | ||
421 | return -1; | ||
422 | } | ||
423 | |||
424 | static int blackbird_load_firmware(struct cx8802_dev *dev) | ||
425 | { | ||
426 | static const unsigned char magic[8] = { | ||
427 | 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa | ||
428 | }; | ||
429 | const struct firmware *firmware; | ||
430 | int i, retval = 0; | ||
431 | u32 value = 0; | ||
432 | u32 checksum = 0; | ||
433 | u32 *dataptr; | ||
434 | |||
435 | retval = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED); | ||
436 | retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); | ||
437 | retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_REFRESH, 0x80000640); | ||
438 | retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A); | ||
439 | msleep(1); | ||
440 | retval |= register_write(dev->core, IVTV_REG_APU, 0); | ||
441 | |||
442 | if (retval < 0) | ||
443 | dprintk(0, "Error with register_write\n"); | ||
444 | |||
445 | retval = request_firmware(&firmware, CX2341X_FIRM_ENC_FILENAME, | ||
446 | &dev->pci->dev); | ||
447 | |||
448 | |||
449 | if (retval != 0) { | ||
450 | dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n", | ||
451 | CX2341X_FIRM_ENC_FILENAME); | ||
452 | dprintk(0, "Please fix your hotplug setup, the board will " | ||
453 | "not work without firmware loaded!\n"); | ||
454 | return -1; | ||
455 | } | ||
456 | |||
457 | if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) { | ||
458 | dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n", | ||
459 | firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE); | ||
460 | release_firmware(firmware); | ||
461 | return -1; | ||
462 | } | ||
463 | |||
464 | if (0 != memcmp(firmware->data, magic, 8)) { | ||
465 | dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n"); | ||
466 | release_firmware(firmware); | ||
467 | return -1; | ||
468 | } | ||
469 | |||
470 | /* transfer to the chip */ | ||
471 | dprintk(1,"Loading firmware ...\n"); | ||
472 | dataptr = (u32*)firmware->data; | ||
473 | for (i = 0; i < (firmware->size >> 2); i++) { | ||
474 | value = *dataptr; | ||
475 | checksum += ~value; | ||
476 | memory_write(dev->core, i, value); | ||
477 | dataptr++; | ||
478 | } | ||
479 | |||
480 | /* read back to verify with the checksum */ | ||
481 | for (i--; i >= 0; i--) { | ||
482 | memory_read(dev->core, i, &value); | ||
483 | checksum -= ~value; | ||
484 | } | ||
485 | if (checksum) { | ||
486 | dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n"); | ||
487 | release_firmware(firmware); | ||
488 | return -1; | ||
489 | } | ||
490 | release_firmware(firmware); | ||
491 | dprintk(0, "Firmware upload successful.\n"); | ||
492 | |||
493 | retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); | ||
494 | retval |= register_read(dev->core, IVTV_REG_SPU, &value); | ||
495 | retval |= register_write(dev->core, IVTV_REG_SPU, value & 0xFFFFFFFE); | ||
496 | msleep(1); | ||
497 | |||
498 | retval |= register_read(dev->core, IVTV_REG_VPU, &value); | ||
499 | retval |= register_write(dev->core, IVTV_REG_VPU, value & 0xFFFFFFE8); | ||
500 | |||
501 | if (retval < 0) | ||
502 | dprintk(0, "Error with register_write\n"); | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | /** | ||
507 | Settings used by the windows tv app for PVR2000: | ||
508 | ================================================================================================================= | ||
509 | Profile | Codec | Resolution | CBR/VBR | Video Qlty | V. Bitrate | Frmrate | Audio Codec | A. Bitrate | A. Mode | ||
510 | ----------------------------------------------------------------------------------------------------------------- | ||
511 | MPEG-1 | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 2000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo | ||
512 | MPEG-2 | MPEG2 | 720x576PAL | VBR | 600 :Good | 4000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo | ||
513 | VCD | MPEG1 | 352x288PAL | (CBR) | 1000:Optimal | 1150 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo | ||
514 | DVD | MPEG2 | 720x576PAL | VBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo | ||
515 | DB* DVD | MPEG2 | 720x576PAL | CBR | 600 :Good | 6000 Kbps | 25fps | MPG1 Layer2 | 224kbps | Stereo | ||
516 | ================================================================================================================= | ||
517 | *DB: "DirectBurn" | ||
518 | */ | ||
519 | |||
520 | static void blackbird_codec_settings(struct cx8802_dev *dev) | ||
521 | { | ||
522 | /* assign frame size */ | ||
523 | blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, | ||
524 | dev->height, dev->width); | ||
525 | |||
526 | dev->params.width = dev->width; | ||
527 | dev->params.height = dev->height; | ||
528 | dev->params.is_50hz = (dev->core->tvnorm & V4L2_STD_625_50) != 0; | ||
529 | |||
530 | cx2341x_update(dev, blackbird_mbox_func, NULL, &dev->params); | ||
531 | } | ||
532 | |||
533 | static int blackbird_initialize_codec(struct cx8802_dev *dev) | ||
534 | { | ||
535 | struct cx88_core *core = dev->core; | ||
536 | int version; | ||
537 | int retval; | ||
538 | |||
539 | dprintk(1,"Initialize codec\n"); | ||
540 | retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ | ||
541 | if (retval < 0) { | ||
542 | |||
543 | dev->mpeg_active = 0; | ||
544 | |||
545 | /* ping was not successful, reset and upload firmware */ | ||
546 | cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */ | ||
547 | cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */ | ||
548 | retval = blackbird_load_firmware(dev); | ||
549 | if (retval < 0) | ||
550 | return retval; | ||
551 | |||
552 | retval = blackbird_find_mailbox(dev); | ||
553 | if (retval < 0) | ||
554 | return -1; | ||
555 | |||
556 | dev->mailbox = retval; | ||
557 | |||
558 | retval = blackbird_api_cmd(dev, CX2341X_ENC_PING_FW, 0, 0); /* ping */ | ||
559 | if (retval < 0) { | ||
560 | dprintk(0, "ERROR: Firmware ping failed!\n"); | ||
561 | return -1; | ||
562 | } | ||
563 | |||
564 | retval = blackbird_api_cmd(dev, CX2341X_ENC_GET_VERSION, 0, 1, &version); | ||
565 | if (retval < 0) { | ||
566 | dprintk(0, "ERROR: Firmware get encoder version failed!\n"); | ||
567 | return -1; | ||
568 | } | ||
569 | dprintk(0, "Firmware version is 0x%08x\n", version); | ||
570 | } | ||
571 | |||
572 | cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */ | ||
573 | cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */ | ||
574 | cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */ | ||
575 | cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */ | ||
576 | |||
577 | blackbird_codec_settings(dev); | ||
578 | |||
579 | blackbird_api_cmd(dev, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0, | ||
580 | BLACKBIRD_FIELD1_SAA7115, | ||
581 | BLACKBIRD_FIELD2_SAA7115 | ||
582 | ); | ||
583 | |||
584 | blackbird_api_cmd(dev, CX2341X_ENC_SET_PLACEHOLDER, 12, 0, | ||
585 | BLACKBIRD_CUSTOM_EXTENSION_USR_DATA, | ||
586 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); | ||
587 | |||
588 | return 0; | ||
589 | } | ||
590 | |||
591 | static int blackbird_start_codec(struct file *file, void *priv) | ||
592 | { | ||
593 | struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; | ||
594 | struct cx88_core *core = dev->core; | ||
595 | /* start capturing to the host interface */ | ||
596 | u32 reg; | ||
597 | |||
598 | int i; | ||
599 | int lastchange = -1; | ||
600 | int lastval = 0; | ||
601 | |||
602 | for (i = 0; (i < 10) && (i < (lastchange + 4)); i++) { | ||
603 | reg = cx_read(AUD_STATUS); | ||
604 | |||
605 | dprintk(1, "AUD_STATUS:%dL: 0x%x\n", i, reg); | ||
606 | if ((reg & 0x0F) != lastval) { | ||
607 | lastval = reg & 0x0F; | ||
608 | lastchange = i; | ||
609 | } | ||
610 | msleep(100); | ||
611 | } | ||
612 | |||
613 | /* unmute audio source */ | ||
614 | cx_clear(AUD_VOL_CTL, (1 << 6)); | ||
615 | |||
616 | blackbird_api_cmd(dev, CX2341X_ENC_REFRESH_INPUT, 0, 0); | ||
617 | |||
618 | /* initialize the video input */ | ||
619 | blackbird_api_cmd(dev, CX2341X_ENC_INITIALIZE_INPUT, 0, 0); | ||
620 | |||
621 | /* start capturing to the host interface */ | ||
622 | blackbird_api_cmd(dev, CX2341X_ENC_START_CAPTURE, 2, 0, | ||
623 | BLACKBIRD_MPEG_CAPTURE, | ||
624 | BLACKBIRD_RAW_BITS_NONE | ||
625 | ); | ||
626 | |||
627 | dev->mpeg_active = 1; | ||
628 | return 0; | ||
629 | } | ||
630 | |||
631 | static int blackbird_stop_codec(struct cx8802_dev *dev) | ||
632 | { | ||
633 | blackbird_api_cmd(dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, | ||
634 | BLACKBIRD_END_NOW, | ||
635 | BLACKBIRD_MPEG_CAPTURE, | ||
636 | BLACKBIRD_RAW_BITS_NONE | ||
637 | ); | ||
638 | |||
639 | dev->mpeg_active = 0; | ||
640 | return 0; | ||
641 | } | ||
642 | |||
643 | /* ------------------------------------------------------------------ */ | ||
644 | |||
645 | static int bb_buf_setup(struct videobuf_queue *q, | ||
646 | unsigned int *count, unsigned int *size) | ||
647 | { | ||
648 | struct cx8802_fh *fh = q->priv_data; | ||
649 | |||
650 | fh->dev->ts_packet_size = 188 * 4; /* was: 512 */ | ||
651 | fh->dev->ts_packet_count = mpegbufs; /* was: 100 */ | ||
652 | |||
653 | *size = fh->dev->ts_packet_size * fh->dev->ts_packet_count; | ||
654 | *count = fh->dev->ts_packet_count; | ||
655 | return 0; | ||
656 | } | ||
657 | |||
658 | static int | ||
659 | bb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, | ||
660 | enum v4l2_field field) | ||
661 | { | ||
662 | struct cx8802_fh *fh = q->priv_data; | ||
663 | return cx8802_buf_prepare(q, fh->dev, (struct cx88_buffer*)vb, field); | ||
664 | } | ||
665 | |||
666 | static void | ||
667 | bb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
668 | { | ||
669 | struct cx8802_fh *fh = q->priv_data; | ||
670 | cx8802_buf_queue(fh->dev, (struct cx88_buffer*)vb); | ||
671 | } | ||
672 | |||
673 | static void | ||
674 | bb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
675 | { | ||
676 | cx88_free_buffer(q, (struct cx88_buffer*)vb); | ||
677 | } | ||
678 | |||
679 | static struct videobuf_queue_ops blackbird_qops = { | ||
680 | .buf_setup = bb_buf_setup, | ||
681 | .buf_prepare = bb_buf_prepare, | ||
682 | .buf_queue = bb_buf_queue, | ||
683 | .buf_release = bb_buf_release, | ||
684 | }; | ||
685 | |||
686 | /* ------------------------------------------------------------------ */ | ||
687 | |||
688 | static const u32 *ctrl_classes[] = { | ||
689 | cx88_user_ctrls, | ||
690 | cx2341x_mpeg_ctrls, | ||
691 | NULL | ||
692 | }; | ||
693 | |||
694 | static int blackbird_queryctrl(struct cx8802_dev *dev, struct v4l2_queryctrl *qctrl) | ||
695 | { | ||
696 | qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); | ||
697 | if (qctrl->id == 0) | ||
698 | return -EINVAL; | ||
699 | |||
700 | /* Standard V4L2 controls */ | ||
701 | if (cx8800_ctrl_query(dev->core, qctrl) == 0) | ||
702 | return 0; | ||
703 | |||
704 | /* MPEG V4L2 controls */ | ||
705 | if (cx2341x_ctrl_query(&dev->params, qctrl)) | ||
706 | qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | /* ------------------------------------------------------------------ */ | ||
711 | /* IOCTL Handlers */ | ||
712 | |||
713 | static int vidioc_querymenu (struct file *file, void *priv, | ||
714 | struct v4l2_querymenu *qmenu) | ||
715 | { | ||
716 | struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; | ||
717 | struct v4l2_queryctrl qctrl; | ||
718 | |||
719 | qctrl.id = qmenu->id; | ||
720 | blackbird_queryctrl(dev, &qctrl); | ||
721 | return v4l2_ctrl_query_menu(qmenu, &qctrl, | ||
722 | cx2341x_ctrl_get_menu(&dev->params, qmenu->id)); | ||
723 | } | ||
724 | |||
725 | static int vidioc_querycap (struct file *file, void *priv, | ||
726 | struct v4l2_capability *cap) | ||
727 | { | ||
728 | struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; | ||
729 | struct cx88_core *core = dev->core; | ||
730 | |||
731 | strcpy(cap->driver, "cx88_blackbird"); | ||
732 | strlcpy(cap->card, core->board.name, sizeof(cap->card)); | ||
733 | sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); | ||
734 | cap->capabilities = | ||
735 | V4L2_CAP_VIDEO_CAPTURE | | ||
736 | V4L2_CAP_READWRITE | | ||
737 | V4L2_CAP_STREAMING; | ||
738 | if (UNSET != core->board.tuner_type) | ||
739 | cap->capabilities |= V4L2_CAP_TUNER; | ||
740 | return 0; | ||
741 | } | ||
742 | |||
743 | static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, | ||
744 | struct v4l2_fmtdesc *f) | ||
745 | { | ||
746 | if (f->index != 0) | ||
747 | return -EINVAL; | ||
748 | |||
749 | strlcpy(f->description, "MPEG", sizeof(f->description)); | ||
750 | f->pixelformat = V4L2_PIX_FMT_MPEG; | ||
751 | return 0; | ||
752 | } | ||
753 | |||
754 | static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, | ||
755 | struct v4l2_format *f) | ||
756 | { | ||
757 | struct cx8802_fh *fh = priv; | ||
758 | struct cx8802_dev *dev = fh->dev; | ||
759 | |||
760 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; | ||
761 | f->fmt.pix.bytesperline = 0; | ||
762 | f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */ | ||
763 | f->fmt.pix.colorspace = 0; | ||
764 | f->fmt.pix.width = dev->width; | ||
765 | f->fmt.pix.height = dev->height; | ||
766 | f->fmt.pix.field = fh->mpegq.field; | ||
767 | dprintk(0,"VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", | ||
768 | dev->width, dev->height, fh->mpegq.field ); | ||
769 | return 0; | ||
770 | } | ||
771 | |||
772 | static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, | ||
773 | struct v4l2_format *f) | ||
774 | { | ||
775 | struct cx8802_fh *fh = priv; | ||
776 | struct cx8802_dev *dev = fh->dev; | ||
777 | |||
778 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; | ||
779 | f->fmt.pix.bytesperline = 0; | ||
780 | f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; | ||
781 | f->fmt.pix.colorspace = 0; | ||
782 | dprintk(0,"VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", | ||
783 | dev->width, dev->height, fh->mpegq.field ); | ||
784 | return 0; | ||
785 | } | ||
786 | |||
787 | static int vidioc_s_fmt_vid_cap (struct file *file, void *priv, | ||
788 | struct v4l2_format *f) | ||
789 | { | ||
790 | struct cx8802_fh *fh = priv; | ||
791 | struct cx8802_dev *dev = fh->dev; | ||
792 | struct cx88_core *core = dev->core; | ||
793 | |||
794 | f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; | ||
795 | f->fmt.pix.bytesperline = 0; | ||
796 | f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; | ||
797 | f->fmt.pix.colorspace = 0; | ||
798 | dev->width = f->fmt.pix.width; | ||
799 | dev->height = f->fmt.pix.height; | ||
800 | fh->mpegq.field = f->fmt.pix.field; | ||
801 | cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); | ||
802 | blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, | ||
803 | f->fmt.pix.height, f->fmt.pix.width); | ||
804 | dprintk(0,"VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", | ||
805 | f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field ); | ||
806 | return 0; | ||
807 | } | ||
808 | |||
809 | static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p) | ||
810 | { | ||
811 | struct cx8802_fh *fh = priv; | ||
812 | return (videobuf_reqbufs(&fh->mpegq, p)); | ||
813 | } | ||
814 | |||
815 | static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p) | ||
816 | { | ||
817 | struct cx8802_fh *fh = priv; | ||
818 | return (videobuf_querybuf(&fh->mpegq, p)); | ||
819 | } | ||
820 | |||
821 | static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) | ||
822 | { | ||
823 | struct cx8802_fh *fh = priv; | ||
824 | return (videobuf_qbuf(&fh->mpegq, p)); | ||
825 | } | ||
826 | |||
827 | static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) | ||
828 | { | ||
829 | struct cx8802_fh *fh = priv; | ||
830 | return (videobuf_dqbuf(&fh->mpegq, p, | ||
831 | file->f_flags & O_NONBLOCK)); | ||
832 | } | ||
833 | |||
834 | static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) | ||
835 | { | ||
836 | struct cx8802_fh *fh = priv; | ||
837 | return videobuf_streamon(&fh->mpegq); | ||
838 | } | ||
839 | |||
840 | static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) | ||
841 | { | ||
842 | struct cx8802_fh *fh = priv; | ||
843 | return videobuf_streamoff(&fh->mpegq); | ||
844 | } | ||
845 | |||
846 | static int vidioc_g_ext_ctrls (struct file *file, void *priv, | ||
847 | struct v4l2_ext_controls *f) | ||
848 | { | ||
849 | struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; | ||
850 | |||
851 | if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) | ||
852 | return -EINVAL; | ||
853 | return cx2341x_ext_ctrls(&dev->params, 0, f, VIDIOC_G_EXT_CTRLS); | ||
854 | } | ||
855 | |||
856 | static int vidioc_s_ext_ctrls (struct file *file, void *priv, | ||
857 | struct v4l2_ext_controls *f) | ||
858 | { | ||
859 | struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; | ||
860 | struct cx2341x_mpeg_params p; | ||
861 | int err; | ||
862 | |||
863 | if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) | ||
864 | return -EINVAL; | ||
865 | |||
866 | if (dev->mpeg_active) | ||
867 | blackbird_stop_codec(dev); | ||
868 | |||
869 | p = dev->params; | ||
870 | err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_S_EXT_CTRLS); | ||
871 | if (!err) { | ||
872 | err = cx2341x_update(dev, blackbird_mbox_func, &dev->params, &p); | ||
873 | dev->params = p; | ||
874 | } | ||
875 | return err; | ||
876 | } | ||
877 | |||
878 | static int vidioc_try_ext_ctrls (struct file *file, void *priv, | ||
879 | struct v4l2_ext_controls *f) | ||
880 | { | ||
881 | struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; | ||
882 | struct cx2341x_mpeg_params p; | ||
883 | int err; | ||
884 | |||
885 | if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) | ||
886 | return -EINVAL; | ||
887 | p = dev->params; | ||
888 | err = cx2341x_ext_ctrls(&p, 0, f, VIDIOC_TRY_EXT_CTRLS); | ||
889 | |||
890 | return err; | ||
891 | } | ||
892 | |||
893 | static int vidioc_s_frequency (struct file *file, void *priv, | ||
894 | struct v4l2_frequency *f) | ||
895 | { | ||
896 | struct cx8802_fh *fh = priv; | ||
897 | struct cx8802_dev *dev = fh->dev; | ||
898 | struct cx88_core *core = dev->core; | ||
899 | |||
900 | if (dev->mpeg_active) | ||
901 | blackbird_stop_codec(dev); | ||
902 | |||
903 | cx88_set_freq (core,f); | ||
904 | blackbird_initialize_codec(dev); | ||
905 | cx88_set_scale(dev->core, dev->width, dev->height, | ||
906 | fh->mpegq.field); | ||
907 | return 0; | ||
908 | } | ||
909 | |||
910 | static int vidioc_log_status (struct file *file, void *priv) | ||
911 | { | ||
912 | struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; | ||
913 | struct cx88_core *core = dev->core; | ||
914 | char name[32 + 2]; | ||
915 | |||
916 | snprintf(name, sizeof(name), "%s/2", core->name); | ||
917 | printk("%s/2: ============ START LOG STATUS ============\n", | ||
918 | core->name); | ||
919 | call_all(core, core, log_status); | ||
920 | cx2341x_log_status(&dev->params, name); | ||
921 | printk("%s/2: ============= END LOG STATUS =============\n", | ||
922 | core->name); | ||
923 | return 0; | ||
924 | } | ||
925 | |||
926 | static int vidioc_queryctrl (struct file *file, void *priv, | ||
927 | struct v4l2_queryctrl *qctrl) | ||
928 | { | ||
929 | struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; | ||
930 | |||
931 | if (blackbird_queryctrl(dev, qctrl) == 0) | ||
932 | return 0; | ||
933 | |||
934 | qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); | ||
935 | if (unlikely(qctrl->id == 0)) | ||
936 | return -EINVAL; | ||
937 | return cx8800_ctrl_query(dev->core, qctrl); | ||
938 | } | ||
939 | |||
940 | static int vidioc_enum_input (struct file *file, void *priv, | ||
941 | struct v4l2_input *i) | ||
942 | { | ||
943 | struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; | ||
944 | return cx88_enum_input (core,i); | ||
945 | } | ||
946 | |||
947 | static int vidioc_g_ctrl (struct file *file, void *priv, | ||
948 | struct v4l2_control *ctl) | ||
949 | { | ||
950 | struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; | ||
951 | return | ||
952 | cx88_get_control(core,ctl); | ||
953 | } | ||
954 | |||
955 | static int vidioc_s_ctrl (struct file *file, void *priv, | ||
956 | struct v4l2_control *ctl) | ||
957 | { | ||
958 | struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; | ||
959 | return | ||
960 | cx88_set_control(core,ctl); | ||
961 | } | ||
962 | |||
963 | static int vidioc_g_frequency (struct file *file, void *priv, | ||
964 | struct v4l2_frequency *f) | ||
965 | { | ||
966 | struct cx8802_fh *fh = priv; | ||
967 | struct cx88_core *core = fh->dev->core; | ||
968 | |||
969 | if (unlikely(UNSET == core->board.tuner_type)) | ||
970 | return -EINVAL; | ||
971 | |||
972 | f->type = V4L2_TUNER_ANALOG_TV; | ||
973 | f->frequency = core->freq; | ||
974 | call_all(core, tuner, g_frequency, f); | ||
975 | |||
976 | return 0; | ||
977 | } | ||
978 | |||
979 | static int vidioc_g_input (struct file *file, void *priv, unsigned int *i) | ||
980 | { | ||
981 | struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; | ||
982 | |||
983 | *i = core->input; | ||
984 | return 0; | ||
985 | } | ||
986 | |||
987 | static int vidioc_s_input (struct file *file, void *priv, unsigned int i) | ||
988 | { | ||
989 | struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; | ||
990 | |||
991 | if (i >= 4) | ||
992 | return -EINVAL; | ||
993 | |||
994 | mutex_lock(&core->lock); | ||
995 | cx88_newstation(core); | ||
996 | cx88_video_mux(core,i); | ||
997 | mutex_unlock(&core->lock); | ||
998 | return 0; | ||
999 | } | ||
1000 | |||
1001 | static int vidioc_g_tuner (struct file *file, void *priv, | ||
1002 | struct v4l2_tuner *t) | ||
1003 | { | ||
1004 | struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; | ||
1005 | u32 reg; | ||
1006 | |||
1007 | if (unlikely(UNSET == core->board.tuner_type)) | ||
1008 | return -EINVAL; | ||
1009 | if (0 != t->index) | ||
1010 | return -EINVAL; | ||
1011 | |||
1012 | strcpy(t->name, "Television"); | ||
1013 | t->type = V4L2_TUNER_ANALOG_TV; | ||
1014 | t->capability = V4L2_TUNER_CAP_NORM; | ||
1015 | t->rangehigh = 0xffffffffUL; | ||
1016 | |||
1017 | cx88_get_stereo(core ,t); | ||
1018 | reg = cx_read(MO_DEVICE_STATUS); | ||
1019 | t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; | ||
1020 | return 0; | ||
1021 | } | ||
1022 | |||
1023 | static int vidioc_s_tuner (struct file *file, void *priv, | ||
1024 | struct v4l2_tuner *t) | ||
1025 | { | ||
1026 | struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; | ||
1027 | |||
1028 | if (UNSET == core->board.tuner_type) | ||
1029 | return -EINVAL; | ||
1030 | if (0 != t->index) | ||
1031 | return -EINVAL; | ||
1032 | |||
1033 | cx88_set_stereo(core, t->audmode, 1); | ||
1034 | return 0; | ||
1035 | } | ||
1036 | |||
1037 | static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) | ||
1038 | { | ||
1039 | struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; | ||
1040 | |||
1041 | mutex_lock(&core->lock); | ||
1042 | cx88_set_tvnorm(core,*id); | ||
1043 | mutex_unlock(&core->lock); | ||
1044 | return 0; | ||
1045 | } | ||
1046 | |||
1047 | /* FIXME: cx88_ioctl_hook not implemented */ | ||
1048 | |||
1049 | static int mpeg_open(struct file *file) | ||
1050 | { | ||
1051 | struct video_device *vdev = video_devdata(file); | ||
1052 | struct cx8802_dev *dev = video_drvdata(file); | ||
1053 | struct cx8802_fh *fh; | ||
1054 | struct cx8802_driver *drv = NULL; | ||
1055 | int err; | ||
1056 | |||
1057 | dprintk( 1, "%s\n", __func__); | ||
1058 | |||
1059 | mutex_lock(&dev->core->lock); | ||
1060 | |||
1061 | /* Make sure we can acquire the hardware */ | ||
1062 | drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); | ||
1063 | if (!drv) { | ||
1064 | dprintk(1, "%s: blackbird driver is not loaded\n", __func__); | ||
1065 | mutex_unlock(&dev->core->lock); | ||
1066 | return -ENODEV; | ||
1067 | } | ||
1068 | |||
1069 | err = drv->request_acquire(drv); | ||
1070 | if (err != 0) { | ||
1071 | dprintk(1,"%s: Unable to acquire hardware, %d\n", __func__, err); | ||
1072 | mutex_unlock(&dev->core->lock); | ||
1073 | return err; | ||
1074 | } | ||
1075 | |||
1076 | if (!dev->core->mpeg_users && blackbird_initialize_codec(dev) < 0) { | ||
1077 | drv->request_release(drv); | ||
1078 | mutex_unlock(&dev->core->lock); | ||
1079 | return -EINVAL; | ||
1080 | } | ||
1081 | dprintk(1, "open dev=%s\n", video_device_node_name(vdev)); | ||
1082 | |||
1083 | /* allocate + initialize per filehandle data */ | ||
1084 | fh = kzalloc(sizeof(*fh),GFP_KERNEL); | ||
1085 | if (NULL == fh) { | ||
1086 | drv->request_release(drv); | ||
1087 | mutex_unlock(&dev->core->lock); | ||
1088 | return -ENOMEM; | ||
1089 | } | ||
1090 | file->private_data = fh; | ||
1091 | fh->dev = dev; | ||
1092 | |||
1093 | videobuf_queue_sg_init(&fh->mpegq, &blackbird_qops, | ||
1094 | &dev->pci->dev, &dev->slock, | ||
1095 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
1096 | V4L2_FIELD_INTERLACED, | ||
1097 | sizeof(struct cx88_buffer), | ||
1098 | fh, NULL); | ||
1099 | |||
1100 | /* FIXME: locking against other video device */ | ||
1101 | cx88_set_scale(dev->core, dev->width, dev->height, | ||
1102 | fh->mpegq.field); | ||
1103 | |||
1104 | dev->core->mpeg_users++; | ||
1105 | mutex_unlock(&dev->core->lock); | ||
1106 | return 0; | ||
1107 | } | ||
1108 | |||
1109 | static int mpeg_release(struct file *file) | ||
1110 | { | ||
1111 | struct cx8802_fh *fh = file->private_data; | ||
1112 | struct cx8802_dev *dev = fh->dev; | ||
1113 | struct cx8802_driver *drv = NULL; | ||
1114 | |||
1115 | mutex_lock(&dev->core->lock); | ||
1116 | |||
1117 | if (dev->mpeg_active && dev->core->mpeg_users == 1) | ||
1118 | blackbird_stop_codec(dev); | ||
1119 | |||
1120 | cx8802_cancel_buffers(fh->dev); | ||
1121 | /* stop mpeg capture */ | ||
1122 | videobuf_stop(&fh->mpegq); | ||
1123 | |||
1124 | videobuf_mmap_free(&fh->mpegq); | ||
1125 | |||
1126 | file->private_data = NULL; | ||
1127 | kfree(fh); | ||
1128 | |||
1129 | /* Make sure we release the hardware */ | ||
1130 | drv = cx8802_get_driver(dev, CX88_MPEG_BLACKBIRD); | ||
1131 | WARN_ON(!drv); | ||
1132 | if (drv) | ||
1133 | drv->request_release(drv); | ||
1134 | |||
1135 | dev->core->mpeg_users--; | ||
1136 | |||
1137 | mutex_unlock(&dev->core->lock); | ||
1138 | |||
1139 | return 0; | ||
1140 | } | ||
1141 | |||
1142 | static ssize_t | ||
1143 | mpeg_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | ||
1144 | { | ||
1145 | struct cx8802_fh *fh = file->private_data; | ||
1146 | struct cx8802_dev *dev = fh->dev; | ||
1147 | |||
1148 | if (!dev->mpeg_active) | ||
1149 | blackbird_start_codec(file, fh); | ||
1150 | |||
1151 | return videobuf_read_stream(&fh->mpegq, data, count, ppos, 0, | ||
1152 | file->f_flags & O_NONBLOCK); | ||
1153 | } | ||
1154 | |||
1155 | static unsigned int | ||
1156 | mpeg_poll(struct file *file, struct poll_table_struct *wait) | ||
1157 | { | ||
1158 | struct cx8802_fh *fh = file->private_data; | ||
1159 | struct cx8802_dev *dev = fh->dev; | ||
1160 | |||
1161 | if (!dev->mpeg_active) | ||
1162 | blackbird_start_codec(file, fh); | ||
1163 | |||
1164 | return videobuf_poll_stream(file, &fh->mpegq, wait); | ||
1165 | } | ||
1166 | |||
1167 | static int | ||
1168 | mpeg_mmap(struct file *file, struct vm_area_struct * vma) | ||
1169 | { | ||
1170 | struct cx8802_fh *fh = file->private_data; | ||
1171 | |||
1172 | return videobuf_mmap_mapper(&fh->mpegq, vma); | ||
1173 | } | ||
1174 | |||
1175 | static const struct v4l2_file_operations mpeg_fops = | ||
1176 | { | ||
1177 | .owner = THIS_MODULE, | ||
1178 | .open = mpeg_open, | ||
1179 | .release = mpeg_release, | ||
1180 | .read = mpeg_read, | ||
1181 | .poll = mpeg_poll, | ||
1182 | .mmap = mpeg_mmap, | ||
1183 | .ioctl = video_ioctl2, | ||
1184 | }; | ||
1185 | |||
1186 | static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { | ||
1187 | .vidioc_querymenu = vidioc_querymenu, | ||
1188 | .vidioc_querycap = vidioc_querycap, | ||
1189 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, | ||
1190 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, | ||
1191 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, | ||
1192 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, | ||
1193 | .vidioc_reqbufs = vidioc_reqbufs, | ||
1194 | .vidioc_querybuf = vidioc_querybuf, | ||
1195 | .vidioc_qbuf = vidioc_qbuf, | ||
1196 | .vidioc_dqbuf = vidioc_dqbuf, | ||
1197 | .vidioc_streamon = vidioc_streamon, | ||
1198 | .vidioc_streamoff = vidioc_streamoff, | ||
1199 | .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, | ||
1200 | .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, | ||
1201 | .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, | ||
1202 | .vidioc_s_frequency = vidioc_s_frequency, | ||
1203 | .vidioc_log_status = vidioc_log_status, | ||
1204 | .vidioc_queryctrl = vidioc_queryctrl, | ||
1205 | .vidioc_enum_input = vidioc_enum_input, | ||
1206 | .vidioc_g_ctrl = vidioc_g_ctrl, | ||
1207 | .vidioc_s_ctrl = vidioc_s_ctrl, | ||
1208 | .vidioc_g_frequency = vidioc_g_frequency, | ||
1209 | .vidioc_g_input = vidioc_g_input, | ||
1210 | .vidioc_s_input = vidioc_s_input, | ||
1211 | .vidioc_g_tuner = vidioc_g_tuner, | ||
1212 | .vidioc_s_tuner = vidioc_s_tuner, | ||
1213 | .vidioc_s_std = vidioc_s_std, | ||
1214 | }; | ||
1215 | |||
1216 | static struct video_device cx8802_mpeg_template = { | ||
1217 | .name = "cx8802", | ||
1218 | .fops = &mpeg_fops, | ||
1219 | .ioctl_ops = &mpeg_ioctl_ops, | ||
1220 | .tvnorms = CX88_NORMS, | ||
1221 | .current_norm = V4L2_STD_NTSC_M, | ||
1222 | }; | ||
1223 | |||
1224 | /* ------------------------------------------------------------------ */ | ||
1225 | |||
1226 | /* The CX8802 MPEG API will call this when we can use the hardware */ | ||
1227 | static int cx8802_blackbird_advise_acquire(struct cx8802_driver *drv) | ||
1228 | { | ||
1229 | struct cx88_core *core = drv->core; | ||
1230 | int err = 0; | ||
1231 | |||
1232 | switch (core->boardnr) { | ||
1233 | case CX88_BOARD_HAUPPAUGE_HVR1300: | ||
1234 | /* By default, core setup will leave the cx22702 out of reset, on the bus. | ||
1235 | * We left the hardware on power up with the cx22702 active. | ||
1236 | * We're being given access to re-arrange the GPIOs. | ||
1237 | * Take the bus off the cx22702 and put the cx23416 on it. | ||
1238 | */ | ||
1239 | /* Toggle reset on cx22702 leaving i2c active */ | ||
1240 | cx_set(MO_GP0_IO, 0x00000080); | ||
1241 | udelay(1000); | ||
1242 | cx_clear(MO_GP0_IO, 0x00000080); | ||
1243 | udelay(50); | ||
1244 | cx_set(MO_GP0_IO, 0x00000080); | ||
1245 | udelay(1000); | ||
1246 | /* tri-state the cx22702 pins */ | ||
1247 | cx_set(MO_GP0_IO, 0x00000004); | ||
1248 | udelay(1000); | ||
1249 | break; | ||
1250 | default: | ||
1251 | err = -ENODEV; | ||
1252 | } | ||
1253 | return err; | ||
1254 | } | ||
1255 | |||
1256 | /* The CX8802 MPEG API will call this when we need to release the hardware */ | ||
1257 | static int cx8802_blackbird_advise_release(struct cx8802_driver *drv) | ||
1258 | { | ||
1259 | struct cx88_core *core = drv->core; | ||
1260 | int err = 0; | ||
1261 | |||
1262 | switch (core->boardnr) { | ||
1263 | case CX88_BOARD_HAUPPAUGE_HVR1300: | ||
1264 | /* Exit leaving the cx23416 on the bus */ | ||
1265 | break; | ||
1266 | default: | ||
1267 | err = -ENODEV; | ||
1268 | } | ||
1269 | return err; | ||
1270 | } | ||
1271 | |||
1272 | static void blackbird_unregister_video(struct cx8802_dev *dev) | ||
1273 | { | ||
1274 | if (dev->mpeg_dev) { | ||
1275 | if (video_is_registered(dev->mpeg_dev)) | ||
1276 | video_unregister_device(dev->mpeg_dev); | ||
1277 | else | ||
1278 | video_device_release(dev->mpeg_dev); | ||
1279 | dev->mpeg_dev = NULL; | ||
1280 | } | ||
1281 | } | ||
1282 | |||
1283 | static int blackbird_register_video(struct cx8802_dev *dev) | ||
1284 | { | ||
1285 | int err; | ||
1286 | |||
1287 | dev->mpeg_dev = cx88_vdev_init(dev->core,dev->pci, | ||
1288 | &cx8802_mpeg_template,"mpeg"); | ||
1289 | video_set_drvdata(dev->mpeg_dev, dev); | ||
1290 | err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1); | ||
1291 | if (err < 0) { | ||
1292 | printk(KERN_INFO "%s/2: can't register mpeg device\n", | ||
1293 | dev->core->name); | ||
1294 | return err; | ||
1295 | } | ||
1296 | printk(KERN_INFO "%s/2: registered device %s [mpeg]\n", | ||
1297 | dev->core->name, video_device_node_name(dev->mpeg_dev)); | ||
1298 | return 0; | ||
1299 | } | ||
1300 | |||
1301 | /* ----------------------------------------------------------- */ | ||
1302 | |||
1303 | static int cx8802_blackbird_probe(struct cx8802_driver *drv) | ||
1304 | { | ||
1305 | struct cx88_core *core = drv->core; | ||
1306 | struct cx8802_dev *dev = core->dvbdev; | ||
1307 | int err; | ||
1308 | |||
1309 | dprintk( 1, "%s\n", __func__); | ||
1310 | dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", | ||
1311 | core->boardnr, | ||
1312 | core->name, | ||
1313 | core->pci_bus, | ||
1314 | core->pci_slot); | ||
1315 | |||
1316 | err = -ENODEV; | ||
1317 | if (!(core->board.mpeg & CX88_MPEG_BLACKBIRD)) | ||
1318 | goto fail_core; | ||
1319 | |||
1320 | dev->width = 720; | ||
1321 | dev->height = 576; | ||
1322 | cx2341x_fill_defaults(&dev->params); | ||
1323 | dev->params.port = CX2341X_PORT_STREAMING; | ||
1324 | |||
1325 | cx8802_mpeg_template.current_norm = core->tvnorm; | ||
1326 | |||
1327 | if (core->tvnorm & V4L2_STD_525_60) { | ||
1328 | dev->height = 480; | ||
1329 | } else { | ||
1330 | dev->height = 576; | ||
1331 | } | ||
1332 | |||
1333 | /* blackbird stuff */ | ||
1334 | printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n", | ||
1335 | core->name); | ||
1336 | host_setup(dev->core); | ||
1337 | |||
1338 | blackbird_initialize_codec(dev); | ||
1339 | blackbird_register_video(dev); | ||
1340 | |||
1341 | /* initial device configuration: needed ? */ | ||
1342 | // init_controls(core); | ||
1343 | cx88_set_tvnorm(core,core->tvnorm); | ||
1344 | cx88_video_mux(core,0); | ||
1345 | |||
1346 | return 0; | ||
1347 | |||
1348 | fail_core: | ||
1349 | return err; | ||
1350 | } | ||
1351 | |||
1352 | static int cx8802_blackbird_remove(struct cx8802_driver *drv) | ||
1353 | { | ||
1354 | /* blackbird */ | ||
1355 | blackbird_unregister_video(drv->core->dvbdev); | ||
1356 | |||
1357 | return 0; | ||
1358 | } | ||
1359 | |||
1360 | static struct cx8802_driver cx8802_blackbird_driver = { | ||
1361 | .type_id = CX88_MPEG_BLACKBIRD, | ||
1362 | .hw_access = CX8802_DRVCTL_SHARED, | ||
1363 | .probe = cx8802_blackbird_probe, | ||
1364 | .remove = cx8802_blackbird_remove, | ||
1365 | .advise_acquire = cx8802_blackbird_advise_acquire, | ||
1366 | .advise_release = cx8802_blackbird_advise_release, | ||
1367 | }; | ||
1368 | |||
1369 | static int __init blackbird_init(void) | ||
1370 | { | ||
1371 | printk(KERN_INFO "cx2388x blackbird driver version %s loaded\n", | ||
1372 | CX88_VERSION); | ||
1373 | return cx8802_register_driver(&cx8802_blackbird_driver); | ||
1374 | } | ||
1375 | |||
1376 | static void __exit blackbird_fini(void) | ||
1377 | { | ||
1378 | cx8802_unregister_driver(&cx8802_blackbird_driver); | ||
1379 | } | ||
1380 | |||
1381 | module_init(blackbird_init); | ||
1382 | module_exit(blackbird_fini); | ||
1383 | |||
1384 | module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644); | ||
1385 | MODULE_PARM_DESC(debug,"enable debug messages [video]"); | ||
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c new file mode 100644 index 00000000000..0d719faafd8 --- /dev/null +++ b/drivers/media/video/cx88/cx88-cards.c | |||
@@ -0,0 +1,3699 @@ | |||
1 | /* | ||
2 | * | ||
3 | * device driver for Conexant 2388x based TV cards | ||
4 | * card-specific stuff. | ||
5 | * | ||
6 | * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | */ | ||
22 | |||
23 | #include <linux/init.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/pci.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/slab.h> | ||
28 | |||
29 | #include "cx88.h" | ||
30 | #include "tea5767.h" | ||
31 | #include "xc4000.h" | ||
32 | |||
33 | static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
34 | static unsigned int radio[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
35 | static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
36 | |||
37 | module_param_array(tuner, int, NULL, 0444); | ||
38 | module_param_array(radio, int, NULL, 0444); | ||
39 | module_param_array(card, int, NULL, 0444); | ||
40 | |||
41 | MODULE_PARM_DESC(tuner,"tuner type"); | ||
42 | MODULE_PARM_DESC(radio,"radio tuner type"); | ||
43 | MODULE_PARM_DESC(card,"card type"); | ||
44 | |||
45 | static unsigned int latency = UNSET; | ||
46 | module_param(latency,int,0444); | ||
47 | MODULE_PARM_DESC(latency,"pci latency timer"); | ||
48 | |||
49 | static int disable_ir; | ||
50 | module_param(disable_ir, int, 0444); | ||
51 | MODULE_PARM_DESC(disable_ir, "Disable IR support"); | ||
52 | |||
53 | #define info_printk(core, fmt, arg...) \ | ||
54 | printk(KERN_INFO "%s: " fmt, core->name , ## arg) | ||
55 | |||
56 | #define warn_printk(core, fmt, arg...) \ | ||
57 | printk(KERN_WARNING "%s: " fmt, core->name , ## arg) | ||
58 | |||
59 | #define err_printk(core, fmt, arg...) \ | ||
60 | printk(KERN_ERR "%s: " fmt, core->name , ## arg) | ||
61 | |||
62 | |||
63 | /* ------------------------------------------------------------------ */ | ||
64 | /* board config info */ | ||
65 | |||
66 | /* If radio_type !=UNSET, radio_addr should be specified | ||
67 | */ | ||
68 | |||
69 | static const struct cx88_board cx88_boards[] = { | ||
70 | [CX88_BOARD_UNKNOWN] = { | ||
71 | .name = "UNKNOWN/GENERIC", | ||
72 | .tuner_type = UNSET, | ||
73 | .radio_type = UNSET, | ||
74 | .tuner_addr = ADDR_UNSET, | ||
75 | .radio_addr = ADDR_UNSET, | ||
76 | .input = {{ | ||
77 | .type = CX88_VMUX_COMPOSITE1, | ||
78 | .vmux = 0, | ||
79 | },{ | ||
80 | .type = CX88_VMUX_COMPOSITE2, | ||
81 | .vmux = 1, | ||
82 | },{ | ||
83 | .type = CX88_VMUX_COMPOSITE3, | ||
84 | .vmux = 2, | ||
85 | },{ | ||
86 | .type = CX88_VMUX_COMPOSITE4, | ||
87 | .vmux = 3, | ||
88 | }}, | ||
89 | }, | ||
90 | [CX88_BOARD_HAUPPAUGE] = { | ||
91 | .name = "Hauppauge WinTV 34xxx models", | ||
92 | .tuner_type = UNSET, | ||
93 | .radio_type = UNSET, | ||
94 | .tuner_addr = ADDR_UNSET, | ||
95 | .radio_addr = ADDR_UNSET, | ||
96 | .tda9887_conf = TDA9887_PRESENT, | ||
97 | .input = {{ | ||
98 | .type = CX88_VMUX_TELEVISION, | ||
99 | .vmux = 0, | ||
100 | .gpio0 = 0xff00, // internal decoder | ||
101 | },{ | ||
102 | .type = CX88_VMUX_DEBUG, | ||
103 | .vmux = 0, | ||
104 | .gpio0 = 0xff01, // mono from tuner chip | ||
105 | },{ | ||
106 | .type = CX88_VMUX_COMPOSITE1, | ||
107 | .vmux = 1, | ||
108 | .gpio0 = 0xff02, | ||
109 | },{ | ||
110 | .type = CX88_VMUX_SVIDEO, | ||
111 | .vmux = 2, | ||
112 | .gpio0 = 0xff02, | ||
113 | }}, | ||
114 | .radio = { | ||
115 | .type = CX88_RADIO, | ||
116 | .gpio0 = 0xff01, | ||
117 | }, | ||
118 | }, | ||
119 | [CX88_BOARD_GDI] = { | ||
120 | .name = "GDI Black Gold", | ||
121 | .tuner_type = UNSET, | ||
122 | .radio_type = UNSET, | ||
123 | .tuner_addr = ADDR_UNSET, | ||
124 | .radio_addr = ADDR_UNSET, | ||
125 | .input = {{ | ||
126 | .type = CX88_VMUX_TELEVISION, | ||
127 | .vmux = 0, | ||
128 | },{ | ||
129 | .type = CX88_VMUX_SVIDEO, | ||
130 | .vmux = 2, | ||
131 | }}, | ||
132 | }, | ||
133 | [CX88_BOARD_PIXELVIEW] = { | ||
134 | .name = "PixelView", | ||
135 | .tuner_type = TUNER_PHILIPS_PAL, | ||
136 | .radio_type = UNSET, | ||
137 | .tuner_addr = ADDR_UNSET, | ||
138 | .radio_addr = ADDR_UNSET, | ||
139 | .input = {{ | ||
140 | .type = CX88_VMUX_TELEVISION, | ||
141 | .vmux = 0, | ||
142 | .gpio0 = 0xff00, // internal decoder | ||
143 | },{ | ||
144 | .type = CX88_VMUX_COMPOSITE1, | ||
145 | .vmux = 1, | ||
146 | },{ | ||
147 | .type = CX88_VMUX_SVIDEO, | ||
148 | .vmux = 2, | ||
149 | }}, | ||
150 | .radio = { | ||
151 | .type = CX88_RADIO, | ||
152 | .gpio0 = 0xff10, | ||
153 | }, | ||
154 | }, | ||
155 | [CX88_BOARD_ATI_WONDER_PRO] = { | ||
156 | .name = "ATI TV Wonder Pro", | ||
157 | .tuner_type = TUNER_PHILIPS_4IN1, | ||
158 | .radio_type = UNSET, | ||
159 | .tuner_addr = ADDR_UNSET, | ||
160 | .radio_addr = ADDR_UNSET, | ||
161 | .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER, | ||
162 | .input = {{ | ||
163 | .type = CX88_VMUX_TELEVISION, | ||
164 | .vmux = 0, | ||
165 | .gpio0 = 0x03ff, | ||
166 | },{ | ||
167 | .type = CX88_VMUX_COMPOSITE1, | ||
168 | .vmux = 1, | ||
169 | .gpio0 = 0x03fe, | ||
170 | },{ | ||
171 | .type = CX88_VMUX_SVIDEO, | ||
172 | .vmux = 2, | ||
173 | .gpio0 = 0x03fe, | ||
174 | }}, | ||
175 | }, | ||
176 | [CX88_BOARD_WINFAST2000XP_EXPERT] = { | ||
177 | .name = "Leadtek Winfast 2000XP Expert", | ||
178 | .tuner_type = TUNER_PHILIPS_4IN1, | ||
179 | .radio_type = UNSET, | ||
180 | .tuner_addr = ADDR_UNSET, | ||
181 | .radio_addr = ADDR_UNSET, | ||
182 | .tda9887_conf = TDA9887_PRESENT, | ||
183 | .input = {{ | ||
184 | .type = CX88_VMUX_TELEVISION, | ||
185 | .vmux = 0, | ||
186 | .gpio0 = 0x00F5e700, | ||
187 | .gpio1 = 0x00003004, | ||
188 | .gpio2 = 0x00F5e700, | ||
189 | .gpio3 = 0x02000000, | ||
190 | },{ | ||
191 | .type = CX88_VMUX_COMPOSITE1, | ||
192 | .vmux = 1, | ||
193 | .gpio0 = 0x00F5c700, | ||
194 | .gpio1 = 0x00003004, | ||
195 | .gpio2 = 0x00F5c700, | ||
196 | .gpio3 = 0x02000000, | ||
197 | },{ | ||
198 | .type = CX88_VMUX_SVIDEO, | ||
199 | .vmux = 2, | ||
200 | .gpio0 = 0x00F5c700, | ||
201 | .gpio1 = 0x00003004, | ||
202 | .gpio2 = 0x00F5c700, | ||
203 | .gpio3 = 0x02000000, | ||
204 | }}, | ||
205 | .radio = { | ||
206 | .type = CX88_RADIO, | ||
207 | .gpio0 = 0x00F5d700, | ||
208 | .gpio1 = 0x00003004, | ||
209 | .gpio2 = 0x00F5d700, | ||
210 | .gpio3 = 0x02000000, | ||
211 | }, | ||
212 | }, | ||
213 | [CX88_BOARD_AVERTV_STUDIO_303] = { | ||
214 | .name = "AverTV Studio 303 (M126)", | ||
215 | .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, | ||
216 | .radio_type = UNSET, | ||
217 | .tuner_addr = ADDR_UNSET, | ||
218 | .radio_addr = ADDR_UNSET, | ||
219 | .tda9887_conf = TDA9887_PRESENT, | ||
220 | .input = {{ | ||
221 | .type = CX88_VMUX_TELEVISION, | ||
222 | .vmux = 0, | ||
223 | .gpio1 = 0xe09f, | ||
224 | },{ | ||
225 | .type = CX88_VMUX_COMPOSITE1, | ||
226 | .vmux = 1, | ||
227 | .gpio1 = 0xe05f, | ||
228 | },{ | ||
229 | .type = CX88_VMUX_SVIDEO, | ||
230 | .vmux = 2, | ||
231 | .gpio1 = 0xe05f, | ||
232 | }}, | ||
233 | .radio = { | ||
234 | .gpio1 = 0xe0df, | ||
235 | .type = CX88_RADIO, | ||
236 | }, | ||
237 | }, | ||
238 | [CX88_BOARD_MSI_TVANYWHERE_MASTER] = { | ||
239 | // added gpio values thanks to Michal | ||
240 | // values for PAL from DScaler | ||
241 | .name = "MSI TV-@nywhere Master", | ||
242 | .tuner_type = TUNER_MT2032, | ||
243 | .radio_type = UNSET, | ||
244 | .tuner_addr = ADDR_UNSET, | ||
245 | .radio_addr = ADDR_UNSET, | ||
246 | .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER_NTSC, | ||
247 | .input = {{ | ||
248 | .type = CX88_VMUX_TELEVISION, | ||
249 | .vmux = 0, | ||
250 | .gpio0 = 0x000040bf, | ||
251 | .gpio1 = 0x000080c0, | ||
252 | .gpio2 = 0x0000ff40, | ||
253 | },{ | ||
254 | .type = CX88_VMUX_COMPOSITE1, | ||
255 | .vmux = 1, | ||
256 | .gpio0 = 0x000040bf, | ||
257 | .gpio1 = 0x000080c0, | ||
258 | .gpio2 = 0x0000ff40, | ||
259 | },{ | ||
260 | .type = CX88_VMUX_SVIDEO, | ||
261 | .vmux = 2, | ||
262 | .gpio0 = 0x000040bf, | ||
263 | .gpio1 = 0x000080c0, | ||
264 | .gpio2 = 0x0000ff40, | ||
265 | }}, | ||
266 | .radio = { | ||
267 | .type = CX88_RADIO, | ||
268 | .vmux = 3, | ||
269 | .gpio0 = 0x000040bf, | ||
270 | .gpio1 = 0x000080c0, | ||
271 | .gpio2 = 0x0000ff20, | ||
272 | }, | ||
273 | }, | ||
274 | [CX88_BOARD_WINFAST_DV2000] = { | ||
275 | .name = "Leadtek Winfast DV2000", | ||
276 | .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, | ||
277 | .radio_type = UNSET, | ||
278 | .tuner_addr = ADDR_UNSET, | ||
279 | .radio_addr = ADDR_UNSET, | ||
280 | .tda9887_conf = TDA9887_PRESENT, | ||
281 | .input = {{ | ||
282 | .type = CX88_VMUX_TELEVISION, | ||
283 | .vmux = 0, | ||
284 | .gpio0 = 0x0035e700, | ||
285 | .gpio1 = 0x00003004, | ||
286 | .gpio2 = 0x0035e700, | ||
287 | .gpio3 = 0x02000000, | ||
288 | },{ | ||
289 | |||
290 | .type = CX88_VMUX_COMPOSITE1, | ||
291 | .vmux = 1, | ||
292 | .gpio0 = 0x0035c700, | ||
293 | .gpio1 = 0x00003004, | ||
294 | .gpio2 = 0x0035c700, | ||
295 | .gpio3 = 0x02000000, | ||
296 | },{ | ||
297 | .type = CX88_VMUX_SVIDEO, | ||
298 | .vmux = 2, | ||
299 | .gpio0 = 0x0035c700, | ||
300 | .gpio1 = 0x0035c700, | ||
301 | .gpio2 = 0x02000000, | ||
302 | .gpio3 = 0x02000000, | ||
303 | }}, | ||
304 | .radio = { | ||
305 | .type = CX88_RADIO, | ||
306 | .gpio0 = 0x0035d700, | ||
307 | .gpio1 = 0x00007004, | ||
308 | .gpio2 = 0x0035d700, | ||
309 | .gpio3 = 0x02000000, | ||
310 | }, | ||
311 | }, | ||
312 | [CX88_BOARD_LEADTEK_PVR2000] = { | ||
313 | // gpio values for PAL version from regspy by DScaler | ||
314 | .name = "Leadtek PVR 2000", | ||
315 | .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, | ||
316 | .radio_type = UNSET, | ||
317 | .tuner_addr = ADDR_UNSET, | ||
318 | .radio_addr = ADDR_UNSET, | ||
319 | .tda9887_conf = TDA9887_PRESENT, | ||
320 | .input = {{ | ||
321 | .type = CX88_VMUX_TELEVISION, | ||
322 | .vmux = 0, | ||
323 | .gpio0 = 0x0000bde2, | ||
324 | .audioroute = 1, | ||
325 | },{ | ||
326 | .type = CX88_VMUX_COMPOSITE1, | ||
327 | .vmux = 1, | ||
328 | .gpio0 = 0x0000bde6, | ||
329 | .audioroute = 1, | ||
330 | },{ | ||
331 | .type = CX88_VMUX_SVIDEO, | ||
332 | .vmux = 2, | ||
333 | .gpio0 = 0x0000bde6, | ||
334 | .audioroute = 1, | ||
335 | }}, | ||
336 | .radio = { | ||
337 | .type = CX88_RADIO, | ||
338 | .gpio0 = 0x0000bd62, | ||
339 | .audioroute = 1, | ||
340 | }, | ||
341 | .mpeg = CX88_MPEG_BLACKBIRD, | ||
342 | }, | ||
343 | [CX88_BOARD_IODATA_GVVCP3PCI] = { | ||
344 | .name = "IODATA GV-VCP3/PCI", | ||
345 | .tuner_type = TUNER_ABSENT, | ||
346 | .radio_type = UNSET, | ||
347 | .tuner_addr = ADDR_UNSET, | ||
348 | .radio_addr = ADDR_UNSET, | ||
349 | .input = {{ | ||
350 | .type = CX88_VMUX_COMPOSITE1, | ||
351 | .vmux = 0, | ||
352 | },{ | ||
353 | .type = CX88_VMUX_COMPOSITE2, | ||
354 | .vmux = 1, | ||
355 | },{ | ||
356 | .type = CX88_VMUX_SVIDEO, | ||
357 | .vmux = 2, | ||
358 | }}, | ||
359 | }, | ||
360 | [CX88_BOARD_PROLINK_PLAYTVPVR] = { | ||
361 | .name = "Prolink PlayTV PVR", | ||
362 | .tuner_type = TUNER_PHILIPS_FM1236_MK3, | ||
363 | .radio_type = UNSET, | ||
364 | .tuner_addr = ADDR_UNSET, | ||
365 | .radio_addr = ADDR_UNSET, | ||
366 | .tda9887_conf = TDA9887_PRESENT, | ||
367 | .input = {{ | ||
368 | .type = CX88_VMUX_TELEVISION, | ||
369 | .vmux = 0, | ||
370 | .gpio0 = 0xbff0, | ||
371 | },{ | ||
372 | .type = CX88_VMUX_COMPOSITE1, | ||
373 | .vmux = 1, | ||
374 | .gpio0 = 0xbff3, | ||
375 | },{ | ||
376 | .type = CX88_VMUX_SVIDEO, | ||
377 | .vmux = 2, | ||
378 | .gpio0 = 0xbff3, | ||
379 | }}, | ||
380 | .radio = { | ||
381 | .type = CX88_RADIO, | ||
382 | .gpio0 = 0xbff0, | ||
383 | }, | ||
384 | }, | ||
385 | [CX88_BOARD_ASUS_PVR_416] = { | ||
386 | .name = "ASUS PVR-416", | ||
387 | .tuner_type = TUNER_PHILIPS_FM1236_MK3, | ||
388 | .radio_type = UNSET, | ||
389 | .tuner_addr = ADDR_UNSET, | ||
390 | .radio_addr = ADDR_UNSET, | ||
391 | .tda9887_conf = TDA9887_PRESENT, | ||
392 | .input = {{ | ||
393 | .type = CX88_VMUX_TELEVISION, | ||
394 | .vmux = 0, | ||
395 | .gpio0 = 0x0000fde6, | ||
396 | },{ | ||
397 | .type = CX88_VMUX_SVIDEO, | ||
398 | .vmux = 2, | ||
399 | .gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in? | ||
400 | .audioroute = 1, | ||
401 | }}, | ||
402 | .radio = { | ||
403 | .type = CX88_RADIO, | ||
404 | .gpio0 = 0x0000fde2, | ||
405 | }, | ||
406 | .mpeg = CX88_MPEG_BLACKBIRD, | ||
407 | }, | ||
408 | [CX88_BOARD_MSI_TVANYWHERE] = { | ||
409 | .name = "MSI TV-@nywhere", | ||
410 | .tuner_type = TUNER_MT2032, | ||
411 | .radio_type = UNSET, | ||
412 | .tuner_addr = ADDR_UNSET, | ||
413 | .radio_addr = ADDR_UNSET, | ||
414 | .tda9887_conf = TDA9887_PRESENT, | ||
415 | .input = {{ | ||
416 | .type = CX88_VMUX_TELEVISION, | ||
417 | .vmux = 0, | ||
418 | .gpio0 = 0x00000fbf, | ||
419 | .gpio2 = 0x0000fc08, | ||
420 | },{ | ||
421 | .type = CX88_VMUX_COMPOSITE1, | ||
422 | .vmux = 1, | ||
423 | .gpio0 = 0x00000fbf, | ||
424 | .gpio2 = 0x0000fc68, | ||
425 | },{ | ||
426 | .type = CX88_VMUX_SVIDEO, | ||
427 | .vmux = 2, | ||
428 | .gpio0 = 0x00000fbf, | ||
429 | .gpio2 = 0x0000fc68, | ||
430 | }}, | ||
431 | }, | ||
432 | [CX88_BOARD_KWORLD_DVB_T] = { | ||
433 | .name = "KWorld/VStream XPert DVB-T", | ||
434 | .tuner_type = TUNER_ABSENT, | ||
435 | .radio_type = UNSET, | ||
436 | .tuner_addr = ADDR_UNSET, | ||
437 | .radio_addr = ADDR_UNSET, | ||
438 | .input = {{ | ||
439 | .type = CX88_VMUX_COMPOSITE1, | ||
440 | .vmux = 1, | ||
441 | .gpio0 = 0x0700, | ||
442 | .gpio2 = 0x0101, | ||
443 | },{ | ||
444 | .type = CX88_VMUX_SVIDEO, | ||
445 | .vmux = 2, | ||
446 | .gpio0 = 0x0700, | ||
447 | .gpio2 = 0x0101, | ||
448 | }}, | ||
449 | .mpeg = CX88_MPEG_DVB, | ||
450 | }, | ||
451 | [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = { | ||
452 | .name = "DViCO FusionHDTV DVB-T1", | ||
453 | .tuner_type = TUNER_ABSENT, /* No analog tuner */ | ||
454 | .radio_type = UNSET, | ||
455 | .tuner_addr = ADDR_UNSET, | ||
456 | .radio_addr = ADDR_UNSET, | ||
457 | .input = {{ | ||
458 | .type = CX88_VMUX_COMPOSITE1, | ||
459 | .vmux = 1, | ||
460 | .gpio0 = 0x000027df, | ||
461 | },{ | ||
462 | .type = CX88_VMUX_SVIDEO, | ||
463 | .vmux = 2, | ||
464 | .gpio0 = 0x000027df, | ||
465 | }}, | ||
466 | .mpeg = CX88_MPEG_DVB, | ||
467 | }, | ||
468 | [CX88_BOARD_KWORLD_LTV883] = { | ||
469 | .name = "KWorld LTV883RF", | ||
470 | .tuner_type = TUNER_TNF_8831BGFF, | ||
471 | .radio_type = UNSET, | ||
472 | .tuner_addr = ADDR_UNSET, | ||
473 | .radio_addr = ADDR_UNSET, | ||
474 | .input = {{ | ||
475 | .type = CX88_VMUX_TELEVISION, | ||
476 | .vmux = 0, | ||
477 | .gpio0 = 0x07f8, | ||
478 | },{ | ||
479 | .type = CX88_VMUX_DEBUG, | ||
480 | .vmux = 0, | ||
481 | .gpio0 = 0x07f9, // mono from tuner chip | ||
482 | },{ | ||
483 | .type = CX88_VMUX_COMPOSITE1, | ||
484 | .vmux = 1, | ||
485 | .gpio0 = 0x000007fa, | ||
486 | },{ | ||
487 | .type = CX88_VMUX_SVIDEO, | ||
488 | .vmux = 2, | ||
489 | .gpio0 = 0x000007fa, | ||
490 | }}, | ||
491 | .radio = { | ||
492 | .type = CX88_RADIO, | ||
493 | .gpio0 = 0x000007f8, | ||
494 | }, | ||
495 | }, | ||
496 | [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q] = { | ||
497 | .name = "DViCO FusionHDTV 3 Gold-Q", | ||
498 | .tuner_type = TUNER_MICROTUNE_4042FI5, | ||
499 | .radio_type = UNSET, | ||
500 | .tuner_addr = ADDR_UNSET, | ||
501 | .radio_addr = ADDR_UNSET, | ||
502 | /* | ||
503 | GPIO[0] resets DT3302 DTV receiver | ||
504 | 0 - reset asserted | ||
505 | 1 - normal operation | ||
506 | GPIO[1] mutes analog audio output connector | ||
507 | 0 - enable selected source | ||
508 | 1 - mute | ||
509 | GPIO[2] selects source for analog audio output connector | ||
510 | 0 - analog audio input connector on tab | ||
511 | 1 - analog DAC output from CX23881 chip | ||
512 | GPIO[3] selects RF input connector on tuner module | ||
513 | 0 - RF connector labeled CABLE | ||
514 | 1 - RF connector labeled ANT | ||
515 | GPIO[4] selects high RF for QAM256 mode | ||
516 | 0 - normal RF | ||
517 | 1 - high RF | ||
518 | */ | ||
519 | .input = {{ | ||
520 | .type = CX88_VMUX_TELEVISION, | ||
521 | .vmux = 0, | ||
522 | .gpio0 = 0x0f0d, | ||
523 | },{ | ||
524 | .type = CX88_VMUX_CABLE, | ||
525 | .vmux = 0, | ||
526 | .gpio0 = 0x0f05, | ||
527 | },{ | ||
528 | .type = CX88_VMUX_COMPOSITE1, | ||
529 | .vmux = 1, | ||
530 | .gpio0 = 0x0f00, | ||
531 | },{ | ||
532 | .type = CX88_VMUX_SVIDEO, | ||
533 | .vmux = 2, | ||
534 | .gpio0 = 0x0f00, | ||
535 | }}, | ||
536 | .mpeg = CX88_MPEG_DVB, | ||
537 | }, | ||
538 | [CX88_BOARD_HAUPPAUGE_DVB_T1] = { | ||
539 | .name = "Hauppauge Nova-T DVB-T", | ||
540 | .tuner_type = TUNER_ABSENT, | ||
541 | .radio_type = UNSET, | ||
542 | .tuner_addr = ADDR_UNSET, | ||
543 | .radio_addr = ADDR_UNSET, | ||
544 | .input = {{ | ||
545 | .type = CX88_VMUX_DVB, | ||
546 | .vmux = 0, | ||
547 | }}, | ||
548 | .mpeg = CX88_MPEG_DVB, | ||
549 | }, | ||
550 | [CX88_BOARD_CONEXANT_DVB_T1] = { | ||
551 | .name = "Conexant DVB-T reference design", | ||
552 | .tuner_type = TUNER_ABSENT, | ||
553 | .radio_type = UNSET, | ||
554 | .tuner_addr = ADDR_UNSET, | ||
555 | .radio_addr = ADDR_UNSET, | ||
556 | .input = {{ | ||
557 | .type = CX88_VMUX_DVB, | ||
558 | .vmux = 0, | ||
559 | }}, | ||
560 | .mpeg = CX88_MPEG_DVB, | ||
561 | }, | ||
562 | [CX88_BOARD_PROVIDEO_PV259] = { | ||
563 | .name = "Provideo PV259", | ||
564 | .tuner_type = TUNER_PHILIPS_FQ1216ME, | ||
565 | .radio_type = UNSET, | ||
566 | .tuner_addr = ADDR_UNSET, | ||
567 | .radio_addr = ADDR_UNSET, | ||
568 | .input = {{ | ||
569 | .type = CX88_VMUX_TELEVISION, | ||
570 | .vmux = 0, | ||
571 | .audioroute = 1, | ||
572 | }}, | ||
573 | .mpeg = CX88_MPEG_BLACKBIRD, | ||
574 | }, | ||
575 | [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS] = { | ||
576 | .name = "DViCO FusionHDTV DVB-T Plus", | ||
577 | .tuner_type = TUNER_ABSENT, /* No analog tuner */ | ||
578 | .radio_type = UNSET, | ||
579 | .tuner_addr = ADDR_UNSET, | ||
580 | .radio_addr = ADDR_UNSET, | ||
581 | .input = {{ | ||
582 | .type = CX88_VMUX_COMPOSITE1, | ||
583 | .vmux = 1, | ||
584 | .gpio0 = 0x000027df, | ||
585 | },{ | ||
586 | .type = CX88_VMUX_SVIDEO, | ||
587 | .vmux = 2, | ||
588 | .gpio0 = 0x000027df, | ||
589 | }}, | ||
590 | .mpeg = CX88_MPEG_DVB, | ||
591 | }, | ||
592 | [CX88_BOARD_DNTV_LIVE_DVB_T] = { | ||
593 | .name = "digitalnow DNTV Live! DVB-T", | ||
594 | .tuner_type = TUNER_ABSENT, | ||
595 | .radio_type = UNSET, | ||
596 | .tuner_addr = ADDR_UNSET, | ||
597 | .radio_addr = ADDR_UNSET, | ||
598 | .input = {{ | ||
599 | .type = CX88_VMUX_COMPOSITE1, | ||
600 | .vmux = 1, | ||
601 | .gpio0 = 0x00000700, | ||
602 | .gpio2 = 0x00000101, | ||
603 | },{ | ||
604 | .type = CX88_VMUX_SVIDEO, | ||
605 | .vmux = 2, | ||
606 | .gpio0 = 0x00000700, | ||
607 | .gpio2 = 0x00000101, | ||
608 | }}, | ||
609 | .mpeg = CX88_MPEG_DVB, | ||
610 | }, | ||
611 | [CX88_BOARD_PCHDTV_HD3000] = { | ||
612 | .name = "pcHDTV HD3000 HDTV", | ||
613 | .tuner_type = TUNER_THOMSON_DTT761X, | ||
614 | .radio_type = UNSET, | ||
615 | .tuner_addr = ADDR_UNSET, | ||
616 | .radio_addr = ADDR_UNSET, | ||
617 | .tda9887_conf = TDA9887_PRESENT, | ||
618 | /* GPIO[2] = audio source for analog audio out connector | ||
619 | * 0 = analog audio input connector | ||
620 | * 1 = CX88 audio DACs | ||
621 | * | ||
622 | * GPIO[7] = input to CX88's audio/chroma ADC | ||
623 | * 0 = FM 10.7 MHz IF | ||
624 | * 1 = Sound 4.5 MHz IF | ||
625 | * | ||
626 | * GPIO[1,5,6] = Oren 51132 pins 27,35,28 respectively | ||
627 | * | ||
628 | * GPIO[16] = Remote control input | ||
629 | */ | ||
630 | .input = {{ | ||
631 | .type = CX88_VMUX_TELEVISION, | ||
632 | .vmux = 0, | ||
633 | .gpio0 = 0x00008484, | ||
634 | },{ | ||
635 | .type = CX88_VMUX_COMPOSITE1, | ||
636 | .vmux = 1, | ||
637 | .gpio0 = 0x00008400, | ||
638 | },{ | ||
639 | .type = CX88_VMUX_SVIDEO, | ||
640 | .vmux = 2, | ||
641 | .gpio0 = 0x00008400, | ||
642 | }}, | ||
643 | .radio = { | ||
644 | .type = CX88_RADIO, | ||
645 | .gpio0 = 0x00008404, | ||
646 | }, | ||
647 | .mpeg = CX88_MPEG_DVB, | ||
648 | }, | ||
649 | [CX88_BOARD_HAUPPAUGE_ROSLYN] = { | ||
650 | // entry added by Kaustubh D. Bhalerao <bhalerao.1@osu.edu> | ||
651 | // GPIO values obtained from regspy, courtesy Sean Covel | ||
652 | .name = "Hauppauge WinTV 28xxx (Roslyn) models", | ||
653 | .tuner_type = UNSET, | ||
654 | .radio_type = UNSET, | ||
655 | .tuner_addr = ADDR_UNSET, | ||
656 | .radio_addr = ADDR_UNSET, | ||
657 | .input = {{ | ||
658 | .type = CX88_VMUX_TELEVISION, | ||
659 | .vmux = 0, | ||
660 | .gpio0 = 0xed1a, | ||
661 | .gpio2 = 0x00ff, | ||
662 | },{ | ||
663 | .type = CX88_VMUX_DEBUG, | ||
664 | .vmux = 0, | ||
665 | .gpio0 = 0xff01, | ||
666 | },{ | ||
667 | .type = CX88_VMUX_COMPOSITE1, | ||
668 | .vmux = 1, | ||
669 | .gpio0 = 0xff02, | ||
670 | },{ | ||
671 | .type = CX88_VMUX_SVIDEO, | ||
672 | .vmux = 2, | ||
673 | .gpio0 = 0xed92, | ||
674 | .gpio2 = 0x00ff, | ||
675 | }}, | ||
676 | .radio = { | ||
677 | .type = CX88_RADIO, | ||
678 | .gpio0 = 0xed96, | ||
679 | .gpio2 = 0x00ff, | ||
680 | }, | ||
681 | .mpeg = CX88_MPEG_BLACKBIRD, | ||
682 | }, | ||
683 | [CX88_BOARD_DIGITALLOGIC_MEC] = { | ||
684 | .name = "Digital-Logic MICROSPACE Entertainment Center (MEC)", | ||
685 | .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, | ||
686 | .radio_type = UNSET, | ||
687 | .tuner_addr = ADDR_UNSET, | ||
688 | .radio_addr = ADDR_UNSET, | ||
689 | .tda9887_conf = TDA9887_PRESENT, | ||
690 | .input = {{ | ||
691 | .type = CX88_VMUX_TELEVISION, | ||
692 | .vmux = 0, | ||
693 | .gpio0 = 0x00009d80, | ||
694 | .audioroute = 1, | ||
695 | },{ | ||
696 | .type = CX88_VMUX_COMPOSITE1, | ||
697 | .vmux = 1, | ||
698 | .gpio0 = 0x00009d76, | ||
699 | .audioroute = 1, | ||
700 | },{ | ||
701 | .type = CX88_VMUX_SVIDEO, | ||
702 | .vmux = 2, | ||
703 | .gpio0 = 0x00009d76, | ||
704 | .audioroute = 1, | ||
705 | }}, | ||
706 | .radio = { | ||
707 | .type = CX88_RADIO, | ||
708 | .gpio0 = 0x00009d00, | ||
709 | .audioroute = 1, | ||
710 | }, | ||
711 | .mpeg = CX88_MPEG_BLACKBIRD, | ||
712 | }, | ||
713 | [CX88_BOARD_IODATA_GVBCTV7E] = { | ||
714 | .name = "IODATA GV/BCTV7E", | ||
715 | .tuner_type = TUNER_PHILIPS_FQ1286, | ||
716 | .radio_type = UNSET, | ||
717 | .tuner_addr = ADDR_UNSET, | ||
718 | .radio_addr = ADDR_UNSET, | ||
719 | .tda9887_conf = TDA9887_PRESENT, | ||
720 | .input = {{ | ||
721 | .type = CX88_VMUX_TELEVISION, | ||
722 | .vmux = 1, | ||
723 | .gpio1 = 0x0000e03f, | ||
724 | },{ | ||
725 | .type = CX88_VMUX_COMPOSITE1, | ||
726 | .vmux = 2, | ||
727 | .gpio1 = 0x0000e07f, | ||
728 | },{ | ||
729 | .type = CX88_VMUX_SVIDEO, | ||
730 | .vmux = 3, | ||
731 | .gpio1 = 0x0000e07f, | ||
732 | }} | ||
733 | }, | ||
734 | [CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO] = { | ||
735 | .name = "PixelView PlayTV Ultra Pro (Stereo)", | ||
736 | /* May be also TUNER_YMEC_TVF_5533MF for NTSC/M or PAL/M */ | ||
737 | .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, | ||
738 | .radio_type = UNSET, | ||
739 | .tuner_addr = ADDR_UNSET, | ||
740 | .radio_addr = ADDR_UNSET, | ||
741 | /* Some variants use a tda9874 and so need the tvaudio module. */ | ||
742 | .audio_chip = V4L2_IDENT_TVAUDIO, | ||
743 | .input = {{ | ||
744 | .type = CX88_VMUX_TELEVISION, | ||
745 | .vmux = 0, | ||
746 | .gpio0 = 0xbf61, /* internal decoder */ | ||
747 | },{ | ||
748 | .type = CX88_VMUX_COMPOSITE1, | ||
749 | .vmux = 1, | ||
750 | .gpio0 = 0xbf63, | ||
751 | },{ | ||
752 | .type = CX88_VMUX_SVIDEO, | ||
753 | .vmux = 2, | ||
754 | .gpio0 = 0xbf63, | ||
755 | }}, | ||
756 | .radio = { | ||
757 | .type = CX88_RADIO, | ||
758 | .gpio0 = 0xbf60, | ||
759 | }, | ||
760 | }, | ||
761 | [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T] = { | ||
762 | .name = "DViCO FusionHDTV 3 Gold-T", | ||
763 | .tuner_type = TUNER_THOMSON_DTT761X, | ||
764 | .radio_type = UNSET, | ||
765 | .tuner_addr = ADDR_UNSET, | ||
766 | .radio_addr = ADDR_UNSET, | ||
767 | .tda9887_conf = TDA9887_PRESENT, | ||
768 | .input = {{ | ||
769 | .type = CX88_VMUX_TELEVISION, | ||
770 | .vmux = 0, | ||
771 | .gpio0 = 0x97ed, | ||
772 | },{ | ||
773 | .type = CX88_VMUX_COMPOSITE1, | ||
774 | .vmux = 1, | ||
775 | .gpio0 = 0x97e9, | ||
776 | },{ | ||
777 | .type = CX88_VMUX_SVIDEO, | ||
778 | .vmux = 2, | ||
779 | .gpio0 = 0x97e9, | ||
780 | }}, | ||
781 | .mpeg = CX88_MPEG_DVB, | ||
782 | }, | ||
783 | [CX88_BOARD_ADSTECH_DVB_T_PCI] = { | ||
784 | .name = "ADS Tech Instant TV DVB-T PCI", | ||
785 | .tuner_type = TUNER_ABSENT, | ||
786 | .radio_type = UNSET, | ||
787 | .tuner_addr = ADDR_UNSET, | ||
788 | .radio_addr = ADDR_UNSET, | ||
789 | .input = {{ | ||
790 | .type = CX88_VMUX_COMPOSITE1, | ||
791 | .vmux = 1, | ||
792 | .gpio0 = 0x0700, | ||
793 | .gpio2 = 0x0101, | ||
794 | },{ | ||
795 | .type = CX88_VMUX_SVIDEO, | ||
796 | .vmux = 2, | ||
797 | .gpio0 = 0x0700, | ||
798 | .gpio2 = 0x0101, | ||
799 | }}, | ||
800 | .mpeg = CX88_MPEG_DVB, | ||
801 | }, | ||
802 | [CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1] = { | ||
803 | .name = "TerraTec Cinergy 1400 DVB-T", | ||
804 | .tuner_type = TUNER_ABSENT, | ||
805 | .input = {{ | ||
806 | .type = CX88_VMUX_DVB, | ||
807 | .vmux = 0, | ||
808 | },{ | ||
809 | .type = CX88_VMUX_COMPOSITE1, | ||
810 | .vmux = 2, | ||
811 | },{ | ||
812 | .type = CX88_VMUX_SVIDEO, | ||
813 | .vmux = 2, | ||
814 | }}, | ||
815 | .mpeg = CX88_MPEG_DVB, | ||
816 | }, | ||
817 | [CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD] = { | ||
818 | .name = "DViCO FusionHDTV 5 Gold", | ||
819 | .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H062F */ | ||
820 | .radio_type = UNSET, | ||
821 | .tuner_addr = ADDR_UNSET, | ||
822 | .radio_addr = ADDR_UNSET, | ||
823 | .tda9887_conf = TDA9887_PRESENT, | ||
824 | .input = {{ | ||
825 | .type = CX88_VMUX_TELEVISION, | ||
826 | .vmux = 0, | ||
827 | .gpio0 = 0x87fd, | ||
828 | },{ | ||
829 | .type = CX88_VMUX_COMPOSITE1, | ||
830 | .vmux = 1, | ||
831 | .gpio0 = 0x87f9, | ||
832 | },{ | ||
833 | .type = CX88_VMUX_SVIDEO, | ||
834 | .vmux = 2, | ||
835 | .gpio0 = 0x87f9, | ||
836 | }}, | ||
837 | .mpeg = CX88_MPEG_DVB, | ||
838 | }, | ||
839 | [CX88_BOARD_AVERMEDIA_ULTRATV_MC_550] = { | ||
840 | .name = "AverMedia UltraTV Media Center PCI 550", | ||
841 | .tuner_type = TUNER_PHILIPS_FM1236_MK3, | ||
842 | .radio_type = UNSET, | ||
843 | .tuner_addr = ADDR_UNSET, | ||
844 | .radio_addr = ADDR_UNSET, | ||
845 | .tda9887_conf = TDA9887_PRESENT, | ||
846 | .input = {{ | ||
847 | .type = CX88_VMUX_COMPOSITE1, | ||
848 | .vmux = 0, | ||
849 | .gpio0 = 0x0000cd73, | ||
850 | .audioroute = 1, | ||
851 | },{ | ||
852 | .type = CX88_VMUX_SVIDEO, | ||
853 | .vmux = 1, | ||
854 | .gpio0 = 0x0000cd73, | ||
855 | .audioroute = 1, | ||
856 | },{ | ||
857 | .type = CX88_VMUX_TELEVISION, | ||
858 | .vmux = 3, | ||
859 | .gpio0 = 0x0000cdb3, | ||
860 | .audioroute = 1, | ||
861 | }}, | ||
862 | .radio = { | ||
863 | .type = CX88_RADIO, | ||
864 | .vmux = 2, | ||
865 | .gpio0 = 0x0000cdf3, | ||
866 | .audioroute = 1, | ||
867 | }, | ||
868 | .mpeg = CX88_MPEG_BLACKBIRD, | ||
869 | }, | ||
870 | [CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD] = { | ||
871 | /* Alexander Wold <awold@bigfoot.com> */ | ||
872 | .name = "Kworld V-Stream Xpert DVD", | ||
873 | .tuner_type = UNSET, | ||
874 | .input = {{ | ||
875 | .type = CX88_VMUX_COMPOSITE1, | ||
876 | .vmux = 1, | ||
877 | .gpio0 = 0x03000000, | ||
878 | .gpio1 = 0x01000000, | ||
879 | .gpio2 = 0x02000000, | ||
880 | .gpio3 = 0x00100000, | ||
881 | },{ | ||
882 | .type = CX88_VMUX_SVIDEO, | ||
883 | .vmux = 2, | ||
884 | .gpio0 = 0x03000000, | ||
885 | .gpio1 = 0x01000000, | ||
886 | .gpio2 = 0x02000000, | ||
887 | .gpio3 = 0x00100000, | ||
888 | }}, | ||
889 | }, | ||
890 | [CX88_BOARD_ATI_HDTVWONDER] = { | ||
891 | .name = "ATI HDTV Wonder", | ||
892 | .tuner_type = TUNER_PHILIPS_TUV1236D, | ||
893 | .radio_type = UNSET, | ||
894 | .tuner_addr = ADDR_UNSET, | ||
895 | .radio_addr = ADDR_UNSET, | ||
896 | .input = {{ | ||
897 | .type = CX88_VMUX_TELEVISION, | ||
898 | .vmux = 0, | ||
899 | .gpio0 = 0x00000ff7, | ||
900 | .gpio1 = 0x000000ff, | ||
901 | .gpio2 = 0x00000001, | ||
902 | .gpio3 = 0x00000000, | ||
903 | },{ | ||
904 | .type = CX88_VMUX_COMPOSITE1, | ||
905 | .vmux = 1, | ||
906 | .gpio0 = 0x00000ffe, | ||
907 | .gpio1 = 0x000000ff, | ||
908 | .gpio2 = 0x00000001, | ||
909 | .gpio3 = 0x00000000, | ||
910 | },{ | ||
911 | .type = CX88_VMUX_SVIDEO, | ||
912 | .vmux = 2, | ||
913 | .gpio0 = 0x00000ffe, | ||
914 | .gpio1 = 0x000000ff, | ||
915 | .gpio2 = 0x00000001, | ||
916 | .gpio3 = 0x00000000, | ||
917 | }}, | ||
918 | .mpeg = CX88_MPEG_DVB, | ||
919 | }, | ||
920 | [CX88_BOARD_WINFAST_DTV1000] = { | ||
921 | .name = "WinFast DTV1000-T", | ||
922 | .tuner_type = TUNER_ABSENT, | ||
923 | .radio_type = UNSET, | ||
924 | .tuner_addr = ADDR_UNSET, | ||
925 | .radio_addr = ADDR_UNSET, | ||
926 | .input = {{ | ||
927 | .type = CX88_VMUX_DVB, | ||
928 | .vmux = 0, | ||
929 | },{ | ||
930 | .type = CX88_VMUX_COMPOSITE1, | ||
931 | .vmux = 1, | ||
932 | },{ | ||
933 | .type = CX88_VMUX_SVIDEO, | ||
934 | .vmux = 2, | ||
935 | }}, | ||
936 | .mpeg = CX88_MPEG_DVB, | ||
937 | }, | ||
938 | [CX88_BOARD_AVERTV_303] = { | ||
939 | .name = "AVerTV 303 (M126)", | ||
940 | .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, | ||
941 | .radio_type = UNSET, | ||
942 | .tuner_addr = ADDR_UNSET, | ||
943 | .radio_addr = ADDR_UNSET, | ||
944 | .tda9887_conf = TDA9887_PRESENT, | ||
945 | .input = {{ | ||
946 | .type = CX88_VMUX_TELEVISION, | ||
947 | .vmux = 0, | ||
948 | .gpio0 = 0x00ff, | ||
949 | .gpio1 = 0xe09f, | ||
950 | .gpio2 = 0x0010, | ||
951 | .gpio3 = 0x0000, | ||
952 | },{ | ||
953 | .type = CX88_VMUX_COMPOSITE1, | ||
954 | .vmux = 1, | ||
955 | .gpio0 = 0x00ff, | ||
956 | .gpio1 = 0xe05f, | ||
957 | .gpio2 = 0x0010, | ||
958 | .gpio3 = 0x0000, | ||
959 | },{ | ||
960 | .type = CX88_VMUX_SVIDEO, | ||
961 | .vmux = 2, | ||
962 | .gpio0 = 0x00ff, | ||
963 | .gpio1 = 0xe05f, | ||
964 | .gpio2 = 0x0010, | ||
965 | .gpio3 = 0x0000, | ||
966 | }}, | ||
967 | }, | ||
968 | [CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1] = { | ||
969 | .name = "Hauppauge Nova-S-Plus DVB-S", | ||
970 | .tuner_type = TUNER_ABSENT, | ||
971 | .radio_type = UNSET, | ||
972 | .tuner_addr = ADDR_UNSET, | ||
973 | .radio_addr = ADDR_UNSET, | ||
974 | .audio_chip = V4L2_IDENT_WM8775, | ||
975 | .i2sinputcntl = 2, | ||
976 | .input = {{ | ||
977 | .type = CX88_VMUX_DVB, | ||
978 | .vmux = 0, | ||
979 | /* 2: Line-In */ | ||
980 | .audioroute = 2, | ||
981 | },{ | ||
982 | .type = CX88_VMUX_COMPOSITE1, | ||
983 | .vmux = 1, | ||
984 | /* 2: Line-In */ | ||
985 | .audioroute = 2, | ||
986 | },{ | ||
987 | .type = CX88_VMUX_SVIDEO, | ||
988 | .vmux = 2, | ||
989 | /* 2: Line-In */ | ||
990 | .audioroute = 2, | ||
991 | }}, | ||
992 | .mpeg = CX88_MPEG_DVB, | ||
993 | }, | ||
994 | [CX88_BOARD_HAUPPAUGE_NOVASE2_S1] = { | ||
995 | .name = "Hauppauge Nova-SE2 DVB-S", | ||
996 | .tuner_type = TUNER_ABSENT, | ||
997 | .radio_type = UNSET, | ||
998 | .tuner_addr = ADDR_UNSET, | ||
999 | .radio_addr = ADDR_UNSET, | ||
1000 | .input = {{ | ||
1001 | .type = CX88_VMUX_DVB, | ||
1002 | .vmux = 0, | ||
1003 | }}, | ||
1004 | .mpeg = CX88_MPEG_DVB, | ||
1005 | }, | ||
1006 | [CX88_BOARD_KWORLD_DVBS_100] = { | ||
1007 | .name = "KWorld DVB-S 100", | ||
1008 | .tuner_type = TUNER_ABSENT, | ||
1009 | .radio_type = UNSET, | ||
1010 | .tuner_addr = ADDR_UNSET, | ||
1011 | .radio_addr = ADDR_UNSET, | ||
1012 | .audio_chip = V4L2_IDENT_WM8775, | ||
1013 | .input = {{ | ||
1014 | .type = CX88_VMUX_DVB, | ||
1015 | .vmux = 0, | ||
1016 | /* 2: Line-In */ | ||
1017 | .audioroute = 2, | ||
1018 | },{ | ||
1019 | .type = CX88_VMUX_COMPOSITE1, | ||
1020 | .vmux = 1, | ||
1021 | /* 2: Line-In */ | ||
1022 | .audioroute = 2, | ||
1023 | },{ | ||
1024 | .type = CX88_VMUX_SVIDEO, | ||
1025 | .vmux = 2, | ||
1026 | /* 2: Line-In */ | ||
1027 | .audioroute = 2, | ||
1028 | }}, | ||
1029 | .mpeg = CX88_MPEG_DVB, | ||
1030 | }, | ||
1031 | [CX88_BOARD_HAUPPAUGE_HVR1100] = { | ||
1032 | .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid", | ||
1033 | .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, | ||
1034 | .radio_type = UNSET, | ||
1035 | .tuner_addr = ADDR_UNSET, | ||
1036 | .radio_addr = ADDR_UNSET, | ||
1037 | .tda9887_conf = TDA9887_PRESENT, | ||
1038 | .input = {{ | ||
1039 | .type = CX88_VMUX_TELEVISION, | ||
1040 | .vmux = 0, | ||
1041 | },{ | ||
1042 | .type = CX88_VMUX_COMPOSITE1, | ||
1043 | .vmux = 1, | ||
1044 | },{ | ||
1045 | .type = CX88_VMUX_SVIDEO, | ||
1046 | .vmux = 2, | ||
1047 | }}, | ||
1048 | /* fixme: Add radio support */ | ||
1049 | .mpeg = CX88_MPEG_DVB, | ||
1050 | }, | ||
1051 | [CX88_BOARD_HAUPPAUGE_HVR1100LP] = { | ||
1052 | .name = "Hauppauge WinTV-HVR1100 DVB-T/Hybrid (Low Profile)", | ||
1053 | .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, | ||
1054 | .radio_type = UNSET, | ||
1055 | .tuner_addr = ADDR_UNSET, | ||
1056 | .radio_addr = ADDR_UNSET, | ||
1057 | .tda9887_conf = TDA9887_PRESENT, | ||
1058 | .input = {{ | ||
1059 | .type = CX88_VMUX_TELEVISION, | ||
1060 | .vmux = 0, | ||
1061 | },{ | ||
1062 | .type = CX88_VMUX_COMPOSITE1, | ||
1063 | .vmux = 1, | ||
1064 | }}, | ||
1065 | /* fixme: Add radio support */ | ||
1066 | .mpeg = CX88_MPEG_DVB, | ||
1067 | }, | ||
1068 | [CX88_BOARD_DNTV_LIVE_DVB_T_PRO] = { | ||
1069 | .name = "digitalnow DNTV Live! DVB-T Pro", | ||
1070 | .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, | ||
1071 | .radio_type = UNSET, | ||
1072 | .tuner_addr = ADDR_UNSET, | ||
1073 | .radio_addr = ADDR_UNSET, | ||
1074 | .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | | ||
1075 | TDA9887_PORT2_ACTIVE, | ||
1076 | .input = {{ | ||
1077 | .type = CX88_VMUX_TELEVISION, | ||
1078 | .vmux = 0, | ||
1079 | .gpio0 = 0xf80808, | ||
1080 | },{ | ||
1081 | .type = CX88_VMUX_COMPOSITE1, | ||
1082 | .vmux = 1, | ||
1083 | .gpio0 = 0xf80808, | ||
1084 | },{ | ||
1085 | .type = CX88_VMUX_SVIDEO, | ||
1086 | .vmux = 2, | ||
1087 | .gpio0 = 0xf80808, | ||
1088 | }}, | ||
1089 | .radio = { | ||
1090 | .type = CX88_RADIO, | ||
1091 | .gpio0 = 0xf80808, | ||
1092 | }, | ||
1093 | .mpeg = CX88_MPEG_DVB, | ||
1094 | }, | ||
1095 | [CX88_BOARD_KWORLD_DVB_T_CX22702] = { | ||
1096 | /* Kworld V-stream Xpert DVB-T with Thomson tuner */ | ||
1097 | /* DTT 7579 Conexant CX22702-19 Conexant CX2388x */ | ||
1098 | /* Manenti Marco <marco_manenti@colman.it> */ | ||
1099 | .name = "KWorld/VStream XPert DVB-T with cx22702", | ||
1100 | .tuner_type = TUNER_ABSENT, | ||
1101 | .radio_type = UNSET, | ||
1102 | .tuner_addr = ADDR_UNSET, | ||
1103 | .radio_addr = ADDR_UNSET, | ||
1104 | .input = {{ | ||
1105 | .type = CX88_VMUX_COMPOSITE1, | ||
1106 | .vmux = 1, | ||
1107 | .gpio0 = 0x0700, | ||
1108 | .gpio2 = 0x0101, | ||
1109 | },{ | ||
1110 | .type = CX88_VMUX_SVIDEO, | ||
1111 | .vmux = 2, | ||
1112 | .gpio0 = 0x0700, | ||
1113 | .gpio2 = 0x0101, | ||
1114 | }}, | ||
1115 | .mpeg = CX88_MPEG_DVB, | ||
1116 | }, | ||
1117 | [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL] = { | ||
1118 | .name = "DViCO FusionHDTV DVB-T Dual Digital", | ||
1119 | .tuner_type = TUNER_ABSENT, /* No analog tuner */ | ||
1120 | .radio_type = UNSET, | ||
1121 | .tuner_addr = ADDR_UNSET, | ||
1122 | .radio_addr = ADDR_UNSET, | ||
1123 | .input = {{ | ||
1124 | .type = CX88_VMUX_COMPOSITE1, | ||
1125 | .vmux = 1, | ||
1126 | .gpio0 = 0x000067df, | ||
1127 | },{ | ||
1128 | .type = CX88_VMUX_SVIDEO, | ||
1129 | .vmux = 2, | ||
1130 | .gpio0 = 0x000067df, | ||
1131 | }}, | ||
1132 | .mpeg = CX88_MPEG_DVB, | ||
1133 | }, | ||
1134 | [CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT] = { | ||
1135 | .name = "KWorld HardwareMpegTV XPert", | ||
1136 | .tuner_type = TUNER_PHILIPS_TDA8290, | ||
1137 | .radio_type = UNSET, | ||
1138 | .tuner_addr = ADDR_UNSET, | ||
1139 | .radio_addr = ADDR_UNSET, | ||
1140 | .input = {{ | ||
1141 | .type = CX88_VMUX_TELEVISION, | ||
1142 | .vmux = 0, | ||
1143 | .gpio0 = 0x3de2, | ||
1144 | .gpio2 = 0x00ff, | ||
1145 | },{ | ||
1146 | .type = CX88_VMUX_COMPOSITE1, | ||
1147 | .vmux = 1, | ||
1148 | .gpio0 = 0x3de6, | ||
1149 | .audioroute = 1, | ||
1150 | },{ | ||
1151 | .type = CX88_VMUX_SVIDEO, | ||
1152 | .vmux = 2, | ||
1153 | .gpio0 = 0x3de6, | ||
1154 | .audioroute = 1, | ||
1155 | }}, | ||
1156 | .radio = { | ||
1157 | .type = CX88_RADIO, | ||
1158 | .gpio0 = 0x3de6, | ||
1159 | .gpio2 = 0x00ff, | ||
1160 | }, | ||
1161 | .mpeg = CX88_MPEG_BLACKBIRD, | ||
1162 | }, | ||
1163 | [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID] = { | ||
1164 | .name = "DViCO FusionHDTV DVB-T Hybrid", | ||
1165 | .tuner_type = TUNER_THOMSON_FE6600, | ||
1166 | .radio_type = UNSET, | ||
1167 | .tuner_addr = ADDR_UNSET, | ||
1168 | .radio_addr = ADDR_UNSET, | ||
1169 | .input = {{ | ||
1170 | .type = CX88_VMUX_TELEVISION, | ||
1171 | .vmux = 0, | ||
1172 | .gpio0 = 0x0000a75f, | ||
1173 | },{ | ||
1174 | .type = CX88_VMUX_COMPOSITE1, | ||
1175 | .vmux = 1, | ||
1176 | .gpio0 = 0x0000a75b, | ||
1177 | },{ | ||
1178 | .type = CX88_VMUX_SVIDEO, | ||
1179 | .vmux = 2, | ||
1180 | .gpio0 = 0x0000a75b, | ||
1181 | }}, | ||
1182 | .mpeg = CX88_MPEG_DVB, | ||
1183 | }, | ||
1184 | [CX88_BOARD_PCHDTV_HD5500] = { | ||
1185 | .name = "pcHDTV HD5500 HDTV", | ||
1186 | .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */ | ||
1187 | .radio_type = UNSET, | ||
1188 | .tuner_addr = ADDR_UNSET, | ||
1189 | .radio_addr = ADDR_UNSET, | ||
1190 | .tda9887_conf = TDA9887_PRESENT, | ||
1191 | .input = {{ | ||
1192 | .type = CX88_VMUX_TELEVISION, | ||
1193 | .vmux = 0, | ||
1194 | .gpio0 = 0x87fd, | ||
1195 | },{ | ||
1196 | .type = CX88_VMUX_COMPOSITE1, | ||
1197 | .vmux = 1, | ||
1198 | .gpio0 = 0x87f9, | ||
1199 | },{ | ||
1200 | .type = CX88_VMUX_SVIDEO, | ||
1201 | .vmux = 2, | ||
1202 | .gpio0 = 0x87f9, | ||
1203 | }}, | ||
1204 | .mpeg = CX88_MPEG_DVB, | ||
1205 | }, | ||
1206 | [CX88_BOARD_KWORLD_MCE200_DELUXE] = { | ||
1207 | /* FIXME: tested TV input only, disabled composite, | ||
1208 | svideo and radio until they can be tested also. */ | ||
1209 | .name = "Kworld MCE 200 Deluxe", | ||
1210 | .tuner_type = TUNER_TENA_9533_DI, | ||
1211 | .radio_type = UNSET, | ||
1212 | .tda9887_conf = TDA9887_PRESENT, | ||
1213 | .tuner_addr = ADDR_UNSET, | ||
1214 | .radio_addr = ADDR_UNSET, | ||
1215 | .input = {{ | ||
1216 | .type = CX88_VMUX_TELEVISION, | ||
1217 | .vmux = 0, | ||
1218 | .gpio0 = 0x0000BDE6 | ||
1219 | }}, | ||
1220 | .mpeg = CX88_MPEG_BLACKBIRD, | ||
1221 | }, | ||
1222 | [CX88_BOARD_PIXELVIEW_PLAYTV_P7000] = { | ||
1223 | /* FIXME: SVideo, Composite and FM inputs are untested */ | ||
1224 | .name = "PixelView PlayTV P7000", | ||
1225 | .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, | ||
1226 | .radio_type = UNSET, | ||
1227 | .tuner_addr = ADDR_UNSET, | ||
1228 | .radio_addr = ADDR_UNSET, | ||
1229 | .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE | | ||
1230 | TDA9887_PORT2_ACTIVE, | ||
1231 | .input = {{ | ||
1232 | .type = CX88_VMUX_TELEVISION, | ||
1233 | .vmux = 0, | ||
1234 | .gpio0 = 0x5da6, | ||
1235 | }}, | ||
1236 | .mpeg = CX88_MPEG_BLACKBIRD, | ||
1237 | }, | ||
1238 | [CX88_BOARD_NPGTECH_REALTV_TOP10FM] = { | ||
1239 | .name = "NPG Tech Real TV FM Top 10", | ||
1240 | .tuner_type = TUNER_TNF_5335MF, /* Actually a TNF9535 */ | ||
1241 | .radio_type = UNSET, | ||
1242 | .tuner_addr = ADDR_UNSET, | ||
1243 | .radio_addr = ADDR_UNSET, | ||
1244 | .input = {{ | ||
1245 | .type = CX88_VMUX_TELEVISION, | ||
1246 | .vmux = 0, | ||
1247 | .gpio0 = 0x0788, | ||
1248 | },{ | ||
1249 | .type = CX88_VMUX_COMPOSITE1, | ||
1250 | .vmux = 1, | ||
1251 | .gpio0 = 0x078b, | ||
1252 | },{ | ||
1253 | .type = CX88_VMUX_SVIDEO, | ||
1254 | .vmux = 2, | ||
1255 | .gpio0 = 0x078b, | ||
1256 | }}, | ||
1257 | .radio = { | ||
1258 | .type = CX88_RADIO, | ||
1259 | .gpio0 = 0x074a, | ||
1260 | }, | ||
1261 | }, | ||
1262 | [CX88_BOARD_WINFAST_DTV2000H] = { | ||
1263 | .name = "WinFast DTV2000 H", | ||
1264 | .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, | ||
1265 | .radio_type = UNSET, | ||
1266 | .tuner_addr = ADDR_UNSET, | ||
1267 | .radio_addr = ADDR_UNSET, | ||
1268 | .tda9887_conf = TDA9887_PRESENT, | ||
1269 | .input = {{ | ||
1270 | .type = CX88_VMUX_TELEVISION, | ||
1271 | .vmux = 0, | ||
1272 | .gpio0 = 0x00017304, | ||
1273 | .gpio1 = 0x00008203, | ||
1274 | .gpio2 = 0x00017304, | ||
1275 | .gpio3 = 0x02000000, | ||
1276 | }, { | ||
1277 | .type = CX88_VMUX_COMPOSITE1, | ||
1278 | .vmux = 1, | ||
1279 | .gpio0 = 0x0001d701, | ||
1280 | .gpio1 = 0x0000b207, | ||
1281 | .gpio2 = 0x0001d701, | ||
1282 | .gpio3 = 0x02000000, | ||
1283 | }, { | ||
1284 | .type = CX88_VMUX_COMPOSITE2, | ||
1285 | .vmux = 2, | ||
1286 | .gpio0 = 0x0001d503, | ||
1287 | .gpio1 = 0x0000b207, | ||
1288 | .gpio2 = 0x0001d503, | ||
1289 | .gpio3 = 0x02000000, | ||
1290 | }, { | ||
1291 | .type = CX88_VMUX_SVIDEO, | ||
1292 | .vmux = 3, | ||
1293 | .gpio0 = 0x0001d701, | ||
1294 | .gpio1 = 0x0000b207, | ||
1295 | .gpio2 = 0x0001d701, | ||
1296 | .gpio3 = 0x02000000, | ||
1297 | }}, | ||
1298 | .radio = { | ||
1299 | .type = CX88_RADIO, | ||
1300 | .gpio0 = 0x00015702, | ||
1301 | .gpio1 = 0x0000f207, | ||
1302 | .gpio2 = 0x00015702, | ||
1303 | .gpio3 = 0x02000000, | ||
1304 | }, | ||
1305 | .mpeg = CX88_MPEG_DVB, | ||
1306 | }, | ||
1307 | [CX88_BOARD_WINFAST_DTV2000H_J] = { | ||
1308 | .name = "WinFast DTV2000 H rev. J", | ||
1309 | .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, | ||
1310 | .radio_type = UNSET, | ||
1311 | .tuner_addr = ADDR_UNSET, | ||
1312 | .radio_addr = ADDR_UNSET, | ||
1313 | .tda9887_conf = TDA9887_PRESENT, | ||
1314 | .input = {{ | ||
1315 | .type = CX88_VMUX_TELEVISION, | ||
1316 | .vmux = 0, | ||
1317 | .gpio0 = 0x00017300, | ||
1318 | .gpio1 = 0x00008207, | ||
1319 | .gpio2 = 0x00000000, | ||
1320 | .gpio3 = 0x02000000, | ||
1321 | },{ | ||
1322 | .type = CX88_VMUX_TELEVISION, | ||
1323 | .vmux = 0, | ||
1324 | .gpio0 = 0x00018300, | ||
1325 | .gpio1 = 0x0000f207, | ||
1326 | .gpio2 = 0x00017304, | ||
1327 | .gpio3 = 0x02000000, | ||
1328 | },{ | ||
1329 | .type = CX88_VMUX_COMPOSITE1, | ||
1330 | .vmux = 1, | ||
1331 | .gpio0 = 0x00018301, | ||
1332 | .gpio1 = 0x0000f207, | ||
1333 | .gpio2 = 0x00017304, | ||
1334 | .gpio3 = 0x02000000, | ||
1335 | },{ | ||
1336 | .type = CX88_VMUX_SVIDEO, | ||
1337 | .vmux = 2, | ||
1338 | .gpio0 = 0x00018301, | ||
1339 | .gpio1 = 0x0000f207, | ||
1340 | .gpio2 = 0x00017304, | ||
1341 | .gpio3 = 0x02000000, | ||
1342 | }}, | ||
1343 | .radio = { | ||
1344 | .type = CX88_RADIO, | ||
1345 | .gpio0 = 0x00015702, | ||
1346 | .gpio1 = 0x0000f207, | ||
1347 | .gpio2 = 0x00015702, | ||
1348 | .gpio3 = 0x02000000, | ||
1349 | }, | ||
1350 | .mpeg = CX88_MPEG_DVB, | ||
1351 | }, | ||
1352 | [CX88_BOARD_GENIATECH_DVBS] = { | ||
1353 | .name = "Geniatech DVB-S", | ||
1354 | .tuner_type = TUNER_ABSENT, | ||
1355 | .radio_type = UNSET, | ||
1356 | .tuner_addr = ADDR_UNSET, | ||
1357 | .radio_addr = ADDR_UNSET, | ||
1358 | .input = {{ | ||
1359 | .type = CX88_VMUX_DVB, | ||
1360 | .vmux = 0, | ||
1361 | },{ | ||
1362 | .type = CX88_VMUX_COMPOSITE1, | ||
1363 | .vmux = 1, | ||
1364 | }}, | ||
1365 | .mpeg = CX88_MPEG_DVB, | ||
1366 | }, | ||
1367 | [CX88_BOARD_HAUPPAUGE_HVR3000] = { | ||
1368 | .name = "Hauppauge WinTV-HVR3000 TriMode Analog/DVB-S/DVB-T", | ||
1369 | .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, | ||
1370 | .radio_type = UNSET, | ||
1371 | .tuner_addr = ADDR_UNSET, | ||
1372 | .radio_addr = ADDR_UNSET, | ||
1373 | .tda9887_conf = TDA9887_PRESENT, | ||
1374 | .audio_chip = V4L2_IDENT_WM8775, | ||
1375 | .input = {{ | ||
1376 | .type = CX88_VMUX_TELEVISION, | ||
1377 | .vmux = 0, | ||
1378 | .gpio0 = 0x84bf, | ||
1379 | /* 1: TV Audio / FM Mono */ | ||
1380 | .audioroute = 1, | ||
1381 | },{ | ||
1382 | .type = CX88_VMUX_COMPOSITE1, | ||
1383 | .vmux = 1, | ||
1384 | .gpio0 = 0x84bf, | ||
1385 | /* 2: Line-In */ | ||
1386 | .audioroute = 2, | ||
1387 | },{ | ||
1388 | .type = CX88_VMUX_SVIDEO, | ||
1389 | .vmux = 2, | ||
1390 | .gpio0 = 0x84bf, | ||
1391 | /* 2: Line-In */ | ||
1392 | .audioroute = 2, | ||
1393 | }}, | ||
1394 | .radio = { | ||
1395 | .type = CX88_RADIO, | ||
1396 | .gpio0 = 0x84bf, | ||
1397 | /* 4: FM Stereo (untested) */ | ||
1398 | .audioroute = 8, | ||
1399 | }, | ||
1400 | .mpeg = CX88_MPEG_DVB, | ||
1401 | .num_frontends = 2, | ||
1402 | }, | ||
1403 | [CX88_BOARD_NORWOOD_MICRO] = { | ||
1404 | .name = "Norwood Micro TV Tuner", | ||
1405 | .tuner_type = TUNER_TNF_5335MF, | ||
1406 | .radio_type = UNSET, | ||
1407 | .tuner_addr = ADDR_UNSET, | ||
1408 | .radio_addr = ADDR_UNSET, | ||
1409 | .input = {{ | ||
1410 | .type = CX88_VMUX_TELEVISION, | ||
1411 | .vmux = 0, | ||
1412 | .gpio0 = 0x0709, | ||
1413 | },{ | ||
1414 | .type = CX88_VMUX_COMPOSITE1, | ||
1415 | .vmux = 1, | ||
1416 | .gpio0 = 0x070b, | ||
1417 | },{ | ||
1418 | .type = CX88_VMUX_SVIDEO, | ||
1419 | .vmux = 2, | ||
1420 | .gpio0 = 0x070b, | ||
1421 | }}, | ||
1422 | }, | ||
1423 | [CX88_BOARD_TE_DTV_250_OEM_SWANN] = { | ||
1424 | .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM", | ||
1425 | .tuner_type = TUNER_LG_PAL_NEW_TAPC, | ||
1426 | .radio_type = UNSET, | ||
1427 | .tuner_addr = ADDR_UNSET, | ||
1428 | .radio_addr = ADDR_UNSET, | ||
1429 | .input = {{ | ||
1430 | .type = CX88_VMUX_TELEVISION, | ||
1431 | .vmux = 0, | ||
1432 | .gpio0 = 0x003fffff, | ||
1433 | .gpio1 = 0x00e00000, | ||
1434 | .gpio2 = 0x003fffff, | ||
1435 | .gpio3 = 0x02000000, | ||
1436 | },{ | ||
1437 | .type = CX88_VMUX_COMPOSITE1, | ||
1438 | .vmux = 1, | ||
1439 | .gpio0 = 0x003fffff, | ||
1440 | .gpio1 = 0x00e00000, | ||
1441 | .gpio2 = 0x003fffff, | ||
1442 | .gpio3 = 0x02000000, | ||
1443 | },{ | ||
1444 | .type = CX88_VMUX_SVIDEO, | ||
1445 | .vmux = 2, | ||
1446 | .gpio0 = 0x003fffff, | ||
1447 | .gpio1 = 0x00e00000, | ||
1448 | .gpio2 = 0x003fffff, | ||
1449 | .gpio3 = 0x02000000, | ||
1450 | }}, | ||
1451 | }, | ||
1452 | [CX88_BOARD_HAUPPAUGE_HVR1300] = { | ||
1453 | .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder", | ||
1454 | .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, | ||
1455 | .radio_type = UNSET, | ||
1456 | .tuner_addr = ADDR_UNSET, | ||
1457 | .radio_addr = ADDR_UNSET, | ||
1458 | .tda9887_conf = TDA9887_PRESENT, | ||
1459 | .audio_chip = V4L2_IDENT_WM8775, | ||
1460 | /* | ||
1461 | * gpio0 as reported by Mike Crash <mike AT mikecrash.com> | ||
1462 | */ | ||
1463 | .input = {{ | ||
1464 | .type = CX88_VMUX_TELEVISION, | ||
1465 | .vmux = 0, | ||
1466 | .gpio0 = 0xef88, | ||
1467 | /* 1: TV Audio / FM Mono */ | ||
1468 | .audioroute = 1, | ||
1469 | },{ | ||
1470 | .type = CX88_VMUX_COMPOSITE1, | ||
1471 | .vmux = 1, | ||
1472 | .gpio0 = 0xef88, | ||
1473 | /* 2: Line-In */ | ||
1474 | .audioroute = 2, | ||
1475 | },{ | ||
1476 | .type = CX88_VMUX_SVIDEO, | ||
1477 | .vmux = 2, | ||
1478 | .gpio0 = 0xef88, | ||
1479 | /* 2: Line-In */ | ||
1480 | .audioroute = 2, | ||
1481 | }}, | ||
1482 | .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, | ||
1483 | .radio = { | ||
1484 | .type = CX88_RADIO, | ||
1485 | .gpio0 = 0xef88, | ||
1486 | /* 4: FM Stereo (untested) */ | ||
1487 | .audioroute = 8, | ||
1488 | }, | ||
1489 | }, | ||
1490 | [CX88_BOARD_SAMSUNG_SMT_7020] = { | ||
1491 | .name = "Samsung SMT 7020 DVB-S", | ||
1492 | .tuner_type = TUNER_ABSENT, | ||
1493 | .radio_type = UNSET, | ||
1494 | .tuner_addr = ADDR_UNSET, | ||
1495 | .radio_addr = ADDR_UNSET, | ||
1496 | .input = { { | ||
1497 | .type = CX88_VMUX_DVB, | ||
1498 | .vmux = 0, | ||
1499 | } }, | ||
1500 | .mpeg = CX88_MPEG_DVB, | ||
1501 | }, | ||
1502 | [CX88_BOARD_ADSTECH_PTV_390] = { | ||
1503 | .name = "ADS Tech Instant Video PCI", | ||
1504 | .tuner_type = TUNER_ABSENT, | ||
1505 | .radio_type = UNSET, | ||
1506 | .tuner_addr = ADDR_UNSET, | ||
1507 | .radio_addr = ADDR_UNSET, | ||
1508 | .input = {{ | ||
1509 | .type = CX88_VMUX_DEBUG, | ||
1510 | .vmux = 3, | ||
1511 | .gpio0 = 0x04ff, | ||
1512 | },{ | ||
1513 | .type = CX88_VMUX_COMPOSITE1, | ||
1514 | .vmux = 1, | ||
1515 | .gpio0 = 0x07fa, | ||
1516 | },{ | ||
1517 | .type = CX88_VMUX_SVIDEO, | ||
1518 | .vmux = 2, | ||
1519 | .gpio0 = 0x07fa, | ||
1520 | }}, | ||
1521 | }, | ||
1522 | [CX88_BOARD_PINNACLE_PCTV_HD_800i] = { | ||
1523 | .name = "Pinnacle PCTV HD 800i", | ||
1524 | .tuner_type = TUNER_XC5000, | ||
1525 | .radio_type = UNSET, | ||
1526 | .tuner_addr = ADDR_UNSET, | ||
1527 | .radio_addr = ADDR_UNSET, | ||
1528 | .input = {{ | ||
1529 | .type = CX88_VMUX_TELEVISION, | ||
1530 | .vmux = 0, | ||
1531 | .gpio0 = 0x04fb, | ||
1532 | .gpio1 = 0x10ff, | ||
1533 | },{ | ||
1534 | .type = CX88_VMUX_COMPOSITE1, | ||
1535 | .vmux = 1, | ||
1536 | .gpio0 = 0x04fb, | ||
1537 | .gpio1 = 0x10ef, | ||
1538 | .audioroute = 1, | ||
1539 | },{ | ||
1540 | .type = CX88_VMUX_SVIDEO, | ||
1541 | .vmux = 2, | ||
1542 | .gpio0 = 0x04fb, | ||
1543 | .gpio1 = 0x10ef, | ||
1544 | .audioroute = 1, | ||
1545 | }}, | ||
1546 | .mpeg = CX88_MPEG_DVB, | ||
1547 | }, | ||
1548 | [CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO] = { | ||
1549 | .name = "DViCO FusionHDTV 5 PCI nano", | ||
1550 | /* xc3008 tuner, digital only for now */ | ||
1551 | .tuner_type = TUNER_ABSENT, | ||
1552 | .radio_type = UNSET, | ||
1553 | .tuner_addr = ADDR_UNSET, | ||
1554 | .radio_addr = ADDR_UNSET, | ||
1555 | .input = {{ | ||
1556 | .type = CX88_VMUX_TELEVISION, | ||
1557 | .vmux = 0, | ||
1558 | .gpio0 = 0x000027df, /* Unconfirmed */ | ||
1559 | }, { | ||
1560 | .type = CX88_VMUX_COMPOSITE1, | ||
1561 | .vmux = 1, | ||
1562 | .gpio0 = 0x000027df, /* Unconfirmed */ | ||
1563 | .audioroute = 1, | ||
1564 | }, { | ||
1565 | .type = CX88_VMUX_SVIDEO, | ||
1566 | .vmux = 2, | ||
1567 | .gpio0 = 0x000027df, /* Unconfirmed */ | ||
1568 | .audioroute = 1, | ||
1569 | } }, | ||
1570 | .mpeg = CX88_MPEG_DVB, | ||
1571 | }, | ||
1572 | [CX88_BOARD_PINNACLE_HYBRID_PCTV] = { | ||
1573 | .name = "Pinnacle Hybrid PCTV", | ||
1574 | .tuner_type = TUNER_XC2028, | ||
1575 | .tuner_addr = 0x61, | ||
1576 | .radio_type = TUNER_XC2028, | ||
1577 | .radio_addr = 0x61, | ||
1578 | .input = { { | ||
1579 | .type = CX88_VMUX_TELEVISION, | ||
1580 | .vmux = 0, | ||
1581 | .gpio0 = 0x004ff, | ||
1582 | .gpio1 = 0x010ff, | ||
1583 | .gpio2 = 0x00001, | ||
1584 | }, { | ||
1585 | .type = CX88_VMUX_COMPOSITE1, | ||
1586 | .vmux = 1, | ||
1587 | .gpio0 = 0x004fb, | ||
1588 | .gpio1 = 0x010ef, | ||
1589 | .audioroute = 1, | ||
1590 | }, { | ||
1591 | .type = CX88_VMUX_SVIDEO, | ||
1592 | .vmux = 2, | ||
1593 | .gpio0 = 0x004fb, | ||
1594 | .gpio1 = 0x010ef, | ||
1595 | .audioroute = 1, | ||
1596 | } }, | ||
1597 | .radio = { | ||
1598 | .type = CX88_RADIO, | ||
1599 | .gpio0 = 0x004ff, | ||
1600 | .gpio1 = 0x010ff, | ||
1601 | .gpio2 = 0x0ff, | ||
1602 | }, | ||
1603 | .mpeg = CX88_MPEG_DVB, | ||
1604 | }, | ||
1605 | /* Terry Wu <terrywu2009@gmail.com> */ | ||
1606 | /* TV Audio : set GPIO 2, 18, 19 value to 0, 1, 0 */ | ||
1607 | /* FM Audio : set GPIO 2, 18, 19 value to 0, 0, 0 */ | ||
1608 | /* Line-in Audio : set GPIO 2, 18, 19 value to 0, 1, 1 */ | ||
1609 | /* Mute Audio : set GPIO 2 value to 1 */ | ||
1610 | [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = { | ||
1611 | .name = "Leadtek TV2000 XP Global", | ||
1612 | .tuner_type = TUNER_XC2028, | ||
1613 | .tuner_addr = 0x61, | ||
1614 | .radio_type = TUNER_XC2028, | ||
1615 | .radio_addr = 0x61, | ||
1616 | .input = { { | ||
1617 | .type = CX88_VMUX_TELEVISION, | ||
1618 | .vmux = 0, | ||
1619 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
1620 | .gpio1 = 0x0000, | ||
1621 | .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */ | ||
1622 | .gpio3 = 0x0000, | ||
1623 | }, { | ||
1624 | .type = CX88_VMUX_COMPOSITE1, | ||
1625 | .vmux = 1, | ||
1626 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
1627 | .gpio1 = 0x0000, | ||
1628 | .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */ | ||
1629 | .gpio3 = 0x0000, | ||
1630 | }, { | ||
1631 | .type = CX88_VMUX_SVIDEO, | ||
1632 | .vmux = 2, | ||
1633 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
1634 | .gpio1 = 0x0000, | ||
1635 | .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */ | ||
1636 | .gpio3 = 0x0000, | ||
1637 | } }, | ||
1638 | .radio = { | ||
1639 | .type = CX88_RADIO, | ||
1640 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
1641 | .gpio1 = 0x0000, | ||
1642 | .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */ | ||
1643 | .gpio3 = 0x0000, | ||
1644 | }, | ||
1645 | }, | ||
1646 | [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = { | ||
1647 | .name = "PowerColor RA330", /* Long names may confuse LIRC. */ | ||
1648 | .tuner_type = TUNER_XC2028, | ||
1649 | .tuner_addr = 0x61, | ||
1650 | .input = { { | ||
1651 | .type = CX88_VMUX_DEBUG, | ||
1652 | .vmux = 3, /* Due to the way the cx88 driver is written, */ | ||
1653 | .gpio0 = 0x00ff, /* there is no way to deactivate audio pass- */ | ||
1654 | .gpio1 = 0xf39d, /* through without this entry. Furthermore, if */ | ||
1655 | .gpio3 = 0x0000, /* the TV mux entry is first, you get audio */ | ||
1656 | }, { /* from the tuner on boot for a little while. */ | ||
1657 | .type = CX88_VMUX_TELEVISION, | ||
1658 | .vmux = 0, | ||
1659 | .gpio0 = 0x00ff, | ||
1660 | .gpio1 = 0xf35d, | ||
1661 | .gpio3 = 0x0000, | ||
1662 | }, { | ||
1663 | .type = CX88_VMUX_COMPOSITE1, | ||
1664 | .vmux = 1, | ||
1665 | .gpio0 = 0x00ff, | ||
1666 | .gpio1 = 0xf37d, | ||
1667 | .gpio3 = 0x0000, | ||
1668 | }, { | ||
1669 | .type = CX88_VMUX_SVIDEO, | ||
1670 | .vmux = 2, | ||
1671 | .gpio0 = 0x000ff, | ||
1672 | .gpio1 = 0x0f37d, | ||
1673 | .gpio3 = 0x00000, | ||
1674 | } }, | ||
1675 | .radio = { | ||
1676 | .type = CX88_RADIO, | ||
1677 | .gpio0 = 0x000ff, | ||
1678 | .gpio1 = 0x0f35d, | ||
1679 | .gpio3 = 0x00000, | ||
1680 | }, | ||
1681 | }, | ||
1682 | [CX88_BOARD_GENIATECH_X8000_MT] = { | ||
1683 | /* Also PowerColor Real Angel 330 and Geniatech X800 OEM */ | ||
1684 | .name = "Geniatech X8000-MT DVBT", | ||
1685 | .tuner_type = TUNER_XC2028, | ||
1686 | .tuner_addr = 0x61, | ||
1687 | .input = { { | ||
1688 | .type = CX88_VMUX_TELEVISION, | ||
1689 | .vmux = 0, | ||
1690 | .gpio0 = 0x00000000, | ||
1691 | .gpio1 = 0x00e3e341, | ||
1692 | .gpio2 = 0x00000000, | ||
1693 | .gpio3 = 0x00000000, | ||
1694 | }, { | ||
1695 | .type = CX88_VMUX_COMPOSITE1, | ||
1696 | .vmux = 1, | ||
1697 | .gpio0 = 0x00000000, | ||
1698 | .gpio1 = 0x00e3e361, | ||
1699 | .gpio2 = 0x00000000, | ||
1700 | .gpio3 = 0x00000000, | ||
1701 | }, { | ||
1702 | .type = CX88_VMUX_SVIDEO, | ||
1703 | .vmux = 2, | ||
1704 | .gpio0 = 0x00000000, | ||
1705 | .gpio1 = 0x00e3e361, | ||
1706 | .gpio2 = 0x00000000, | ||
1707 | .gpio3 = 0x00000000, | ||
1708 | } }, | ||
1709 | .radio = { | ||
1710 | .type = CX88_RADIO, | ||
1711 | .gpio0 = 0x00000000, | ||
1712 | .gpio1 = 0x00e3e341, | ||
1713 | .gpio2 = 0x00000000, | ||
1714 | .gpio3 = 0x00000000, | ||
1715 | }, | ||
1716 | .mpeg = CX88_MPEG_DVB, | ||
1717 | }, | ||
1718 | [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = { | ||
1719 | .name = "DViCO FusionHDTV DVB-T PRO", | ||
1720 | .tuner_type = TUNER_XC2028, | ||
1721 | .tuner_addr = 0x61, | ||
1722 | .radio_type = UNSET, | ||
1723 | .radio_addr = ADDR_UNSET, | ||
1724 | .input = { { | ||
1725 | .type = CX88_VMUX_COMPOSITE1, | ||
1726 | .vmux = 1, | ||
1727 | .gpio0 = 0x000067df, | ||
1728 | }, { | ||
1729 | .type = CX88_VMUX_SVIDEO, | ||
1730 | .vmux = 2, | ||
1731 | .gpio0 = 0x000067df, | ||
1732 | } }, | ||
1733 | .mpeg = CX88_MPEG_DVB, | ||
1734 | }, | ||
1735 | [CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD] = { | ||
1736 | .name = "DViCO FusionHDTV 7 Gold", | ||
1737 | .tuner_type = TUNER_XC5000, | ||
1738 | .radio_type = UNSET, | ||
1739 | .tuner_addr = ADDR_UNSET, | ||
1740 | .radio_addr = ADDR_UNSET, | ||
1741 | .input = {{ | ||
1742 | .type = CX88_VMUX_TELEVISION, | ||
1743 | .vmux = 0, | ||
1744 | .gpio0 = 0x10df, | ||
1745 | },{ | ||
1746 | .type = CX88_VMUX_COMPOSITE1, | ||
1747 | .vmux = 1, | ||
1748 | .gpio0 = 0x16d9, | ||
1749 | },{ | ||
1750 | .type = CX88_VMUX_SVIDEO, | ||
1751 | .vmux = 2, | ||
1752 | .gpio0 = 0x16d9, | ||
1753 | }}, | ||
1754 | .mpeg = CX88_MPEG_DVB, | ||
1755 | }, | ||
1756 | [CX88_BOARD_PROLINK_PV_8000GT] = { | ||
1757 | .name = "Prolink Pixelview MPEG 8000GT", | ||
1758 | .tuner_type = TUNER_XC2028, | ||
1759 | .tuner_addr = 0x61, | ||
1760 | .input = { { | ||
1761 | .type = CX88_VMUX_TELEVISION, | ||
1762 | .vmux = 0, | ||
1763 | .gpio0 = 0x0ff, | ||
1764 | .gpio2 = 0x0cfb, | ||
1765 | }, { | ||
1766 | .type = CX88_VMUX_COMPOSITE1, | ||
1767 | .vmux = 1, | ||
1768 | .gpio2 = 0x0cfb, | ||
1769 | }, { | ||
1770 | .type = CX88_VMUX_SVIDEO, | ||
1771 | .vmux = 2, | ||
1772 | .gpio2 = 0x0cfb, | ||
1773 | } }, | ||
1774 | .radio = { | ||
1775 | .type = CX88_RADIO, | ||
1776 | .gpio2 = 0x0cfb, | ||
1777 | }, | ||
1778 | }, | ||
1779 | [CX88_BOARD_PROLINK_PV_GLOBAL_XTREME] = { | ||
1780 | .name = "Prolink Pixelview Global Extreme", | ||
1781 | .tuner_type = TUNER_XC2028, | ||
1782 | .tuner_addr = 0x61, | ||
1783 | .input = { { | ||
1784 | .type = CX88_VMUX_TELEVISION, | ||
1785 | .vmux = 0, | ||
1786 | .gpio0 = 0x04fb, | ||
1787 | .gpio1 = 0x04080, | ||
1788 | .gpio2 = 0x0cf7, | ||
1789 | }, { | ||
1790 | .type = CX88_VMUX_COMPOSITE1, | ||
1791 | .vmux = 1, | ||
1792 | .gpio0 = 0x04fb, | ||
1793 | .gpio1 = 0x04080, | ||
1794 | .gpio2 = 0x0cfb, | ||
1795 | }, { | ||
1796 | .type = CX88_VMUX_SVIDEO, | ||
1797 | .vmux = 2, | ||
1798 | .gpio0 = 0x04fb, | ||
1799 | .gpio1 = 0x04080, | ||
1800 | .gpio2 = 0x0cfb, | ||
1801 | } }, | ||
1802 | .radio = { | ||
1803 | .type = CX88_RADIO, | ||
1804 | .gpio0 = 0x04ff, | ||
1805 | .gpio1 = 0x04080, | ||
1806 | .gpio2 = 0x0cf7, | ||
1807 | }, | ||
1808 | }, | ||
1809 | /* Both radio, analog and ATSC work with this board. | ||
1810 | However, for analog to work, s5h1409 gate should be open, | ||
1811 | otherwise, tuner-xc3028 won't be detected. | ||
1812 | A proper fix require using the newer i2c methods to add | ||
1813 | tuner-xc3028 without doing an i2c probe. | ||
1814 | */ | ||
1815 | [CX88_BOARD_KWORLD_ATSC_120] = { | ||
1816 | .name = "Kworld PlusTV HD PCI 120 (ATSC 120)", | ||
1817 | .tuner_type = TUNER_XC2028, | ||
1818 | .radio_type = UNSET, | ||
1819 | .tuner_addr = ADDR_UNSET, | ||
1820 | .radio_addr = ADDR_UNSET, | ||
1821 | .input = { { | ||
1822 | .type = CX88_VMUX_TELEVISION, | ||
1823 | .vmux = 0, | ||
1824 | .gpio0 = 0x000000ff, | ||
1825 | .gpio1 = 0x0000f35d, | ||
1826 | .gpio2 = 0x00000000, | ||
1827 | }, { | ||
1828 | .type = CX88_VMUX_COMPOSITE1, | ||
1829 | .vmux = 1, | ||
1830 | .gpio0 = 0x000000ff, | ||
1831 | .gpio1 = 0x0000f37e, | ||
1832 | .gpio2 = 0x00000000, | ||
1833 | }, { | ||
1834 | .type = CX88_VMUX_SVIDEO, | ||
1835 | .vmux = 2, | ||
1836 | .gpio0 = 0x000000ff, | ||
1837 | .gpio1 = 0x0000f37e, | ||
1838 | .gpio2 = 0x00000000, | ||
1839 | } }, | ||
1840 | .radio = { | ||
1841 | .type = CX88_RADIO, | ||
1842 | .gpio0 = 0x000000ff, | ||
1843 | .gpio1 = 0x0000f35d, | ||
1844 | .gpio2 = 0x00000000, | ||
1845 | }, | ||
1846 | .mpeg = CX88_MPEG_DVB, | ||
1847 | }, | ||
1848 | [CX88_BOARD_HAUPPAUGE_HVR4000] = { | ||
1849 | .name = "Hauppauge WinTV-HVR4000 DVB-S/S2/T/Hybrid", | ||
1850 | .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, | ||
1851 | .radio_type = UNSET, | ||
1852 | .tuner_addr = ADDR_UNSET, | ||
1853 | .radio_addr = ADDR_UNSET, | ||
1854 | .tda9887_conf = TDA9887_PRESENT, | ||
1855 | .audio_chip = V4L2_IDENT_WM8775, | ||
1856 | /* | ||
1857 | * GPIO0 (WINTV2000) | ||
1858 | * | ||
1859 | * Analogue SAT DVB-T | ||
1860 | * Antenna 0xc4bf 0xc4bb | ||
1861 | * Composite 0xc4bf 0xc4bb | ||
1862 | * S-Video 0xc4bf 0xc4bb | ||
1863 | * Composite1 0xc4ff 0xc4fb | ||
1864 | * S-Video1 0xc4ff 0xc4fb | ||
1865 | * | ||
1866 | * BIT VALUE FUNCTION GP{x}_IO | ||
1867 | * 0 1 I:? | ||
1868 | * 1 1 I:? | ||
1869 | * 2 1 O:MPEG PORT 0=DVB-T 1=DVB-S | ||
1870 | * 3 1 I:? | ||
1871 | * 4 1 I:? | ||
1872 | * 5 1 I:? | ||
1873 | * 6 0 O:INPUT SELECTOR 0=INTERNAL 1=EXPANSION | ||
1874 | * 7 1 O:DVB-T DEMOD RESET LOW | ||
1875 | * | ||
1876 | * BIT VALUE FUNCTION GP{x}_OE | ||
1877 | * 8 0 I | ||
1878 | * 9 0 I | ||
1879 | * a 1 O | ||
1880 | * b 0 I | ||
1881 | * c 0 I | ||
1882 | * d 0 I | ||
1883 | * e 1 O | ||
1884 | * f 1 O | ||
1885 | * | ||
1886 | * WM8775 ADC | ||
1887 | * | ||
1888 | * 1: TV Audio / FM Mono | ||
1889 | * 2: Line-In | ||
1890 | * 3: Line-In Expansion | ||
1891 | * 4: FM Stereo | ||
1892 | */ | ||
1893 | .input = {{ | ||
1894 | .type = CX88_VMUX_TELEVISION, | ||
1895 | .vmux = 0, | ||
1896 | .gpio0 = 0xc4bf, | ||
1897 | /* 1: TV Audio / FM Mono */ | ||
1898 | .audioroute = 1, | ||
1899 | }, { | ||
1900 | .type = CX88_VMUX_COMPOSITE1, | ||
1901 | .vmux = 1, | ||
1902 | .gpio0 = 0xc4bf, | ||
1903 | /* 2: Line-In */ | ||
1904 | .audioroute = 2, | ||
1905 | }, { | ||
1906 | .type = CX88_VMUX_SVIDEO, | ||
1907 | .vmux = 2, | ||
1908 | .gpio0 = 0xc4bf, | ||
1909 | /* 2: Line-In */ | ||
1910 | .audioroute = 2, | ||
1911 | } }, | ||
1912 | .radio = { | ||
1913 | .type = CX88_RADIO, | ||
1914 | .gpio0 = 0xc4bf, | ||
1915 | /* 4: FM Stereo */ | ||
1916 | .audioroute = 8, | ||
1917 | }, | ||
1918 | .mpeg = CX88_MPEG_DVB, | ||
1919 | .num_frontends = 2, | ||
1920 | }, | ||
1921 | [CX88_BOARD_HAUPPAUGE_HVR4000LITE] = { | ||
1922 | .name = "Hauppauge WinTV-HVR4000(Lite) DVB-S/S2", | ||
1923 | .tuner_type = UNSET, | ||
1924 | .radio_type = UNSET, | ||
1925 | .tuner_addr = ADDR_UNSET, | ||
1926 | .radio_addr = ADDR_UNSET, | ||
1927 | .input = {{ | ||
1928 | .type = CX88_VMUX_DVB, | ||
1929 | .vmux = 0, | ||
1930 | } }, | ||
1931 | .mpeg = CX88_MPEG_DVB, | ||
1932 | }, | ||
1933 | [CX88_BOARD_TEVII_S420] = { | ||
1934 | .name = "TeVii S420 DVB-S", | ||
1935 | .tuner_type = UNSET, | ||
1936 | .radio_type = UNSET, | ||
1937 | .tuner_addr = ADDR_UNSET, | ||
1938 | .radio_addr = ADDR_UNSET, | ||
1939 | .input = {{ | ||
1940 | .type = CX88_VMUX_DVB, | ||
1941 | .vmux = 0, | ||
1942 | } }, | ||
1943 | .mpeg = CX88_MPEG_DVB, | ||
1944 | }, | ||
1945 | [CX88_BOARD_TEVII_S460] = { | ||
1946 | .name = "TeVii S460 DVB-S/S2", | ||
1947 | .tuner_type = UNSET, | ||
1948 | .radio_type = UNSET, | ||
1949 | .tuner_addr = ADDR_UNSET, | ||
1950 | .radio_addr = ADDR_UNSET, | ||
1951 | .input = {{ | ||
1952 | .type = CX88_VMUX_DVB, | ||
1953 | .vmux = 0, | ||
1954 | } }, | ||
1955 | .mpeg = CX88_MPEG_DVB, | ||
1956 | }, | ||
1957 | [CX88_BOARD_TEVII_S464] = { | ||
1958 | .name = "TeVii S464 DVB-S/S2", | ||
1959 | .tuner_type = UNSET, | ||
1960 | .radio_type = UNSET, | ||
1961 | .tuner_addr = ADDR_UNSET, | ||
1962 | .radio_addr = ADDR_UNSET, | ||
1963 | .input = {{ | ||
1964 | .type = CX88_VMUX_DVB, | ||
1965 | .vmux = 0, | ||
1966 | } }, | ||
1967 | .mpeg = CX88_MPEG_DVB, | ||
1968 | }, | ||
1969 | [CX88_BOARD_OMICOM_SS4_PCI] = { | ||
1970 | .name = "Omicom SS4 DVB-S/S2 PCI", | ||
1971 | .tuner_type = UNSET, | ||
1972 | .radio_type = UNSET, | ||
1973 | .tuner_addr = ADDR_UNSET, | ||
1974 | .radio_addr = ADDR_UNSET, | ||
1975 | .input = {{ | ||
1976 | .type = CX88_VMUX_DVB, | ||
1977 | .vmux = 0, | ||
1978 | } }, | ||
1979 | .mpeg = CX88_MPEG_DVB, | ||
1980 | }, | ||
1981 | [CX88_BOARD_TBS_8910] = { | ||
1982 | .name = "TBS 8910 DVB-S", | ||
1983 | .tuner_type = UNSET, | ||
1984 | .radio_type = UNSET, | ||
1985 | .tuner_addr = ADDR_UNSET, | ||
1986 | .radio_addr = ADDR_UNSET, | ||
1987 | .input = {{ | ||
1988 | .type = CX88_VMUX_DVB, | ||
1989 | .vmux = 0, | ||
1990 | } }, | ||
1991 | .mpeg = CX88_MPEG_DVB, | ||
1992 | }, | ||
1993 | [CX88_BOARD_TBS_8920] = { | ||
1994 | .name = "TBS 8920 DVB-S/S2", | ||
1995 | .tuner_type = TUNER_ABSENT, | ||
1996 | .radio_type = UNSET, | ||
1997 | .tuner_addr = ADDR_UNSET, | ||
1998 | .radio_addr = ADDR_UNSET, | ||
1999 | .input = {{ | ||
2000 | .type = CX88_VMUX_DVB, | ||
2001 | .vmux = 0, | ||
2002 | .gpio0 = 0x8080, | ||
2003 | } }, | ||
2004 | .mpeg = CX88_MPEG_DVB, | ||
2005 | }, | ||
2006 | [CX88_BOARD_PROF_6200] = { | ||
2007 | .name = "Prof 6200 DVB-S", | ||
2008 | .tuner_type = UNSET, | ||
2009 | .radio_type = UNSET, | ||
2010 | .tuner_addr = ADDR_UNSET, | ||
2011 | .radio_addr = ADDR_UNSET, | ||
2012 | .input = {{ | ||
2013 | .type = CX88_VMUX_DVB, | ||
2014 | .vmux = 0, | ||
2015 | } }, | ||
2016 | .mpeg = CX88_MPEG_DVB, | ||
2017 | }, | ||
2018 | [CX88_BOARD_PROF_7300] = { | ||
2019 | .name = "PROF 7300 DVB-S/S2", | ||
2020 | .tuner_type = UNSET, | ||
2021 | .radio_type = UNSET, | ||
2022 | .tuner_addr = ADDR_UNSET, | ||
2023 | .radio_addr = ADDR_UNSET, | ||
2024 | .input = {{ | ||
2025 | .type = CX88_VMUX_DVB, | ||
2026 | .vmux = 0, | ||
2027 | } }, | ||
2028 | .mpeg = CX88_MPEG_DVB, | ||
2029 | }, | ||
2030 | [CX88_BOARD_SATTRADE_ST4200] = { | ||
2031 | .name = "SATTRADE ST4200 DVB-S/S2", | ||
2032 | .tuner_type = UNSET, | ||
2033 | .radio_type = UNSET, | ||
2034 | .tuner_addr = ADDR_UNSET, | ||
2035 | .radio_addr = ADDR_UNSET, | ||
2036 | .input = {{ | ||
2037 | .type = CX88_VMUX_DVB, | ||
2038 | .vmux = 0, | ||
2039 | } }, | ||
2040 | .mpeg = CX88_MPEG_DVB, | ||
2041 | }, | ||
2042 | [CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = { | ||
2043 | .name = "Terratec Cinergy HT PCI MKII", | ||
2044 | .tuner_type = TUNER_XC2028, | ||
2045 | .tuner_addr = 0x61, | ||
2046 | .radio_type = TUNER_XC2028, | ||
2047 | .radio_addr = 0x61, | ||
2048 | .input = { { | ||
2049 | .type = CX88_VMUX_TELEVISION, | ||
2050 | .vmux = 0, | ||
2051 | .gpio0 = 0x004ff, | ||
2052 | .gpio1 = 0x010ff, | ||
2053 | .gpio2 = 0x00001, | ||
2054 | }, { | ||
2055 | .type = CX88_VMUX_COMPOSITE1, | ||
2056 | .vmux = 1, | ||
2057 | .gpio0 = 0x004fb, | ||
2058 | .gpio1 = 0x010ef, | ||
2059 | .audioroute = 1, | ||
2060 | }, { | ||
2061 | .type = CX88_VMUX_SVIDEO, | ||
2062 | .vmux = 2, | ||
2063 | .gpio0 = 0x004fb, | ||
2064 | .gpio1 = 0x010ef, | ||
2065 | .audioroute = 1, | ||
2066 | } }, | ||
2067 | .radio = { | ||
2068 | .type = CX88_RADIO, | ||
2069 | .gpio0 = 0x004ff, | ||
2070 | .gpio1 = 0x010ff, | ||
2071 | .gpio2 = 0x0ff, | ||
2072 | }, | ||
2073 | .mpeg = CX88_MPEG_DVB, | ||
2074 | }, | ||
2075 | [CX88_BOARD_HAUPPAUGE_IRONLY] = { | ||
2076 | .name = "Hauppauge WinTV-IR Only", | ||
2077 | .tuner_type = UNSET, | ||
2078 | .radio_type = UNSET, | ||
2079 | .tuner_addr = ADDR_UNSET, | ||
2080 | .radio_addr = ADDR_UNSET, | ||
2081 | }, | ||
2082 | [CX88_BOARD_WINFAST_DTV1800H] = { | ||
2083 | .name = "Leadtek WinFast DTV1800 Hybrid", | ||
2084 | .tuner_type = TUNER_XC2028, | ||
2085 | .radio_type = TUNER_XC2028, | ||
2086 | .tuner_addr = 0x61, | ||
2087 | .radio_addr = 0x61, | ||
2088 | /* | ||
2089 | * GPIO setting | ||
2090 | * | ||
2091 | * 2: mute (0=off,1=on) | ||
2092 | * 12: tuner reset pin | ||
2093 | * 13: audio source (0=tuner audio,1=line in) | ||
2094 | * 14: FM (0=on,1=off ???) | ||
2095 | */ | ||
2096 | .input = {{ | ||
2097 | .type = CX88_VMUX_TELEVISION, | ||
2098 | .vmux = 0, | ||
2099 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
2100 | .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */ | ||
2101 | .gpio2 = 0x0000, | ||
2102 | }, { | ||
2103 | .type = CX88_VMUX_COMPOSITE1, | ||
2104 | .vmux = 1, | ||
2105 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
2106 | .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ | ||
2107 | .gpio2 = 0x0000, | ||
2108 | }, { | ||
2109 | .type = CX88_VMUX_SVIDEO, | ||
2110 | .vmux = 2, | ||
2111 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
2112 | .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ | ||
2113 | .gpio2 = 0x0000, | ||
2114 | } }, | ||
2115 | .radio = { | ||
2116 | .type = CX88_RADIO, | ||
2117 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
2118 | .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */ | ||
2119 | .gpio2 = 0x0000, | ||
2120 | }, | ||
2121 | .mpeg = CX88_MPEG_DVB, | ||
2122 | }, | ||
2123 | [CX88_BOARD_WINFAST_DTV1800H_XC4000] = { | ||
2124 | .name = "Leadtek WinFast DTV1800 H (XC4000)", | ||
2125 | .tuner_type = TUNER_XC4000, | ||
2126 | .radio_type = TUNER_XC4000, | ||
2127 | .tuner_addr = 0x61, | ||
2128 | .radio_addr = 0x61, | ||
2129 | /* | ||
2130 | * GPIO setting | ||
2131 | * | ||
2132 | * 2: mute (0=off,1=on) | ||
2133 | * 12: tuner reset pin | ||
2134 | * 13: audio source (0=tuner audio,1=line in) | ||
2135 | * 14: FM (0=on,1=off ???) | ||
2136 | */ | ||
2137 | .input = {{ | ||
2138 | .type = CX88_VMUX_TELEVISION, | ||
2139 | .vmux = 0, | ||
2140 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
2141 | .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */ | ||
2142 | .gpio2 = 0x0000, | ||
2143 | }, { | ||
2144 | .type = CX88_VMUX_COMPOSITE1, | ||
2145 | .vmux = 1, | ||
2146 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
2147 | .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ | ||
2148 | .gpio2 = 0x0000, | ||
2149 | }, { | ||
2150 | .type = CX88_VMUX_SVIDEO, | ||
2151 | .vmux = 2, | ||
2152 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
2153 | .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ | ||
2154 | .gpio2 = 0x0000, | ||
2155 | }}, | ||
2156 | .radio = { | ||
2157 | .type = CX88_RADIO, | ||
2158 | .gpio0 = 0x0400, /* pin 2 = 0 */ | ||
2159 | .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */ | ||
2160 | .gpio2 = 0x0000, | ||
2161 | }, | ||
2162 | .mpeg = CX88_MPEG_DVB, | ||
2163 | }, | ||
2164 | [CX88_BOARD_WINFAST_DTV2000H_PLUS] = { | ||
2165 | .name = "Leadtek WinFast DTV2000 H PLUS", | ||
2166 | .tuner_type = TUNER_XC4000, | ||
2167 | .radio_type = TUNER_XC4000, | ||
2168 | .tuner_addr = 0x61, | ||
2169 | .radio_addr = 0x61, | ||
2170 | /* | ||
2171 | * GPIO | ||
2172 | * 2: 1: mute audio | ||
2173 | * 12: 0: reset XC4000 | ||
2174 | * 13: 1: audio input is line in (0: tuner) | ||
2175 | * 14: 0: FM radio | ||
2176 | * 16: 0: RF input is cable | ||
2177 | */ | ||
2178 | .input = {{ | ||
2179 | .type = CX88_VMUX_TELEVISION, | ||
2180 | .vmux = 0, | ||
2181 | .gpio0 = 0x0403, | ||
2182 | .gpio1 = 0xF0D7, | ||
2183 | .gpio2 = 0x0101, | ||
2184 | .gpio3 = 0x0000, | ||
2185 | }, { | ||
2186 | .type = CX88_VMUX_CABLE, | ||
2187 | .vmux = 0, | ||
2188 | .gpio0 = 0x0403, | ||
2189 | .gpio1 = 0xF0D7, | ||
2190 | .gpio2 = 0x0100, | ||
2191 | .gpio3 = 0x0000, | ||
2192 | }, { | ||
2193 | .type = CX88_VMUX_COMPOSITE1, | ||
2194 | .vmux = 1, | ||
2195 | .gpio0 = 0x0403, /* was 0x0407 */ | ||
2196 | .gpio1 = 0xF0F7, | ||
2197 | .gpio2 = 0x0101, | ||
2198 | .gpio3 = 0x0000, | ||
2199 | }, { | ||
2200 | .type = CX88_VMUX_SVIDEO, | ||
2201 | .vmux = 2, | ||
2202 | .gpio0 = 0x0403, /* was 0x0407 */ | ||
2203 | .gpio1 = 0xF0F7, | ||
2204 | .gpio2 = 0x0101, | ||
2205 | .gpio3 = 0x0000, | ||
2206 | }}, | ||
2207 | .radio = { | ||
2208 | .type = CX88_RADIO, | ||
2209 | .gpio0 = 0x0403, | ||
2210 | .gpio1 = 0xF097, | ||
2211 | .gpio2 = 0x0100, | ||
2212 | .gpio3 = 0x0000, | ||
2213 | }, | ||
2214 | .mpeg = CX88_MPEG_DVB, | ||
2215 | }, | ||
2216 | [CX88_BOARD_PROF_7301] = { | ||
2217 | .name = "Prof 7301 DVB-S/S2", | ||
2218 | .tuner_type = UNSET, | ||
2219 | .radio_type = UNSET, | ||
2220 | .tuner_addr = ADDR_UNSET, | ||
2221 | .radio_addr = ADDR_UNSET, | ||
2222 | .input = { { | ||
2223 | .type = CX88_VMUX_DVB, | ||
2224 | .vmux = 0, | ||
2225 | } }, | ||
2226 | .mpeg = CX88_MPEG_DVB, | ||
2227 | }, | ||
2228 | [CX88_BOARD_TWINHAN_VP1027_DVBS] = { | ||
2229 | .name = "Twinhan VP-1027 DVB-S", | ||
2230 | .tuner_type = TUNER_ABSENT, | ||
2231 | .radio_type = UNSET, | ||
2232 | .tuner_addr = ADDR_UNSET, | ||
2233 | .radio_addr = ADDR_UNSET, | ||
2234 | .input = {{ | ||
2235 | .type = CX88_VMUX_DVB, | ||
2236 | .vmux = 0, | ||
2237 | } }, | ||
2238 | .mpeg = CX88_MPEG_DVB, | ||
2239 | }, | ||
2240 | }; | ||
2241 | |||
2242 | /* ------------------------------------------------------------------ */ | ||
2243 | /* PCI subsystem IDs */ | ||
2244 | |||
2245 | static const struct cx88_subid cx88_subids[] = { | ||
2246 | { | ||
2247 | .subvendor = 0x0070, | ||
2248 | .subdevice = 0x3400, | ||
2249 | .card = CX88_BOARD_HAUPPAUGE, | ||
2250 | },{ | ||
2251 | .subvendor = 0x0070, | ||
2252 | .subdevice = 0x3401, | ||
2253 | .card = CX88_BOARD_HAUPPAUGE, | ||
2254 | },{ | ||
2255 | .subvendor = 0x14c7, | ||
2256 | .subdevice = 0x0106, | ||
2257 | .card = CX88_BOARD_GDI, | ||
2258 | },{ | ||
2259 | .subvendor = 0x14c7, | ||
2260 | .subdevice = 0x0107, /* with mpeg encoder */ | ||
2261 | .card = CX88_BOARD_GDI, | ||
2262 | },{ | ||
2263 | .subvendor = PCI_VENDOR_ID_ATI, | ||
2264 | .subdevice = 0x00f8, | ||
2265 | .card = CX88_BOARD_ATI_WONDER_PRO, | ||
2266 | }, { | ||
2267 | .subvendor = PCI_VENDOR_ID_ATI, | ||
2268 | .subdevice = 0x00f9, | ||
2269 | .card = CX88_BOARD_ATI_WONDER_PRO, | ||
2270 | }, { | ||
2271 | .subvendor = 0x107d, | ||
2272 | .subdevice = 0x6611, | ||
2273 | .card = CX88_BOARD_WINFAST2000XP_EXPERT, | ||
2274 | },{ | ||
2275 | .subvendor = 0x107d, | ||
2276 | .subdevice = 0x6613, /* NTSC */ | ||
2277 | .card = CX88_BOARD_WINFAST2000XP_EXPERT, | ||
2278 | },{ | ||
2279 | .subvendor = 0x107d, | ||
2280 | .subdevice = 0x6620, | ||
2281 | .card = CX88_BOARD_WINFAST_DV2000, | ||
2282 | },{ | ||
2283 | .subvendor = 0x107d, | ||
2284 | .subdevice = 0x663b, | ||
2285 | .card = CX88_BOARD_LEADTEK_PVR2000, | ||
2286 | },{ | ||
2287 | .subvendor = 0x107d, | ||
2288 | .subdevice = 0x663c, | ||
2289 | .card = CX88_BOARD_LEADTEK_PVR2000, | ||
2290 | },{ | ||
2291 | .subvendor = 0x1461, | ||
2292 | .subdevice = 0x000b, | ||
2293 | .card = CX88_BOARD_AVERTV_STUDIO_303, | ||
2294 | },{ | ||
2295 | .subvendor = 0x1462, | ||
2296 | .subdevice = 0x8606, | ||
2297 | .card = CX88_BOARD_MSI_TVANYWHERE_MASTER, | ||
2298 | },{ | ||
2299 | .subvendor = 0x10fc, | ||
2300 | .subdevice = 0xd003, | ||
2301 | .card = CX88_BOARD_IODATA_GVVCP3PCI, | ||
2302 | },{ | ||
2303 | .subvendor = 0x1043, | ||
2304 | .subdevice = 0x4823, /* with mpeg encoder */ | ||
2305 | .card = CX88_BOARD_ASUS_PVR_416, | ||
2306 | },{ | ||
2307 | .subvendor = 0x17de, | ||
2308 | .subdevice = 0x08a6, | ||
2309 | .card = CX88_BOARD_KWORLD_DVB_T, | ||
2310 | },{ | ||
2311 | .subvendor = 0x18ac, | ||
2312 | .subdevice = 0xd810, | ||
2313 | .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q, | ||
2314 | },{ | ||
2315 | .subvendor = 0x18ac, | ||
2316 | .subdevice = 0xd820, | ||
2317 | .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T, | ||
2318 | },{ | ||
2319 | .subvendor = 0x18ac, | ||
2320 | .subdevice = 0xdb00, | ||
2321 | .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1, | ||
2322 | },{ | ||
2323 | .subvendor = 0x0070, | ||
2324 | .subdevice = 0x9002, | ||
2325 | .card = CX88_BOARD_HAUPPAUGE_DVB_T1, | ||
2326 | },{ | ||
2327 | .subvendor = 0x14f1, | ||
2328 | .subdevice = 0x0187, | ||
2329 | .card = CX88_BOARD_CONEXANT_DVB_T1, | ||
2330 | },{ | ||
2331 | .subvendor = 0x1540, | ||
2332 | .subdevice = 0x2580, | ||
2333 | .card = CX88_BOARD_PROVIDEO_PV259, | ||
2334 | },{ | ||
2335 | .subvendor = 0x18ac, | ||
2336 | .subdevice = 0xdb10, | ||
2337 | .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, | ||
2338 | },{ | ||
2339 | .subvendor = 0x1554, | ||
2340 | .subdevice = 0x4811, | ||
2341 | .card = CX88_BOARD_PIXELVIEW, | ||
2342 | },{ | ||
2343 | .subvendor = 0x7063, | ||
2344 | .subdevice = 0x3000, /* HD-3000 card */ | ||
2345 | .card = CX88_BOARD_PCHDTV_HD3000, | ||
2346 | },{ | ||
2347 | .subvendor = 0x17de, | ||
2348 | .subdevice = 0xa8a6, | ||
2349 | .card = CX88_BOARD_DNTV_LIVE_DVB_T, | ||
2350 | },{ | ||
2351 | .subvendor = 0x0070, | ||
2352 | .subdevice = 0x2801, | ||
2353 | .card = CX88_BOARD_HAUPPAUGE_ROSLYN, | ||
2354 | },{ | ||
2355 | .subvendor = 0x14f1, | ||
2356 | .subdevice = 0x0342, | ||
2357 | .card = CX88_BOARD_DIGITALLOGIC_MEC, | ||
2358 | },{ | ||
2359 | .subvendor = 0x10fc, | ||
2360 | .subdevice = 0xd035, | ||
2361 | .card = CX88_BOARD_IODATA_GVBCTV7E, | ||
2362 | },{ | ||
2363 | .subvendor = 0x1421, | ||
2364 | .subdevice = 0x0334, | ||
2365 | .card = CX88_BOARD_ADSTECH_DVB_T_PCI, | ||
2366 | },{ | ||
2367 | .subvendor = 0x153b, | ||
2368 | .subdevice = 0x1166, | ||
2369 | .card = CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1, | ||
2370 | },{ | ||
2371 | .subvendor = 0x18ac, | ||
2372 | .subdevice = 0xd500, | ||
2373 | .card = CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD, | ||
2374 | },{ | ||
2375 | .subvendor = 0x1461, | ||
2376 | .subdevice = 0x8011, | ||
2377 | .card = CX88_BOARD_AVERMEDIA_ULTRATV_MC_550, | ||
2378 | },{ | ||
2379 | .subvendor = PCI_VENDOR_ID_ATI, | ||
2380 | .subdevice = 0xa101, | ||
2381 | .card = CX88_BOARD_ATI_HDTVWONDER, | ||
2382 | },{ | ||
2383 | .subvendor = 0x107d, | ||
2384 | .subdevice = 0x665f, | ||
2385 | .card = CX88_BOARD_WINFAST_DTV1000, | ||
2386 | },{ | ||
2387 | .subvendor = 0x1461, | ||
2388 | .subdevice = 0x000a, | ||
2389 | .card = CX88_BOARD_AVERTV_303, | ||
2390 | },{ | ||
2391 | .subvendor = 0x0070, | ||
2392 | .subdevice = 0x9200, | ||
2393 | .card = CX88_BOARD_HAUPPAUGE_NOVASE2_S1, | ||
2394 | },{ | ||
2395 | .subvendor = 0x0070, | ||
2396 | .subdevice = 0x9201, | ||
2397 | .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1, | ||
2398 | },{ | ||
2399 | .subvendor = 0x0070, | ||
2400 | .subdevice = 0x9202, | ||
2401 | .card = CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1, | ||
2402 | },{ | ||
2403 | .subvendor = 0x17de, | ||
2404 | .subdevice = 0x08b2, | ||
2405 | .card = CX88_BOARD_KWORLD_DVBS_100, | ||
2406 | },{ | ||
2407 | .subvendor = 0x0070, | ||
2408 | .subdevice = 0x9400, | ||
2409 | .card = CX88_BOARD_HAUPPAUGE_HVR1100, | ||
2410 | },{ | ||
2411 | .subvendor = 0x0070, | ||
2412 | .subdevice = 0x9402, | ||
2413 | .card = CX88_BOARD_HAUPPAUGE_HVR1100, | ||
2414 | },{ | ||
2415 | .subvendor = 0x0070, | ||
2416 | .subdevice = 0x9800, | ||
2417 | .card = CX88_BOARD_HAUPPAUGE_HVR1100LP, | ||
2418 | },{ | ||
2419 | .subvendor = 0x0070, | ||
2420 | .subdevice = 0x9802, | ||
2421 | .card = CX88_BOARD_HAUPPAUGE_HVR1100LP, | ||
2422 | },{ | ||
2423 | .subvendor = 0x0070, | ||
2424 | .subdevice = 0x9001, | ||
2425 | .card = CX88_BOARD_HAUPPAUGE_DVB_T1, | ||
2426 | },{ | ||
2427 | .subvendor = 0x1822, | ||
2428 | .subdevice = 0x0025, | ||
2429 | .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, | ||
2430 | },{ | ||
2431 | .subvendor = 0x17de, | ||
2432 | .subdevice = 0x08a1, | ||
2433 | .card = CX88_BOARD_KWORLD_DVB_T_CX22702, | ||
2434 | },{ | ||
2435 | .subvendor = 0x18ac, | ||
2436 | .subdevice = 0xdb50, | ||
2437 | .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL, | ||
2438 | },{ | ||
2439 | .subvendor = 0x18ac, | ||
2440 | .subdevice = 0xdb54, | ||
2441 | .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL, | ||
2442 | /* Re-branded DViCO: DigitalNow DVB-T Dual */ | ||
2443 | },{ | ||
2444 | .subvendor = 0x18ac, | ||
2445 | .subdevice = 0xdb11, | ||
2446 | .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS, | ||
2447 | /* Re-branded DViCO: UltraView DVB-T Plus */ | ||
2448 | }, { | ||
2449 | .subvendor = 0x18ac, | ||
2450 | .subdevice = 0xdb30, | ||
2451 | .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO, | ||
2452 | }, { | ||
2453 | .subvendor = 0x17de, | ||
2454 | .subdevice = 0x0840, | ||
2455 | .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, | ||
2456 | },{ | ||
2457 | .subvendor = 0x1421, | ||
2458 | .subdevice = 0x0305, | ||
2459 | .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, | ||
2460 | },{ | ||
2461 | .subvendor = 0x18ac, | ||
2462 | .subdevice = 0xdb40, | ||
2463 | .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID, | ||
2464 | },{ | ||
2465 | .subvendor = 0x18ac, | ||
2466 | .subdevice = 0xdb44, | ||
2467 | .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID, | ||
2468 | },{ | ||
2469 | .subvendor = 0x7063, | ||
2470 | .subdevice = 0x5500, | ||
2471 | .card = CX88_BOARD_PCHDTV_HD5500, | ||
2472 | },{ | ||
2473 | .subvendor = 0x17de, | ||
2474 | .subdevice = 0x0841, | ||
2475 | .card = CX88_BOARD_KWORLD_MCE200_DELUXE, | ||
2476 | },{ | ||
2477 | .subvendor = 0x1822, | ||
2478 | .subdevice = 0x0019, | ||
2479 | .card = CX88_BOARD_DNTV_LIVE_DVB_T_PRO, | ||
2480 | },{ | ||
2481 | .subvendor = 0x1554, | ||
2482 | .subdevice = 0x4813, | ||
2483 | .card = CX88_BOARD_PIXELVIEW_PLAYTV_P7000, | ||
2484 | },{ | ||
2485 | .subvendor = 0x14f1, | ||
2486 | .subdevice = 0x0842, | ||
2487 | .card = CX88_BOARD_NPGTECH_REALTV_TOP10FM, | ||
2488 | },{ | ||
2489 | .subvendor = 0x107d, | ||
2490 | .subdevice = 0x665e, | ||
2491 | .card = CX88_BOARD_WINFAST_DTV2000H, | ||
2492 | },{ | ||
2493 | .subvendor = 0x107d, | ||
2494 | .subdevice = 0x6f2b, | ||
2495 | .card = CX88_BOARD_WINFAST_DTV2000H_J, | ||
2496 | },{ | ||
2497 | .subvendor = 0x18ac, | ||
2498 | .subdevice = 0xd800, /* FusionHDTV 3 Gold (original revision) */ | ||
2499 | .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q, | ||
2500 | },{ | ||
2501 | .subvendor = 0x14f1, | ||
2502 | .subdevice = 0x0084, | ||
2503 | .card = CX88_BOARD_GENIATECH_DVBS, | ||
2504 | },{ | ||
2505 | .subvendor = 0x0070, | ||
2506 | .subdevice = 0x1404, | ||
2507 | .card = CX88_BOARD_HAUPPAUGE_HVR3000, | ||
2508 | }, { | ||
2509 | .subvendor = 0x18ac, | ||
2510 | .subdevice = 0xdc00, | ||
2511 | .card = CX88_BOARD_SAMSUNG_SMT_7020, | ||
2512 | }, { | ||
2513 | .subvendor = 0x18ac, | ||
2514 | .subdevice = 0xdccd, | ||
2515 | .card = CX88_BOARD_SAMSUNG_SMT_7020, | ||
2516 | },{ | ||
2517 | .subvendor = 0x1461, | ||
2518 | .subdevice = 0xc111, /* AverMedia M150-D */ | ||
2519 | /* This board is known to work with the ASUS PVR416 config */ | ||
2520 | .card = CX88_BOARD_ASUS_PVR_416, | ||
2521 | },{ | ||
2522 | .subvendor = 0xc180, | ||
2523 | .subdevice = 0xc980, | ||
2524 | .card = CX88_BOARD_TE_DTV_250_OEM_SWANN, | ||
2525 | },{ | ||
2526 | .subvendor = 0x0070, | ||
2527 | .subdevice = 0x9600, | ||
2528 | .card = CX88_BOARD_HAUPPAUGE_HVR1300, | ||
2529 | },{ | ||
2530 | .subvendor = 0x0070, | ||
2531 | .subdevice = 0x9601, | ||
2532 | .card = CX88_BOARD_HAUPPAUGE_HVR1300, | ||
2533 | },{ | ||
2534 | .subvendor = 0x0070, | ||
2535 | .subdevice = 0x9602, | ||
2536 | .card = CX88_BOARD_HAUPPAUGE_HVR1300, | ||
2537 | },{ | ||
2538 | .subvendor = 0x107d, | ||
2539 | .subdevice = 0x6632, | ||
2540 | .card = CX88_BOARD_LEADTEK_PVR2000, | ||
2541 | },{ | ||
2542 | .subvendor = 0x12ab, | ||
2543 | .subdevice = 0x2300, /* Club3D Zap TV2100 */ | ||
2544 | .card = CX88_BOARD_KWORLD_DVB_T_CX22702, | ||
2545 | },{ | ||
2546 | .subvendor = 0x0070, | ||
2547 | .subdevice = 0x9000, | ||
2548 | .card = CX88_BOARD_HAUPPAUGE_DVB_T1, | ||
2549 | },{ | ||
2550 | .subvendor = 0x0070, | ||
2551 | .subdevice = 0x1400, | ||
2552 | .card = CX88_BOARD_HAUPPAUGE_HVR3000, | ||
2553 | },{ | ||
2554 | .subvendor = 0x0070, | ||
2555 | .subdevice = 0x1401, | ||
2556 | .card = CX88_BOARD_HAUPPAUGE_HVR3000, | ||
2557 | },{ | ||
2558 | .subvendor = 0x0070, | ||
2559 | .subdevice = 0x1402, | ||
2560 | .card = CX88_BOARD_HAUPPAUGE_HVR3000, | ||
2561 | },{ | ||
2562 | .subvendor = 0x1421, | ||
2563 | .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */ | ||
2564 | .card = CX88_BOARD_KWORLD_DVBS_100, | ||
2565 | },{ | ||
2566 | .subvendor = 0x1421, | ||
2567 | .subdevice = 0x0390, | ||
2568 | .card = CX88_BOARD_ADSTECH_PTV_390, | ||
2569 | },{ | ||
2570 | .subvendor = 0x11bd, | ||
2571 | .subdevice = 0x0051, | ||
2572 | .card = CX88_BOARD_PINNACLE_PCTV_HD_800i, | ||
2573 | }, { | ||
2574 | .subvendor = 0x18ac, | ||
2575 | .subdevice = 0xd530, | ||
2576 | .card = CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO, | ||
2577 | }, { | ||
2578 | .subvendor = 0x12ab, | ||
2579 | .subdevice = 0x1788, | ||
2580 | .card = CX88_BOARD_PINNACLE_HYBRID_PCTV, | ||
2581 | }, { | ||
2582 | .subvendor = 0x14f1, | ||
2583 | .subdevice = 0xea3d, | ||
2584 | .card = CX88_BOARD_POWERCOLOR_REAL_ANGEL, | ||
2585 | }, { | ||
2586 | .subvendor = 0x107d, | ||
2587 | .subdevice = 0x6f18, | ||
2588 | .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, | ||
2589 | }, { | ||
2590 | .subvendor = 0x14f1, | ||
2591 | .subdevice = 0x8852, | ||
2592 | .card = CX88_BOARD_GENIATECH_X8000_MT, | ||
2593 | }, { | ||
2594 | .subvendor = 0x18ac, | ||
2595 | .subdevice = 0xd610, | ||
2596 | .card = CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD, | ||
2597 | }, { | ||
2598 | .subvendor = 0x1554, | ||
2599 | .subdevice = 0x4935, | ||
2600 | .card = CX88_BOARD_PROLINK_PV_8000GT, | ||
2601 | }, { | ||
2602 | .subvendor = 0x1554, | ||
2603 | .subdevice = 0x4976, | ||
2604 | .card = CX88_BOARD_PROLINK_PV_GLOBAL_XTREME, | ||
2605 | }, { | ||
2606 | .subvendor = 0x17de, | ||
2607 | .subdevice = 0x08c1, | ||
2608 | .card = CX88_BOARD_KWORLD_ATSC_120, | ||
2609 | }, { | ||
2610 | .subvendor = 0x0070, | ||
2611 | .subdevice = 0x6900, | ||
2612 | .card = CX88_BOARD_HAUPPAUGE_HVR4000, | ||
2613 | }, { | ||
2614 | .subvendor = 0x0070, | ||
2615 | .subdevice = 0x6904, | ||
2616 | .card = CX88_BOARD_HAUPPAUGE_HVR4000, | ||
2617 | }, { | ||
2618 | .subvendor = 0x0070, | ||
2619 | .subdevice = 0x6902, | ||
2620 | .card = CX88_BOARD_HAUPPAUGE_HVR4000, | ||
2621 | }, { | ||
2622 | .subvendor = 0x0070, | ||
2623 | .subdevice = 0x6905, | ||
2624 | .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE, | ||
2625 | }, { | ||
2626 | .subvendor = 0x0070, | ||
2627 | .subdevice = 0x6906, | ||
2628 | .card = CX88_BOARD_HAUPPAUGE_HVR4000LITE, | ||
2629 | }, { | ||
2630 | .subvendor = 0xd420, | ||
2631 | .subdevice = 0x9022, | ||
2632 | .card = CX88_BOARD_TEVII_S420, | ||
2633 | }, { | ||
2634 | .subvendor = 0xd460, | ||
2635 | .subdevice = 0x9022, | ||
2636 | .card = CX88_BOARD_TEVII_S460, | ||
2637 | }, { | ||
2638 | .subvendor = 0xd464, | ||
2639 | .subdevice = 0x9022, | ||
2640 | .card = CX88_BOARD_TEVII_S464, | ||
2641 | }, { | ||
2642 | .subvendor = 0xA044, | ||
2643 | .subdevice = 0x2011, | ||
2644 | .card = CX88_BOARD_OMICOM_SS4_PCI, | ||
2645 | }, { | ||
2646 | .subvendor = 0x8910, | ||
2647 | .subdevice = 0x8888, | ||
2648 | .card = CX88_BOARD_TBS_8910, | ||
2649 | }, { | ||
2650 | .subvendor = 0x8920, | ||
2651 | .subdevice = 0x8888, | ||
2652 | .card = CX88_BOARD_TBS_8920, | ||
2653 | }, { | ||
2654 | .subvendor = 0xb022, | ||
2655 | .subdevice = 0x3022, | ||
2656 | .card = CX88_BOARD_PROF_6200, | ||
2657 | }, { | ||
2658 | .subvendor = 0xB033, | ||
2659 | .subdevice = 0x3033, | ||
2660 | .card = CX88_BOARD_PROF_7300, | ||
2661 | }, { | ||
2662 | .subvendor = 0xb200, | ||
2663 | .subdevice = 0x4200, | ||
2664 | .card = CX88_BOARD_SATTRADE_ST4200, | ||
2665 | }, { | ||
2666 | .subvendor = 0x153b, | ||
2667 | .subdevice = 0x1177, | ||
2668 | .card = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII, | ||
2669 | }, { | ||
2670 | .subvendor = 0x0070, | ||
2671 | .subdevice = 0x9290, | ||
2672 | .card = CX88_BOARD_HAUPPAUGE_IRONLY, | ||
2673 | }, { | ||
2674 | .subvendor = 0x107d, | ||
2675 | .subdevice = 0x6654, | ||
2676 | .card = CX88_BOARD_WINFAST_DTV1800H, | ||
2677 | }, { | ||
2678 | /* WinFast DTV1800 H with XC4000 tuner */ | ||
2679 | .subvendor = 0x107d, | ||
2680 | .subdevice = 0x6f38, | ||
2681 | .card = CX88_BOARD_WINFAST_DTV1800H_XC4000, | ||
2682 | }, { | ||
2683 | .subvendor = 0x107d, | ||
2684 | .subdevice = 0x6f42, | ||
2685 | .card = CX88_BOARD_WINFAST_DTV2000H_PLUS, | ||
2686 | }, { | ||
2687 | /* PVR2000 PAL Model [107d:6630] */ | ||
2688 | .subvendor = 0x107d, | ||
2689 | .subdevice = 0x6630, | ||
2690 | .card = CX88_BOARD_LEADTEK_PVR2000, | ||
2691 | }, { | ||
2692 | /* PVR2000 PAL Model [107d:6638] */ | ||
2693 | .subvendor = 0x107d, | ||
2694 | .subdevice = 0x6638, | ||
2695 | .card = CX88_BOARD_LEADTEK_PVR2000, | ||
2696 | }, { | ||
2697 | /* PVR2000 NTSC Model [107d:6631] */ | ||
2698 | .subvendor = 0x107d, | ||
2699 | .subdevice = 0x6631, | ||
2700 | .card = CX88_BOARD_LEADTEK_PVR2000, | ||
2701 | }, { | ||
2702 | /* PVR2000 NTSC Model [107d:6637] */ | ||
2703 | .subvendor = 0x107d, | ||
2704 | .subdevice = 0x6637, | ||
2705 | .card = CX88_BOARD_LEADTEK_PVR2000, | ||
2706 | }, { | ||
2707 | /* PVR2000 NTSC Model [107d:663d] */ | ||
2708 | .subvendor = 0x107d, | ||
2709 | .subdevice = 0x663d, | ||
2710 | .card = CX88_BOARD_LEADTEK_PVR2000, | ||
2711 | }, { | ||
2712 | /* DV2000 NTSC Model [107d:6621] */ | ||
2713 | .subvendor = 0x107d, | ||
2714 | .subdevice = 0x6621, | ||
2715 | .card = CX88_BOARD_WINFAST_DV2000, | ||
2716 | }, { | ||
2717 | /* TV2000 XP Global [107d:6618] */ | ||
2718 | .subvendor = 0x107d, | ||
2719 | .subdevice = 0x6618, | ||
2720 | .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, | ||
2721 | }, { | ||
2722 | .subvendor = 0xb034, | ||
2723 | .subdevice = 0x3034, | ||
2724 | .card = CX88_BOARD_PROF_7301, | ||
2725 | }, { | ||
2726 | .subvendor = 0x1822, | ||
2727 | .subdevice = 0x0023, | ||
2728 | .card = CX88_BOARD_TWINHAN_VP1027_DVBS, | ||
2729 | }, | ||
2730 | }; | ||
2731 | |||
2732 | /* ----------------------------------------------------------------------- */ | ||
2733 | /* some leadtek specific stuff */ | ||
2734 | |||
2735 | static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data) | ||
2736 | { | ||
2737 | if (eeprom_data[4] != 0x7d || | ||
2738 | eeprom_data[5] != 0x10 || | ||
2739 | eeprom_data[7] != 0x66) { | ||
2740 | warn_printk(core, "Leadtek eeprom invalid.\n"); | ||
2741 | return; | ||
2742 | } | ||
2743 | |||
2744 | /* Terry Wu <terrywu2009@gmail.com> */ | ||
2745 | switch (eeprom_data[6]) { | ||
2746 | case 0x13: /* SSID 6613 for TV2000 XP Expert NTSC Model */ | ||
2747 | case 0x21: /* SSID 6621 for DV2000 NTSC Model */ | ||
2748 | case 0x31: /* SSID 6631 for PVR2000 NTSC Model */ | ||
2749 | case 0x37: /* SSID 6637 for PVR2000 NTSC Model */ | ||
2750 | case 0x3d: /* SSID 6637 for PVR2000 NTSC Model */ | ||
2751 | core->board.tuner_type = TUNER_PHILIPS_FM1236_MK3; | ||
2752 | break; | ||
2753 | default: | ||
2754 | core->board.tuner_type = TUNER_PHILIPS_FM1216ME_MK3; | ||
2755 | break; | ||
2756 | } | ||
2757 | |||
2758 | info_printk(core, "Leadtek Winfast 2000XP Expert config: " | ||
2759 | "tuner=%d, eeprom[0]=0x%02x\n", | ||
2760 | core->board.tuner_type, eeprom_data[0]); | ||
2761 | } | ||
2762 | |||
2763 | static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) | ||
2764 | { | ||
2765 | struct tveeprom tv; | ||
2766 | |||
2767 | tveeprom_hauppauge_analog(&core->i2c_client, &tv, eeprom_data); | ||
2768 | core->board.tuner_type = tv.tuner_type; | ||
2769 | core->tuner_formats = tv.tuner_formats; | ||
2770 | core->board.radio.type = tv.has_radio ? CX88_RADIO : 0; | ||
2771 | |||
2772 | /* Make sure we support the board model */ | ||
2773 | switch (tv.model) | ||
2774 | { | ||
2775 | case 14009: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in) */ | ||
2776 | case 14019: /* WinTV-HVR3000 (Retail, IR Blaster, b/panel video, 3.5mm audio in) */ | ||
2777 | case 14029: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge) */ | ||
2778 | case 14109: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - low profile) */ | ||
2779 | case 14129: /* WinTV-HVR3000 (Retail, IR, b/panel video, 3.5mm audio in - 880 bridge - LP) */ | ||
2780 | case 14559: /* WinTV-HVR3000 (OEM, no IR, b/panel video, 3.5mm audio in) */ | ||
2781 | case 14569: /* WinTV-HVR3000 (OEM, no IR, no back panel video) */ | ||
2782 | case 14659: /* WinTV-HVR3000 (OEM, no IR, b/panel video, RCA audio in - Low profile) */ | ||
2783 | case 14669: /* WinTV-HVR3000 (OEM, no IR, no b/panel video - Low profile) */ | ||
2784 | case 28552: /* WinTV-PVR 'Roslyn' (No IR) */ | ||
2785 | case 34519: /* WinTV-PCI-FM */ | ||
2786 | case 69009: | ||
2787 | /* WinTV-HVR4000 (DVBS/S2/T, Video and IR, back panel inputs) */ | ||
2788 | case 69100: /* WinTV-HVR4000LITE (DVBS/S2, IR) */ | ||
2789 | case 69500: /* WinTV-HVR4000LITE (DVBS/S2, No IR) */ | ||
2790 | case 69559: | ||
2791 | /* WinTV-HVR4000 (DVBS/S2/T, Video no IR, back panel inputs) */ | ||
2792 | case 69569: /* WinTV-HVR4000 (DVBS/S2/T, Video no IR) */ | ||
2793 | case 90002: /* Nova-T-PCI (9002) */ | ||
2794 | case 92001: /* Nova-S-Plus (Video and IR) */ | ||
2795 | case 92002: /* Nova-S-Plus (Video and IR) */ | ||
2796 | case 90003: /* Nova-T-PCI (9002 No RF out) */ | ||
2797 | case 90500: /* Nova-T-PCI (oem) */ | ||
2798 | case 90501: /* Nova-T-PCI (oem/IR) */ | ||
2799 | case 92000: /* Nova-SE2 (OEM, No Video or IR) */ | ||
2800 | case 92900: /* WinTV-IROnly (No analog or digital Video inputs) */ | ||
2801 | case 94009: /* WinTV-HVR1100 (Video and IR Retail) */ | ||
2802 | case 94501: /* WinTV-HVR1100 (Video and IR OEM) */ | ||
2803 | case 96009: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX) */ | ||
2804 | case 96019: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX/TX) */ | ||
2805 | case 96559: /* WinTV-HVR1300 (PAL Video, MPEG Video no IR) */ | ||
2806 | case 96569: /* WinTV-HVR1300 () */ | ||
2807 | case 96659: /* WinTV-HVR1300 () */ | ||
2808 | case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */ | ||
2809 | /* known */ | ||
2810 | break; | ||
2811 | case CX88_BOARD_SAMSUNG_SMT_7020: | ||
2812 | cx_set(MO_GP0_IO, 0x008989FF); | ||
2813 | break; | ||
2814 | default: | ||
2815 | warn_printk(core, "warning: unknown hauppauge model #%d\n", | ||
2816 | tv.model); | ||
2817 | break; | ||
2818 | } | ||
2819 | |||
2820 | info_printk(core, "hauppauge eeprom: model=%d\n", tv.model); | ||
2821 | } | ||
2822 | |||
2823 | /* ----------------------------------------------------------------------- */ | ||
2824 | /* some GDI (was: Modular Technology) specific stuff */ | ||
2825 | |||
2826 | static const struct { | ||
2827 | int id; | ||
2828 | int fm; | ||
2829 | const char *name; | ||
2830 | } gdi_tuner[] = { | ||
2831 | [ 0x01 ] = { .id = TUNER_ABSENT, | ||
2832 | .name = "NTSC_M" }, | ||
2833 | [ 0x02 ] = { .id = TUNER_ABSENT, | ||
2834 | .name = "PAL_B" }, | ||
2835 | [ 0x03 ] = { .id = TUNER_ABSENT, | ||
2836 | .name = "PAL_I" }, | ||
2837 | [ 0x04 ] = { .id = TUNER_ABSENT, | ||
2838 | .name = "PAL_D" }, | ||
2839 | [ 0x05 ] = { .id = TUNER_ABSENT, | ||
2840 | .name = "SECAM" }, | ||
2841 | |||
2842 | [ 0x10 ] = { .id = TUNER_ABSENT, | ||
2843 | .fm = 1, | ||
2844 | .name = "TEMIC_4049" }, | ||
2845 | [ 0x11 ] = { .id = TUNER_TEMIC_4136FY5, | ||
2846 | .name = "TEMIC_4136" }, | ||
2847 | [ 0x12 ] = { .id = TUNER_ABSENT, | ||
2848 | .name = "TEMIC_4146" }, | ||
2849 | |||
2850 | [ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME, | ||
2851 | .fm = 1, | ||
2852 | .name = "PHILIPS_FQ1216_MK3" }, | ||
2853 | [ 0x21 ] = { .id = TUNER_ABSENT, .fm = 1, | ||
2854 | .name = "PHILIPS_FQ1236_MK3" }, | ||
2855 | [ 0x22 ] = { .id = TUNER_ABSENT, | ||
2856 | .name = "PHILIPS_FI1236_MK3" }, | ||
2857 | [ 0x23 ] = { .id = TUNER_ABSENT, | ||
2858 | .name = "PHILIPS_FI1216_MK3" }, | ||
2859 | }; | ||
2860 | |||
2861 | static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data) | ||
2862 | { | ||
2863 | const char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner)) | ||
2864 | ? gdi_tuner[eeprom_data[0x0d]].name : NULL; | ||
2865 | |||
2866 | info_printk(core, "GDI: tuner=%s\n", name ? name : "unknown"); | ||
2867 | if (NULL == name) | ||
2868 | return; | ||
2869 | core->board.tuner_type = gdi_tuner[eeprom_data[0x0d]].id; | ||
2870 | core->board.radio.type = gdi_tuner[eeprom_data[0x0d]].fm ? | ||
2871 | CX88_RADIO : 0; | ||
2872 | } | ||
2873 | |||
2874 | /* ------------------------------------------------------------------- */ | ||
2875 | /* some Divco specific stuff */ | ||
2876 | static int cx88_dvico_xc2028_callback(struct cx88_core *core, | ||
2877 | int command, int arg) | ||
2878 | { | ||
2879 | switch (command) { | ||
2880 | case XC2028_TUNER_RESET: | ||
2881 | switch (core->boardnr) { | ||
2882 | case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: | ||
2883 | /* GPIO-4 xc3028 tuner */ | ||
2884 | |||
2885 | cx_set(MO_GP0_IO, 0x00001000); | ||
2886 | cx_clear(MO_GP0_IO, 0x00000010); | ||
2887 | msleep(100); | ||
2888 | cx_set(MO_GP0_IO, 0x00000010); | ||
2889 | msleep(100); | ||
2890 | break; | ||
2891 | default: | ||
2892 | cx_write(MO_GP0_IO, 0x101000); | ||
2893 | mdelay(5); | ||
2894 | cx_set(MO_GP0_IO, 0x101010); | ||
2895 | } | ||
2896 | break; | ||
2897 | default: | ||
2898 | return -EINVAL; | ||
2899 | } | ||
2900 | |||
2901 | return 0; | ||
2902 | } | ||
2903 | |||
2904 | |||
2905 | /* ----------------------------------------------------------------------- */ | ||
2906 | /* some Geniatech specific stuff */ | ||
2907 | |||
2908 | static int cx88_xc3028_geniatech_tuner_callback(struct cx88_core *core, | ||
2909 | int command, int mode) | ||
2910 | { | ||
2911 | switch (command) { | ||
2912 | case XC2028_TUNER_RESET: | ||
2913 | switch (INPUT(core->input).type) { | ||
2914 | case CX88_RADIO: | ||
2915 | break; | ||
2916 | case CX88_VMUX_DVB: | ||
2917 | cx_write(MO_GP1_IO, 0x030302); | ||
2918 | mdelay(50); | ||
2919 | break; | ||
2920 | default: | ||
2921 | cx_write(MO_GP1_IO, 0x030301); | ||
2922 | mdelay(50); | ||
2923 | } | ||
2924 | cx_write(MO_GP1_IO, 0x101010); | ||
2925 | mdelay(50); | ||
2926 | cx_write(MO_GP1_IO, 0x101000); | ||
2927 | mdelay(50); | ||
2928 | cx_write(MO_GP1_IO, 0x101010); | ||
2929 | mdelay(50); | ||
2930 | return 0; | ||
2931 | } | ||
2932 | return -EINVAL; | ||
2933 | } | ||
2934 | |||
2935 | static int cx88_xc3028_winfast1800h_callback(struct cx88_core *core, | ||
2936 | int command, int arg) | ||
2937 | { | ||
2938 | switch (command) { | ||
2939 | case XC2028_TUNER_RESET: | ||
2940 | /* GPIO 12 (xc3028 tuner reset) */ | ||
2941 | cx_set(MO_GP1_IO, 0x1010); | ||
2942 | mdelay(50); | ||
2943 | cx_clear(MO_GP1_IO, 0x10); | ||
2944 | mdelay(50); | ||
2945 | cx_set(MO_GP1_IO, 0x10); | ||
2946 | mdelay(50); | ||
2947 | return 0; | ||
2948 | } | ||
2949 | return -EINVAL; | ||
2950 | } | ||
2951 | |||
2952 | static int cx88_xc4000_winfast2000h_plus_callback(struct cx88_core *core, | ||
2953 | int command, int arg) | ||
2954 | { | ||
2955 | switch (command) { | ||
2956 | case XC4000_TUNER_RESET: | ||
2957 | /* GPIO 12 (xc4000 tuner reset) */ | ||
2958 | cx_set(MO_GP1_IO, 0x1010); | ||
2959 | mdelay(50); | ||
2960 | cx_clear(MO_GP1_IO, 0x10); | ||
2961 | mdelay(75); | ||
2962 | cx_set(MO_GP1_IO, 0x10); | ||
2963 | mdelay(75); | ||
2964 | return 0; | ||
2965 | } | ||
2966 | return -EINVAL; | ||
2967 | } | ||
2968 | |||
2969 | /* ------------------------------------------------------------------- */ | ||
2970 | /* some Divco specific stuff */ | ||
2971 | static int cx88_pv_8000gt_callback(struct cx88_core *core, | ||
2972 | int command, int arg) | ||
2973 | { | ||
2974 | switch (command) { | ||
2975 | case XC2028_TUNER_RESET: | ||
2976 | cx_write(MO_GP2_IO, 0xcf7); | ||
2977 | mdelay(50); | ||
2978 | cx_write(MO_GP2_IO, 0xef5); | ||
2979 | mdelay(50); | ||
2980 | cx_write(MO_GP2_IO, 0xcf7); | ||
2981 | break; | ||
2982 | default: | ||
2983 | return -EINVAL; | ||
2984 | } | ||
2985 | |||
2986 | return 0; | ||
2987 | } | ||
2988 | |||
2989 | /* ----------------------------------------------------------------------- */ | ||
2990 | /* some DViCO specific stuff */ | ||
2991 | |||
2992 | static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core) | ||
2993 | { | ||
2994 | struct i2c_msg msg = { .addr = 0x45, .flags = 0 }; | ||
2995 | int i, err; | ||
2996 | static u8 init_bufs[13][5] = { | ||
2997 | { 0x10, 0x00, 0x20, 0x01, 0x03 }, | ||
2998 | { 0x10, 0x10, 0x01, 0x00, 0x21 }, | ||
2999 | { 0x10, 0x10, 0x10, 0x00, 0xCA }, | ||
3000 | { 0x10, 0x10, 0x12, 0x00, 0x08 }, | ||
3001 | { 0x10, 0x10, 0x13, 0x00, 0x0A }, | ||
3002 | { 0x10, 0x10, 0x16, 0x01, 0xC0 }, | ||
3003 | { 0x10, 0x10, 0x22, 0x01, 0x3D }, | ||
3004 | { 0x10, 0x10, 0x73, 0x01, 0x2E }, | ||
3005 | { 0x10, 0x10, 0x72, 0x00, 0xC5 }, | ||
3006 | { 0x10, 0x10, 0x71, 0x01, 0x97 }, | ||
3007 | { 0x10, 0x10, 0x70, 0x00, 0x0F }, | ||
3008 | { 0x10, 0x10, 0xB0, 0x00, 0x01 }, | ||
3009 | { 0x03, 0x0C }, | ||
3010 | }; | ||
3011 | |||
3012 | for (i = 0; i < ARRAY_SIZE(init_bufs); i++) { | ||
3013 | msg.buf = init_bufs[i]; | ||
3014 | msg.len = (i != 12 ? 5 : 2); | ||
3015 | err = i2c_transfer(&core->i2c_adap, &msg, 1); | ||
3016 | if (err != 1) { | ||
3017 | warn_printk(core, "dvico_fusionhdtv_hybrid_init buf %d " | ||
3018 | "failed (err = %d)!\n", i, err); | ||
3019 | return; | ||
3020 | } | ||
3021 | } | ||
3022 | } | ||
3023 | |||
3024 | static int cx88_xc2028_tuner_callback(struct cx88_core *core, | ||
3025 | int command, int arg) | ||
3026 | { | ||
3027 | /* Board-specific callbacks */ | ||
3028 | switch (core->boardnr) { | ||
3029 | case CX88_BOARD_POWERCOLOR_REAL_ANGEL: | ||
3030 | case CX88_BOARD_GENIATECH_X8000_MT: | ||
3031 | case CX88_BOARD_KWORLD_ATSC_120: | ||
3032 | return cx88_xc3028_geniatech_tuner_callback(core, | ||
3033 | command, arg); | ||
3034 | case CX88_BOARD_PROLINK_PV_8000GT: | ||
3035 | case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: | ||
3036 | return cx88_pv_8000gt_callback(core, command, arg); | ||
3037 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: | ||
3038 | case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: | ||
3039 | return cx88_dvico_xc2028_callback(core, command, arg); | ||
3040 | case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: | ||
3041 | case CX88_BOARD_WINFAST_DTV1800H: | ||
3042 | return cx88_xc3028_winfast1800h_callback(core, command, arg); | ||
3043 | } | ||
3044 | |||
3045 | switch (command) { | ||
3046 | case XC2028_TUNER_RESET: | ||
3047 | switch (INPUT(core->input).type) { | ||
3048 | case CX88_RADIO: | ||
3049 | info_printk(core, "setting GPIO to radio!\n"); | ||
3050 | cx_write(MO_GP0_IO, 0x4ff); | ||
3051 | mdelay(250); | ||
3052 | cx_write(MO_GP2_IO, 0xff); | ||
3053 | mdelay(250); | ||
3054 | break; | ||
3055 | case CX88_VMUX_DVB: /* Digital TV*/ | ||
3056 | default: /* Analog TV */ | ||
3057 | info_printk(core, "setting GPIO to TV!\n"); | ||
3058 | break; | ||
3059 | } | ||
3060 | cx_write(MO_GP1_IO, 0x101010); | ||
3061 | mdelay(250); | ||
3062 | cx_write(MO_GP1_IO, 0x101000); | ||
3063 | mdelay(250); | ||
3064 | cx_write(MO_GP1_IO, 0x101010); | ||
3065 | mdelay(250); | ||
3066 | return 0; | ||
3067 | } | ||
3068 | return -EINVAL; | ||
3069 | } | ||
3070 | |||
3071 | static int cx88_xc4000_tuner_callback(struct cx88_core *core, | ||
3072 | int command, int arg) | ||
3073 | { | ||
3074 | /* Board-specific callbacks */ | ||
3075 | switch (core->boardnr) { | ||
3076 | case CX88_BOARD_WINFAST_DTV1800H_XC4000: | ||
3077 | case CX88_BOARD_WINFAST_DTV2000H_PLUS: | ||
3078 | return cx88_xc4000_winfast2000h_plus_callback(core, | ||
3079 | command, arg); | ||
3080 | } | ||
3081 | return -EINVAL; | ||
3082 | } | ||
3083 | |||
3084 | /* ----------------------------------------------------------------------- */ | ||
3085 | /* Tuner callback function. Currently only needed for the Pinnacle * | ||
3086 | * PCTV HD 800i with an xc5000 sillicon tuner. This is used for both * | ||
3087 | * analog tuner attach (tuner-core.c) and dvb tuner attach (cx88-dvb.c) */ | ||
3088 | |||
3089 | static int cx88_xc5000_tuner_callback(struct cx88_core *core, | ||
3090 | int command, int arg) | ||
3091 | { | ||
3092 | switch (core->boardnr) { | ||
3093 | case CX88_BOARD_PINNACLE_PCTV_HD_800i: | ||
3094 | if (command == 0) { /* This is the reset command from xc5000 */ | ||
3095 | |||
3096 | /* djh - According to the engineer at PCTV Systems, | ||
3097 | the xc5000 reset pin is supposed to be on GPIO12. | ||
3098 | However, despite three nights of effort, pulling | ||
3099 | that GPIO low didn't reset the xc5000. While | ||
3100 | pulling MO_SRST_IO low does reset the xc5000, this | ||
3101 | also resets in the s5h1409 being reset as well. | ||
3102 | This causes tuning to always fail since the internal | ||
3103 | state of the s5h1409 does not match the driver's | ||
3104 | state. Given that the only two conditions in which | ||
3105 | the driver performs a reset is during firmware load | ||
3106 | and powering down the chip, I am taking out the | ||
3107 | reset. We know that the chip is being reset | ||
3108 | when the cx88 comes online, and not being able to | ||
3109 | do power management for this board is worse than | ||
3110 | not having any tuning at all. */ | ||
3111 | return 0; | ||
3112 | } else { | ||
3113 | err_printk(core, "xc5000: unknown tuner " | ||
3114 | "callback command.\n"); | ||
3115 | return -EINVAL; | ||
3116 | } | ||
3117 | break; | ||
3118 | case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: | ||
3119 | if (command == 0) { /* This is the reset command from xc5000 */ | ||
3120 | cx_clear(MO_GP0_IO, 0x00000010); | ||
3121 | msleep(10); | ||
3122 | cx_set(MO_GP0_IO, 0x00000010); | ||
3123 | return 0; | ||
3124 | } else { | ||
3125 | printk(KERN_ERR | ||
3126 | "xc5000: unknown tuner callback command.\n"); | ||
3127 | return -EINVAL; | ||
3128 | } | ||
3129 | break; | ||
3130 | } | ||
3131 | return 0; /* Should never be here */ | ||
3132 | } | ||
3133 | |||
3134 | int cx88_tuner_callback(void *priv, int component, int command, int arg) | ||
3135 | { | ||
3136 | struct i2c_algo_bit_data *i2c_algo = priv; | ||
3137 | struct cx88_core *core; | ||
3138 | |||
3139 | if (!i2c_algo) { | ||
3140 | printk(KERN_ERR "cx88: Error - i2c private data undefined.\n"); | ||
3141 | return -EINVAL; | ||
3142 | } | ||
3143 | |||
3144 | core = i2c_algo->data; | ||
3145 | |||
3146 | if (!core) { | ||
3147 | printk(KERN_ERR "cx88: Error - device struct undefined.\n"); | ||
3148 | return -EINVAL; | ||
3149 | } | ||
3150 | |||
3151 | if (component != DVB_FRONTEND_COMPONENT_TUNER) | ||
3152 | return -EINVAL; | ||
3153 | |||
3154 | switch (core->board.tuner_type) { | ||
3155 | case TUNER_XC2028: | ||
3156 | info_printk(core, "Calling XC2028/3028 callback\n"); | ||
3157 | return cx88_xc2028_tuner_callback(core, command, arg); | ||
3158 | case TUNER_XC4000: | ||
3159 | info_printk(core, "Calling XC4000 callback\n"); | ||
3160 | return cx88_xc4000_tuner_callback(core, command, arg); | ||
3161 | case TUNER_XC5000: | ||
3162 | info_printk(core, "Calling XC5000 callback\n"); | ||
3163 | return cx88_xc5000_tuner_callback(core, command, arg); | ||
3164 | } | ||
3165 | err_printk(core, "Error: Calling callback for tuner %d\n", | ||
3166 | core->board.tuner_type); | ||
3167 | return -EINVAL; | ||
3168 | } | ||
3169 | EXPORT_SYMBOL(cx88_tuner_callback); | ||
3170 | |||
3171 | /* ----------------------------------------------------------------------- */ | ||
3172 | |||
3173 | static void cx88_card_list(struct cx88_core *core, struct pci_dev *pci) | ||
3174 | { | ||
3175 | int i; | ||
3176 | |||
3177 | if (0 == pci->subsystem_vendor && | ||
3178 | 0 == pci->subsystem_device) { | ||
3179 | printk(KERN_ERR | ||
3180 | "%s: Your board has no valid PCI Subsystem ID and thus can't\n" | ||
3181 | "%s: be autodetected. Please pass card=<n> insmod option to\n" | ||
3182 | "%s: workaround that. Redirect complaints to the vendor of\n" | ||
3183 | "%s: the TV card. Best regards,\n" | ||
3184 | "%s: -- tux\n", | ||
3185 | core->name,core->name,core->name,core->name,core->name); | ||
3186 | } else { | ||
3187 | printk(KERN_ERR | ||
3188 | "%s: Your board isn't known (yet) to the driver. You can\n" | ||
3189 | "%s: try to pick one of the existing card configs via\n" | ||
3190 | "%s: card=<n> insmod option. Updating to the latest\n" | ||
3191 | "%s: version might help as well.\n", | ||
3192 | core->name,core->name,core->name,core->name); | ||
3193 | } | ||
3194 | err_printk(core, "Here is a list of valid choices for the card=<n> " | ||
3195 | "insmod option:\n"); | ||
3196 | for (i = 0; i < ARRAY_SIZE(cx88_boards); i++) | ||
3197 | printk(KERN_ERR "%s: card=%d -> %s\n", | ||
3198 | core->name, i, cx88_boards[i].name); | ||
3199 | } | ||
3200 | |||
3201 | static void cx88_card_setup_pre_i2c(struct cx88_core *core) | ||
3202 | { | ||
3203 | switch (core->boardnr) { | ||
3204 | case CX88_BOARD_HAUPPAUGE_HVR1300: | ||
3205 | /* | ||
3206 | * Bring the 702 demod up before i2c scanning/attach or devices are hidden | ||
3207 | * We leave here with the 702 on the bus | ||
3208 | * | ||
3209 | * "reset the IR receiver on GPIO[3]" | ||
3210 | * Reported by Mike Crash <mike AT mikecrash.com> | ||
3211 | */ | ||
3212 | cx_write(MO_GP0_IO, 0x0000ef88); | ||
3213 | udelay(1000); | ||
3214 | cx_clear(MO_GP0_IO, 0x00000088); | ||
3215 | udelay(50); | ||
3216 | cx_set(MO_GP0_IO, 0x00000088); /* 702 out of reset */ | ||
3217 | udelay(1000); | ||
3218 | break; | ||
3219 | |||
3220 | case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: | ||
3221 | case CX88_BOARD_PROLINK_PV_8000GT: | ||
3222 | cx_write(MO_GP2_IO, 0xcf7); | ||
3223 | mdelay(50); | ||
3224 | cx_write(MO_GP2_IO, 0xef5); | ||
3225 | mdelay(50); | ||
3226 | cx_write(MO_GP2_IO, 0xcf7); | ||
3227 | msleep(10); | ||
3228 | break; | ||
3229 | |||
3230 | case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: | ||
3231 | /* Enable the xc5000 tuner */ | ||
3232 | cx_set(MO_GP0_IO, 0x00001010); | ||
3233 | break; | ||
3234 | |||
3235 | case CX88_BOARD_HAUPPAUGE_HVR3000: | ||
3236 | case CX88_BOARD_HAUPPAUGE_HVR4000: | ||
3237 | /* Init GPIO */ | ||
3238 | cx_write(MO_GP0_IO, core->board.input[0].gpio0); | ||
3239 | udelay(1000); | ||
3240 | cx_clear(MO_GP0_IO, 0x00000080); | ||
3241 | udelay(50); | ||
3242 | cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */ | ||
3243 | udelay(1000); | ||
3244 | break; | ||
3245 | |||
3246 | case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: | ||
3247 | case CX88_BOARD_WINFAST_DTV1800H: | ||
3248 | cx88_xc3028_winfast1800h_callback(core, XC2028_TUNER_RESET, 0); | ||
3249 | break; | ||
3250 | |||
3251 | case CX88_BOARD_WINFAST_DTV1800H_XC4000: | ||
3252 | case CX88_BOARD_WINFAST_DTV2000H_PLUS: | ||
3253 | cx88_xc4000_winfast2000h_plus_callback(core, | ||
3254 | XC4000_TUNER_RESET, 0); | ||
3255 | break; | ||
3256 | |||
3257 | case CX88_BOARD_TWINHAN_VP1027_DVBS: | ||
3258 | cx_write(MO_GP0_IO, 0x00003230); | ||
3259 | cx_write(MO_GP0_IO, 0x00003210); | ||
3260 | msleep(1); | ||
3261 | cx_write(MO_GP0_IO, 0x00001230); | ||
3262 | break; | ||
3263 | } | ||
3264 | } | ||
3265 | |||
3266 | /* | ||
3267 | * Sets board-dependent xc3028 configuration | ||
3268 | */ | ||
3269 | void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) | ||
3270 | { | ||
3271 | memset(ctl, 0, sizeof(*ctl)); | ||
3272 | |||
3273 | ctl->fname = XC2028_DEFAULT_FIRMWARE; | ||
3274 | ctl->max_len = 64; | ||
3275 | |||
3276 | switch (core->boardnr) { | ||
3277 | case CX88_BOARD_POWERCOLOR_REAL_ANGEL: | ||
3278 | /* Now works with firmware version 2.7 */ | ||
3279 | if (core->i2c_algo.udelay < 16) | ||
3280 | core->i2c_algo.udelay = 16; | ||
3281 | break; | ||
3282 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: | ||
3283 | case CX88_BOARD_WINFAST_DTV1800H: | ||
3284 | ctl->demod = XC3028_FE_ZARLINK456; | ||
3285 | break; | ||
3286 | case CX88_BOARD_KWORLD_ATSC_120: | ||
3287 | case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: | ||
3288 | ctl->demod = XC3028_FE_OREN538; | ||
3289 | break; | ||
3290 | case CX88_BOARD_GENIATECH_X8000_MT: | ||
3291 | /* FIXME: For this board, the xc3028 never recovers after being | ||
3292 | powered down (the reset GPIO probably is not set properly). | ||
3293 | We don't have access to the hardware so we cannot determine | ||
3294 | which GPIO is used for xc3028, so just disable power xc3028 | ||
3295 | power management for now */ | ||
3296 | ctl->disable_power_mgmt = 1; | ||
3297 | break; | ||
3298 | case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: | ||
3299 | case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: | ||
3300 | case CX88_BOARD_PROLINK_PV_8000GT: | ||
3301 | /* | ||
3302 | * Those boards uses non-MTS firmware | ||
3303 | */ | ||
3304 | break; | ||
3305 | case CX88_BOARD_PINNACLE_HYBRID_PCTV: | ||
3306 | case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: | ||
3307 | ctl->demod = XC3028_FE_ZARLINK456; | ||
3308 | ctl->mts = 1; | ||
3309 | break; | ||
3310 | default: | ||
3311 | ctl->demod = XC3028_FE_OREN538; | ||
3312 | ctl->mts = 1; | ||
3313 | } | ||
3314 | } | ||
3315 | EXPORT_SYMBOL_GPL(cx88_setup_xc3028); | ||
3316 | |||
3317 | static void cx88_card_setup(struct cx88_core *core) | ||
3318 | { | ||
3319 | static u8 eeprom[256]; | ||
3320 | struct tuner_setup tun_setup; | ||
3321 | unsigned int mode_mask = T_RADIO | T_ANALOG_TV; | ||
3322 | |||
3323 | memset(&tun_setup, 0, sizeof(tun_setup)); | ||
3324 | |||
3325 | if (0 == core->i2c_rc) { | ||
3326 | core->i2c_client.addr = 0xa0 >> 1; | ||
3327 | tveeprom_read(&core->i2c_client, eeprom, sizeof(eeprom)); | ||
3328 | } | ||
3329 | |||
3330 | switch (core->boardnr) { | ||
3331 | case CX88_BOARD_HAUPPAUGE: | ||
3332 | case CX88_BOARD_HAUPPAUGE_ROSLYN: | ||
3333 | if (0 == core->i2c_rc) | ||
3334 | hauppauge_eeprom(core, eeprom+8); | ||
3335 | break; | ||
3336 | case CX88_BOARD_GDI: | ||
3337 | if (0 == core->i2c_rc) | ||
3338 | gdi_eeprom(core, eeprom); | ||
3339 | break; | ||
3340 | case CX88_BOARD_LEADTEK_PVR2000: | ||
3341 | case CX88_BOARD_WINFAST_DV2000: | ||
3342 | case CX88_BOARD_WINFAST2000XP_EXPERT: | ||
3343 | if (0 == core->i2c_rc) | ||
3344 | leadtek_eeprom(core, eeprom); | ||
3345 | break; | ||
3346 | case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: | ||
3347 | case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: | ||
3348 | case CX88_BOARD_HAUPPAUGE_DVB_T1: | ||
3349 | case CX88_BOARD_HAUPPAUGE_HVR1100: | ||
3350 | case CX88_BOARD_HAUPPAUGE_HVR1100LP: | ||
3351 | case CX88_BOARD_HAUPPAUGE_HVR3000: | ||
3352 | case CX88_BOARD_HAUPPAUGE_HVR1300: | ||
3353 | case CX88_BOARD_HAUPPAUGE_HVR4000: | ||
3354 | case CX88_BOARD_HAUPPAUGE_HVR4000LITE: | ||
3355 | case CX88_BOARD_HAUPPAUGE_IRONLY: | ||
3356 | if (0 == core->i2c_rc) | ||
3357 | hauppauge_eeprom(core, eeprom); | ||
3358 | break; | ||
3359 | case CX88_BOARD_KWORLD_DVBS_100: | ||
3360 | cx_write(MO_GP0_IO, 0x000007f8); | ||
3361 | cx_write(MO_GP1_IO, 0x00000001); | ||
3362 | break; | ||
3363 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: | ||
3364 | /* GPIO0:0 is hooked to demod reset */ | ||
3365 | /* GPIO0:4 is hooked to xc3028 reset */ | ||
3366 | cx_write(MO_GP0_IO, 0x00111100); | ||
3367 | msleep(1); | ||
3368 | cx_write(MO_GP0_IO, 0x00111111); | ||
3369 | break; | ||
3370 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: | ||
3371 | /* GPIO0:6 is hooked to FX2 reset pin */ | ||
3372 | cx_set(MO_GP0_IO, 0x00004040); | ||
3373 | cx_clear(MO_GP0_IO, 0x00000040); | ||
3374 | msleep(1000); | ||
3375 | cx_set(MO_GP0_IO, 0x00004040); | ||
3376 | /* FALLTHROUGH */ | ||
3377 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: | ||
3378 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: | ||
3379 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID: | ||
3380 | /* GPIO0:0 is hooked to mt352 reset pin */ | ||
3381 | cx_set(MO_GP0_IO, 0x00000101); | ||
3382 | cx_clear(MO_GP0_IO, 0x00000001); | ||
3383 | msleep(1); | ||
3384 | cx_set(MO_GP0_IO, 0x00000101); | ||
3385 | if (0 == core->i2c_rc && | ||
3386 | core->boardnr == CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID) | ||
3387 | dvico_fusionhdtv_hybrid_init(core); | ||
3388 | break; | ||
3389 | case CX88_BOARD_KWORLD_DVB_T: | ||
3390 | case CX88_BOARD_DNTV_LIVE_DVB_T: | ||
3391 | cx_set(MO_GP0_IO, 0x00000707); | ||
3392 | cx_set(MO_GP2_IO, 0x00000101); | ||
3393 | cx_clear(MO_GP2_IO, 0x00000001); | ||
3394 | msleep(1); | ||
3395 | cx_clear(MO_GP0_IO, 0x00000007); | ||
3396 | cx_set(MO_GP2_IO, 0x00000101); | ||
3397 | break; | ||
3398 | case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: | ||
3399 | cx_write(MO_GP0_IO, 0x00080808); | ||
3400 | break; | ||
3401 | case CX88_BOARD_ATI_HDTVWONDER: | ||
3402 | if (0 == core->i2c_rc) { | ||
3403 | /* enable tuner */ | ||
3404 | int i; | ||
3405 | static const u8 buffer [][2] = { | ||
3406 | {0x10,0x12}, | ||
3407 | {0x13,0x04}, | ||
3408 | {0x16,0x00}, | ||
3409 | {0x14,0x04}, | ||
3410 | {0x17,0x00} | ||
3411 | }; | ||
3412 | core->i2c_client.addr = 0x0a; | ||
3413 | |||
3414 | for (i = 0; i < ARRAY_SIZE(buffer); i++) | ||
3415 | if (2 != i2c_master_send(&core->i2c_client, | ||
3416 | buffer[i],2)) | ||
3417 | warn_printk(core, "Unable to enable " | ||
3418 | "tuner(%i).\n", i); | ||
3419 | } | ||
3420 | break; | ||
3421 | case CX88_BOARD_MSI_TVANYWHERE_MASTER: | ||
3422 | { | ||
3423 | struct v4l2_priv_tun_config tea5767_cfg; | ||
3424 | struct tea5767_ctrl ctl; | ||
3425 | |||
3426 | memset(&ctl, 0, sizeof(ctl)); | ||
3427 | |||
3428 | ctl.high_cut = 1; | ||
3429 | ctl.st_noise = 1; | ||
3430 | ctl.deemph_75 = 1; | ||
3431 | ctl.xtal_freq = TEA5767_HIGH_LO_13MHz; | ||
3432 | |||
3433 | tea5767_cfg.tuner = TUNER_TEA5767; | ||
3434 | tea5767_cfg.priv = &ctl; | ||
3435 | |||
3436 | call_all(core, tuner, s_config, &tea5767_cfg); | ||
3437 | break; | ||
3438 | } | ||
3439 | case CX88_BOARD_TEVII_S420: | ||
3440 | case CX88_BOARD_TEVII_S460: | ||
3441 | case CX88_BOARD_TEVII_S464: | ||
3442 | case CX88_BOARD_OMICOM_SS4_PCI: | ||
3443 | case CX88_BOARD_TBS_8910: | ||
3444 | case CX88_BOARD_TBS_8920: | ||
3445 | case CX88_BOARD_PROF_6200: | ||
3446 | case CX88_BOARD_PROF_7300: | ||
3447 | case CX88_BOARD_PROF_7301: | ||
3448 | case CX88_BOARD_SATTRADE_ST4200: | ||
3449 | cx_write(MO_GP0_IO, 0x8000); | ||
3450 | msleep(100); | ||
3451 | cx_write(MO_SRST_IO, 0); | ||
3452 | msleep(10); | ||
3453 | cx_write(MO_GP0_IO, 0x8080); | ||
3454 | msleep(100); | ||
3455 | cx_write(MO_SRST_IO, 1); | ||
3456 | msleep(100); | ||
3457 | break; | ||
3458 | } /*end switch() */ | ||
3459 | |||
3460 | |||
3461 | /* Setup tuners */ | ||
3462 | if ((core->board.radio_type != UNSET)) { | ||
3463 | tun_setup.mode_mask = T_RADIO; | ||
3464 | tun_setup.type = core->board.radio_type; | ||
3465 | tun_setup.addr = core->board.radio_addr; | ||
3466 | tun_setup.tuner_callback = cx88_tuner_callback; | ||
3467 | call_all(core, tuner, s_type_addr, &tun_setup); | ||
3468 | mode_mask &= ~T_RADIO; | ||
3469 | } | ||
3470 | |||
3471 | if (core->board.tuner_type != TUNER_ABSENT) { | ||
3472 | tun_setup.mode_mask = mode_mask; | ||
3473 | tun_setup.type = core->board.tuner_type; | ||
3474 | tun_setup.addr = core->board.tuner_addr; | ||
3475 | tun_setup.tuner_callback = cx88_tuner_callback; | ||
3476 | |||
3477 | call_all(core, tuner, s_type_addr, &tun_setup); | ||
3478 | } | ||
3479 | |||
3480 | if (core->board.tda9887_conf) { | ||
3481 | struct v4l2_priv_tun_config tda9887_cfg; | ||
3482 | |||
3483 | tda9887_cfg.tuner = TUNER_TDA9887; | ||
3484 | tda9887_cfg.priv = &core->board.tda9887_conf; | ||
3485 | |||
3486 | call_all(core, tuner, s_config, &tda9887_cfg); | ||
3487 | } | ||
3488 | |||
3489 | if (core->board.tuner_type == TUNER_XC2028) { | ||
3490 | struct v4l2_priv_tun_config xc2028_cfg; | ||
3491 | struct xc2028_ctrl ctl; | ||
3492 | |||
3493 | /* Fills device-dependent initialization parameters */ | ||
3494 | cx88_setup_xc3028(core, &ctl); | ||
3495 | |||
3496 | /* Sends parameters to xc2028/3028 tuner */ | ||
3497 | memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); | ||
3498 | xc2028_cfg.tuner = TUNER_XC2028; | ||
3499 | xc2028_cfg.priv = &ctl; | ||
3500 | info_printk(core, "Asking xc2028/3028 to load firmware %s\n", | ||
3501 | ctl.fname); | ||
3502 | call_all(core, tuner, s_config, &xc2028_cfg); | ||
3503 | } | ||
3504 | call_all(core, core, s_power, 0); | ||
3505 | } | ||
3506 | |||
3507 | /* ------------------------------------------------------------------ */ | ||
3508 | |||
3509 | static int cx88_pci_quirks(const char *name, struct pci_dev *pci) | ||
3510 | { | ||
3511 | unsigned int lat = UNSET; | ||
3512 | u8 ctrl = 0; | ||
3513 | u8 value; | ||
3514 | |||
3515 | /* check pci quirks */ | ||
3516 | if (pci_pci_problems & PCIPCI_TRITON) { | ||
3517 | printk(KERN_INFO "%s: quirk: PCIPCI_TRITON -- set TBFX\n", | ||
3518 | name); | ||
3519 | ctrl |= CX88X_EN_TBFX; | ||
3520 | } | ||
3521 | if (pci_pci_problems & PCIPCI_NATOMA) { | ||
3522 | printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA -- set TBFX\n", | ||
3523 | name); | ||
3524 | ctrl |= CX88X_EN_TBFX; | ||
3525 | } | ||
3526 | if (pci_pci_problems & PCIPCI_VIAETBF) { | ||
3527 | printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF -- set TBFX\n", | ||
3528 | name); | ||
3529 | ctrl |= CX88X_EN_TBFX; | ||
3530 | } | ||
3531 | if (pci_pci_problems & PCIPCI_VSFX) { | ||
3532 | printk(KERN_INFO "%s: quirk: PCIPCI_VSFX -- set VSFX\n", | ||
3533 | name); | ||
3534 | ctrl |= CX88X_EN_VSFX; | ||
3535 | } | ||
3536 | #ifdef PCIPCI_ALIMAGIK | ||
3537 | if (pci_pci_problems & PCIPCI_ALIMAGIK) { | ||
3538 | printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n", | ||
3539 | name); | ||
3540 | lat = 0x0A; | ||
3541 | } | ||
3542 | #endif | ||
3543 | |||
3544 | /* check insmod options */ | ||
3545 | if (UNSET != latency) | ||
3546 | lat = latency; | ||
3547 | |||
3548 | /* apply stuff */ | ||
3549 | if (ctrl) { | ||
3550 | pci_read_config_byte(pci, CX88X_DEVCTRL, &value); | ||
3551 | value |= ctrl; | ||
3552 | pci_write_config_byte(pci, CX88X_DEVCTRL, value); | ||
3553 | } | ||
3554 | if (UNSET != lat) { | ||
3555 | printk(KERN_INFO "%s: setting pci latency timer to %d\n", | ||
3556 | name, latency); | ||
3557 | pci_write_config_byte(pci, PCI_LATENCY_TIMER, latency); | ||
3558 | } | ||
3559 | return 0; | ||
3560 | } | ||
3561 | |||
3562 | int cx88_get_resources(const struct cx88_core *core, struct pci_dev *pci) | ||
3563 | { | ||
3564 | if (request_mem_region(pci_resource_start(pci,0), | ||
3565 | pci_resource_len(pci,0), | ||
3566 | core->name)) | ||
3567 | return 0; | ||
3568 | printk(KERN_ERR | ||
3569 | "%s/%d: Can't get MMIO memory @ 0x%llx, subsystem: %04x:%04x\n", | ||
3570 | core->name, PCI_FUNC(pci->devfn), | ||
3571 | (unsigned long long)pci_resource_start(pci, 0), | ||
3572 | pci->subsystem_vendor, pci->subsystem_device); | ||
3573 | return -EBUSY; | ||
3574 | } | ||
3575 | |||
3576 | /* Allocate and initialize the cx88 core struct. One should hold the | ||
3577 | * devlist mutex before calling this. */ | ||
3578 | struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) | ||
3579 | { | ||
3580 | struct cx88_core *core; | ||
3581 | int i; | ||
3582 | |||
3583 | core = kzalloc(sizeof(*core), GFP_KERNEL); | ||
3584 | if (core == NULL) | ||
3585 | return NULL; | ||
3586 | |||
3587 | atomic_inc(&core->refcount); | ||
3588 | core->pci_bus = pci->bus->number; | ||
3589 | core->pci_slot = PCI_SLOT(pci->devfn); | ||
3590 | core->pci_irqmask = PCI_INT_RISC_RD_BERRINT | PCI_INT_RISC_WR_BERRINT | | ||
3591 | PCI_INT_BRDG_BERRINT | PCI_INT_SRC_DMA_BERRINT | | ||
3592 | PCI_INT_DST_DMA_BERRINT | PCI_INT_IPB_DMA_BERRINT; | ||
3593 | mutex_init(&core->lock); | ||
3594 | |||
3595 | core->nr = nr; | ||
3596 | sprintf(core->name, "cx88[%d]", core->nr); | ||
3597 | |||
3598 | strcpy(core->v4l2_dev.name, core->name); | ||
3599 | if (v4l2_device_register(NULL, &core->v4l2_dev)) { | ||
3600 | kfree(core); | ||
3601 | return NULL; | ||
3602 | } | ||
3603 | |||
3604 | if (0 != cx88_get_resources(core, pci)) { | ||
3605 | v4l2_device_unregister(&core->v4l2_dev); | ||
3606 | kfree(core); | ||
3607 | return NULL; | ||
3608 | } | ||
3609 | |||
3610 | /* PCI stuff */ | ||
3611 | cx88_pci_quirks(core->name, pci); | ||
3612 | core->lmmio = ioremap(pci_resource_start(pci, 0), | ||
3613 | pci_resource_len(pci, 0)); | ||
3614 | core->bmmio = (u8 __iomem *)core->lmmio; | ||
3615 | |||
3616 | if (core->lmmio == NULL) { | ||
3617 | kfree(core); | ||
3618 | return NULL; | ||
3619 | } | ||
3620 | |||
3621 | /* board config */ | ||
3622 | core->boardnr = UNSET; | ||
3623 | if (card[core->nr] < ARRAY_SIZE(cx88_boards)) | ||
3624 | core->boardnr = card[core->nr]; | ||
3625 | for (i = 0; UNSET == core->boardnr && i < ARRAY_SIZE(cx88_subids); i++) | ||
3626 | if (pci->subsystem_vendor == cx88_subids[i].subvendor && | ||
3627 | pci->subsystem_device == cx88_subids[i].subdevice) | ||
3628 | core->boardnr = cx88_subids[i].card; | ||
3629 | if (UNSET == core->boardnr) { | ||
3630 | core->boardnr = CX88_BOARD_UNKNOWN; | ||
3631 | cx88_card_list(core, pci); | ||
3632 | } | ||
3633 | |||
3634 | memcpy(&core->board, &cx88_boards[core->boardnr], sizeof(core->board)); | ||
3635 | |||
3636 | if (!core->board.num_frontends && (core->board.mpeg & CX88_MPEG_DVB)) | ||
3637 | core->board.num_frontends = 1; | ||
3638 | |||
3639 | info_printk(core, "subsystem: %04x:%04x, board: %s [card=%d,%s], frontend(s): %d\n", | ||
3640 | pci->subsystem_vendor, pci->subsystem_device, core->board.name, | ||
3641 | core->boardnr, card[core->nr] == core->boardnr ? | ||
3642 | "insmod option" : "autodetected", | ||
3643 | core->board.num_frontends); | ||
3644 | |||
3645 | if (tuner[core->nr] != UNSET) | ||
3646 | core->board.tuner_type = tuner[core->nr]; | ||
3647 | if (radio[core->nr] != UNSET) | ||
3648 | core->board.radio_type = radio[core->nr]; | ||
3649 | |||
3650 | info_printk(core, "TV tuner type %d, Radio tuner type %d\n", | ||
3651 | core->board.tuner_type, core->board.radio_type); | ||
3652 | |||
3653 | /* init hardware */ | ||
3654 | cx88_reset(core); | ||
3655 | cx88_card_setup_pre_i2c(core); | ||
3656 | cx88_i2c_init(core, pci); | ||
3657 | |||
3658 | /* load tuner module, if needed */ | ||
3659 | if (TUNER_ABSENT != core->board.tuner_type) { | ||
3660 | /* Ignore 0x6b and 0x6f on cx88 boards. | ||
3661 | * FusionHDTV5 RT Gold has an ir receiver at 0x6b | ||
3662 | * and an RTC at 0x6f which can get corrupted if probed. */ | ||
3663 | static const unsigned short tv_addrs[] = { | ||
3664 | 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ | ||
3665 | 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, | ||
3666 | 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, | ||
3667 | I2C_CLIENT_END | ||
3668 | }; | ||
3669 | int has_demod = (core->board.tda9887_conf & TDA9887_PRESENT); | ||
3670 | |||
3671 | /* I don't trust the radio_type as is stored in the card | ||
3672 | definitions, so we just probe for it. | ||
3673 | The radio_type is sometimes missing, or set to UNSET but | ||
3674 | later code configures a tea5767. | ||
3675 | */ | ||
3676 | v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, | ||
3677 | "tuner", 0, v4l2_i2c_tuner_addrs(ADDRS_RADIO)); | ||
3678 | if (has_demod) | ||
3679 | v4l2_i2c_new_subdev(&core->v4l2_dev, | ||
3680 | &core->i2c_adap, "tuner", | ||
3681 | 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); | ||
3682 | if (core->board.tuner_addr == ADDR_UNSET) { | ||
3683 | v4l2_i2c_new_subdev(&core->v4l2_dev, | ||
3684 | &core->i2c_adap, "tuner", | ||
3685 | 0, has_demod ? tv_addrs + 4 : tv_addrs); | ||
3686 | } else { | ||
3687 | v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, | ||
3688 | "tuner", core->board.tuner_addr, NULL); | ||
3689 | } | ||
3690 | } | ||
3691 | |||
3692 | cx88_card_setup(core); | ||
3693 | if (!disable_ir) { | ||
3694 | cx88_i2c_init_ir(core); | ||
3695 | cx88_ir_init(core, pci); | ||
3696 | } | ||
3697 | |||
3698 | return core; | ||
3699 | } | ||
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c new file mode 100644 index 00000000000..fbcaa1c5b09 --- /dev/null +++ b/drivers/media/video/cx88/cx88-core.c | |||
@@ -0,0 +1,1129 @@ | |||
1 | /* | ||
2 | * | ||
3 | * device driver for Conexant 2388x based TV cards | ||
4 | * driver core | ||
5 | * | ||
6 | * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
7 | * | ||
8 | * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> | ||
9 | * - Multituner support | ||
10 | * - video_ioctl2 conversion | ||
11 | * - PAL/M fixes | ||
12 | * | ||
13 | * 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> | ||
31 | #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> | ||
38 | #include <linux/videodev2.h> | ||
39 | #include <linux/mutex.h> | ||
40 | |||
41 | #include "cx88.h" | ||
42 | #include <media/v4l2-common.h> | ||
43 | #include <media/v4l2-ioctl.h> | ||
44 | |||
45 | MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); | ||
46 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
47 | MODULE_LICENSE("GPL"); | ||
48 | |||
49 | /* ------------------------------------------------------------------ */ | ||
50 | |||
51 | static unsigned int core_debug; | ||
52 | module_param(core_debug,int,0644); | ||
53 | MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); | ||
54 | |||
55 | static unsigned int nicam; | ||
56 | module_param(nicam,int,0644); | ||
57 | MODULE_PARM_DESC(nicam,"tv audio is nicam"); | ||
58 | |||
59 | static unsigned int nocomb; | ||
60 | module_param(nocomb,int,0644); | ||
61 | MODULE_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 | |||
66 | static unsigned int cx88_devcount; | ||
67 | static LIST_HEAD(cx88_devlist); | ||
68 | static DEFINE_MUTEX(devlist); | ||
69 | |||
70 | #define NO_SYNC_LINE (-1U) | ||
71 | |||
72 | /* @lpi: lines per IRQ, or 0 to not generate irqs. Note: IRQ to be | ||
73 | generated _after_ lpi lines are transferred. */ | ||
74 | static __le32* cx88_risc_field(__le32 *rp, struct scatterlist *sglist, | ||
75 | unsigned int offset, u32 sync_line, | ||
76 | unsigned int bpl, unsigned int padding, | ||
77 | unsigned int lines, unsigned int lpi) | ||
78 | { | ||
79 | struct scatterlist *sg; | ||
80 | unsigned int line,todo,sol; | ||
81 | |||
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 | } | ||
93 | if (lpi && line>0 && !(line % lpi)) | ||
94 | sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC; | ||
95 | else | ||
96 | sol = RISC_SOL; | ||
97 | if (bpl <= sg_dma_len(sg)-offset) { | ||
98 | /* fits into current chunk */ | ||
99 | *(rp++)=cpu_to_le32(RISC_WRITE|sol|RISC_EOL|bpl); | ||
100 | *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); | ||
101 | offset+=bpl; | ||
102 | } else { | ||
103 | /* scanline needs to be split */ | ||
104 | todo = bpl; | ||
105 | *(rp++)=cpu_to_le32(RISC_WRITE|sol| | ||
106 | (sg_dma_len(sg)-offset)); | ||
107 | *(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)) { | ||
112 | *(rp++)=cpu_to_le32(RISC_WRITE| | ||
113 | sg_dma_len(sg)); | ||
114 | *(rp++)=cpu_to_le32(sg_dma_address(sg)); | ||
115 | todo -= sg_dma_len(sg); | ||
116 | sg++; | ||
117 | } | ||
118 | *(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo); | ||
119 | *(rp++)=cpu_to_le32(sg_dma_address(sg)); | ||
120 | offset += todo; | ||
121 | } | ||
122 | offset += padding; | ||
123 | } | ||
124 | |||
125 | return rp; | ||
126 | } | ||
127 | |||
128 | int 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; | ||
134 | __le32 *rp; | ||
135 | 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 + | ||
144 | 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; | ||
149 | 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, | ||
156 | bpl, padding, lines, 0); | ||
157 | if (UNSET != bottom_offset) | ||
158 | rp = cx88_risc_field(rp, sglist, bottom_offset, 0x200, | ||
159 | bpl, padding, lines, 0); | ||
160 | |||
161 | /* save pointer to jmp instruction address */ | ||
162 | risc->jmp = rp; | ||
163 | BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size); | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
168 | struct scatterlist *sglist, unsigned int bpl, | ||
169 | unsigned int lines, unsigned int lpi) | ||
170 | { | ||
171 | u32 instructions; | ||
172 | __le32 *rp; | ||
173 | int rc; | ||
174 | |||
175 | /* estimate risc mem: worst case is one write per page border + | ||
176 | 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; | ||
181 | if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0) | ||
182 | return rc; | ||
183 | |||
184 | /* write risc instructions */ | ||
185 | rp = risc->cpu; | ||
186 | rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines, lpi); | ||
187 | |||
188 | /* save pointer to jmp instruction address */ | ||
189 | risc->jmp = rp; | ||
190 | BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size); | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | int cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
195 | u32 reg, u32 mask, u32 value) | ||
196 | { | ||
197 | __le32 *rp; | ||
198 | 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 | |||
214 | void | ||
215 | cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf) | ||
216 | { | ||
217 | struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); | ||
218 | |||
219 | BUG_ON(in_interrupt()); | ||
220 | videobuf_waiton(q, &buf->vb, 0, 0); | ||
221 | videobuf_dma_unmap(q->dev, dma); | ||
222 | videobuf_dma_free(dma); | ||
223 | btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); | ||
224 | buf->vb.state = VIDEOBUF_NEEDS_INIT; | ||
225 | } | ||
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 enough 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 | ||
242 | * channels 25+26 (audio) - 4.0k | ||
243 | * channel 28 (mpeg) - 4.0k | ||
244 | * channel 27 (audio rds)- 3.0k | ||
245 | * TOTAL = 29.0k | ||
246 | * | ||
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 | |||
256 | const struct sram_channel const cx88_sram_channels[] = { | ||
257 | [SRAM_CH21] = { | ||
258 | .name = "video y / packed", | ||
259 | .cmds_start = 0x180040, | ||
260 | .ctrl_start = 0x180400, | ||
261 | .cdt = 0x180400 + 64, | ||
262 | .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, | ||
273 | .cdt = 0x1804a0 + 64, | ||
274 | .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, | ||
285 | .cdt = 0x180540 + 64, | ||
286 | .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, | ||
297 | .cdt = 0x1805e0 + 64, | ||
298 | .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, | ||
309 | .cdt = 0x180680 + 64, | ||
310 | .fifo_start = 0x185400, | ||
311 | .fifo_size = 0x001000, | ||
312 | .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, | ||
321 | .cdt = 0x180680 + 64, /* same as audio IN */ | ||
322 | .fifo_start = 0x185400, /* same as audio IN */ | ||
323 | .fifo_size = 0x001000, /* same as audio IN */ | ||
324 | .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, | ||
334 | .fifo_start = 0x186400, | ||
335 | .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 | }, | ||
341 | [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 | }, | ||
353 | }; | ||
354 | |||
355 | int cx88_sram_channel_setup(struct cx88_core *core, | ||
356 | const 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 | |||
395 | static int cx88_risc_decode(u32 risc) | ||
396 | { | ||
397 | static const char * const 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 const 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 const char * const 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 | |||
433 | |||
434 | void cx88_sram_channel_dump(struct cx88_core *core, | ||
435 | const struct sram_channel *ch) | ||
436 | { | ||
437 | static const char * const 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)); | ||
459 | for (n = 1, i = 0; i < 4; i++) { | ||
460 | risc = cx_read(ch->cmds_start + 4 * (i+11)); | ||
461 | printk("%s: risc%d: ", core->name, i); | ||
462 | if (--n) | ||
463 | printk("0x%08x [ arg #%d ]\n", risc, n); | ||
464 | else | ||
465 | n = cx88_risc_decode(risc); | ||
466 | } | ||
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 | |||
492 | static const char *cx88_pci_irqs[32] = { | ||
493 | "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 | }; | ||
498 | |||
499 | void cx88_print_irqbits(const char *name, const char *tag, const char *strings[], | ||
500 | int len, u32 bits, u32 mask) | ||
501 | { | ||
502 | unsigned int i; | ||
503 | |||
504 | printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits); | ||
505 | for (i = 0; i < len; i++) { | ||
506 | 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 | |||
521 | int cx88_core_irq(struct cx88_core *core, u32 status) | ||
522 | { | ||
523 | int handled = 0; | ||
524 | |||
525 | if (status & PCI_INT_IR_SMPINT) { | ||
526 | cx88_ir_irq(core); | ||
527 | handled++; | ||
528 | } | ||
529 | if (!handled) | ||
530 | cx88_print_irqbits(core->name, "irq pci", | ||
531 | cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs), | ||
532 | status, core->pci_irqmask); | ||
533 | return handled; | ||
534 | } | ||
535 | |||
536 | void 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); | ||
547 | /* 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; | ||
552 | do_gettimeofday(&buf->vb.ts); | ||
553 | dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i, | ||
554 | count, buf->count); | ||
555 | buf->vb.state = VIDEOBUF_DONE; | ||
556 | 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) | ||
565 | dprintk(2, "%s: %d buffers handled (should be 1)\n", | ||
566 | __func__, bc); | ||
567 | } | ||
568 | |||
569 | void 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 | |||
593 | int cx88_reset(struct cx88_core *core) | ||
594 | { | ||
595 | dprintk(1,"%s\n",__func__); | ||
596 | 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); | ||
614 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], 128, 0); | ||
615 | |||
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 | |||
627 | /* setup FIFO Thresholds */ | ||
628 | 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 | /* set default notch filter */ | ||
640 | cx_andor(MO_HTOTAL, 0x1800, (HLNotchFilter4xFsc << 11)); | ||
641 | |||
642 | /* Reset on-board parts */ | ||
643 | cx_write(MO_SRST_IO, 0); | ||
644 | msleep(10); | ||
645 | cx_write(MO_SRST_IO, 1); | ||
646 | |||
647 | return 0; | ||
648 | } | ||
649 | |||
650 | /* ------------------------------------------------------------------ */ | ||
651 | |||
652 | static unsigned int inline norm_swidth(v4l2_std_id norm) | ||
653 | { | ||
654 | return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922; | ||
655 | } | ||
656 | |||
657 | static unsigned int inline norm_hdelay(v4l2_std_id norm) | ||
658 | { | ||
659 | return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186; | ||
660 | } | ||
661 | |||
662 | static unsigned int inline norm_vdelay(v4l2_std_id norm) | ||
663 | { | ||
664 | return (norm & V4L2_STD_625_50) ? 0x24 : 0x18; | ||
665 | } | ||
666 | |||
667 | static unsigned int inline norm_fsc8(v4l2_std_id norm) | ||
668 | { | ||
669 | if (norm & V4L2_STD_PAL_M) | ||
670 | return 28604892; // 3.575611 MHz | ||
671 | |||
672 | if (norm & (V4L2_STD_PAL_Nc)) | ||
673 | return 28656448; // 3.582056 MHz | ||
674 | |||
675 | if (norm & V4L2_STD_NTSC) // All NTSC/M and variants | ||
676 | return 28636360; // 3.57954545 MHz +/- 10 Hz | ||
677 | |||
678 | /* SECAM have also different sub carrier for chroma, | ||
679 | but step_db and step_dr, at cx88_set_tvnorm already handles that. | ||
680 | |||
681 | The same FSC applies to PAL/BGDKIH, PAL/60, NTSC/4.43 and PAL/N | ||
682 | */ | ||
683 | |||
684 | return 35468950; // 4.43361875 MHz +/- 5 Hz | ||
685 | } | ||
686 | |||
687 | static unsigned int inline norm_htotal(v4l2_std_id norm) | ||
688 | { | ||
689 | |||
690 | unsigned int fsc4=norm_fsc8(norm)/2; | ||
691 | |||
692 | /* returns 4*FSC / vtotal / frames per seconds */ | ||
693 | return (norm & V4L2_STD_625_50) ? | ||
694 | ((fsc4+312)/625+12)/25 : | ||
695 | ((fsc4+262)/525*1001+15000)/30000; | ||
696 | } | ||
697 | |||
698 | static unsigned int inline norm_vbipack(v4l2_std_id norm) | ||
699 | { | ||
700 | return (norm & V4L2_STD_625_50) ? 511 : 400; | ||
701 | } | ||
702 | |||
703 | int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height, | ||
704 | enum v4l2_field field) | ||
705 | { | ||
706 | unsigned int swidth = norm_swidth(core->tvnorm); | ||
707 | unsigned int sheight = norm_maxh(core->tvnorm); | ||
708 | u32 value; | ||
709 | |||
710 | dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height, | ||
711 | V4L2_FIELD_HAS_TOP(field) ? "T" : "", | ||
712 | V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "", | ||
713 | v4l2_norm_to_name(core->tvnorm)); | ||
714 | if (!V4L2_FIELD_HAS_BOTH(field)) | ||
715 | height *= 2; | ||
716 | |||
717 | // recalc H delay and scale registers | ||
718 | value = (width * norm_hdelay(core->tvnorm)) / swidth; | ||
719 | value &= 0x3fe; | ||
720 | cx_write(MO_HDELAY_EVEN, value); | ||
721 | cx_write(MO_HDELAY_ODD, value); | ||
722 | dprintk(1,"set_scale: hdelay 0x%04x (width %d)\n", value,swidth); | ||
723 | |||
724 | value = (swidth * 4096 / width) - 4096; | ||
725 | cx_write(MO_HSCALE_EVEN, value); | ||
726 | cx_write(MO_HSCALE_ODD, value); | ||
727 | dprintk(1,"set_scale: hscale 0x%04x\n", value); | ||
728 | |||
729 | cx_write(MO_HACTIVE_EVEN, width); | ||
730 | cx_write(MO_HACTIVE_ODD, width); | ||
731 | dprintk(1,"set_scale: hactive 0x%04x\n", width); | ||
732 | |||
733 | // recalc V scale Register (delay is constant) | ||
734 | cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm)); | ||
735 | cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm)); | ||
736 | dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm)); | ||
737 | |||
738 | value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff; | ||
739 | cx_write(MO_VSCALE_EVEN, value); | ||
740 | cx_write(MO_VSCALE_ODD, value); | ||
741 | dprintk(1,"set_scale: vscale 0x%04x\n", value); | ||
742 | |||
743 | cx_write(MO_VACTIVE_EVEN, sheight); | ||
744 | cx_write(MO_VACTIVE_ODD, sheight); | ||
745 | dprintk(1,"set_scale: vactive 0x%04x\n", sheight); | ||
746 | |||
747 | // setup filters | ||
748 | value = 0; | ||
749 | value |= (1 << 19); // CFILT (default) | ||
750 | if (core->tvnorm & V4L2_STD_SECAM) { | ||
751 | value |= (1 << 15); | ||
752 | value |= (1 << 16); | ||
753 | } | ||
754 | if (INPUT(core->input).type == CX88_VMUX_SVIDEO) | ||
755 | value |= (1 << 13) | (1 << 5); | ||
756 | if (V4L2_FIELD_INTERLACED == field) | ||
757 | value |= (1 << 3); // VINT (interlaced vertical scaling) | ||
758 | if (width < 385) | ||
759 | value |= (1 << 0); // 3-tap interpolation | ||
760 | if (width < 193) | ||
761 | value |= (1 << 1); // 5-tap interpolation | ||
762 | if (nocomb) | ||
763 | value |= (3 << 5); // disable comb filter | ||
764 | |||
765 | cx_andor(MO_FILTER_EVEN, 0x7ffc7f, value); /* preserve PEAKEN, PSEL */ | ||
766 | cx_andor(MO_FILTER_ODD, 0x7ffc7f, value); | ||
767 | dprintk(1,"set_scale: filter 0x%04x\n", value); | ||
768 | |||
769 | return 0; | ||
770 | } | ||
771 | |||
772 | static const u32 xtal = 28636363; | ||
773 | |||
774 | static int set_pll(struct cx88_core *core, int prescale, u32 ofreq) | ||
775 | { | ||
776 | static const u32 pre[] = { 0, 0, 0, 3, 2, 1 }; | ||
777 | u64 pll; | ||
778 | u32 reg; | ||
779 | int i; | ||
780 | |||
781 | if (prescale < 2) | ||
782 | prescale = 2; | ||
783 | if (prescale > 5) | ||
784 | prescale = 5; | ||
785 | |||
786 | pll = ofreq * 8 * prescale * (u64)(1 << 20); | ||
787 | do_div(pll,xtal); | ||
788 | reg = (pll & 0x3ffffff) | (pre[prescale] << 26); | ||
789 | if (((reg >> 20) & 0x3f) < 14) { | ||
790 | printk("%s/0: pll out of range\n",core->name); | ||
791 | return -1; | ||
792 | } | ||
793 | |||
794 | dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n", | ||
795 | reg, cx_read(MO_PLL_REG), ofreq); | ||
796 | cx_write(MO_PLL_REG, reg); | ||
797 | for (i = 0; i < 100; i++) { | ||
798 | reg = cx_read(MO_DEVICE_STATUS); | ||
799 | if (reg & (1<<2)) { | ||
800 | dprintk(1,"pll locked [pre=%d,ofreq=%d]\n", | ||
801 | prescale,ofreq); | ||
802 | return 0; | ||
803 | } | ||
804 | dprintk(1,"pll not locked yet, waiting ...\n"); | ||
805 | msleep(10); | ||
806 | } | ||
807 | dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq); | ||
808 | return -1; | ||
809 | } | ||
810 | |||
811 | int cx88_start_audio_dma(struct cx88_core *core) | ||
812 | { | ||
813 | /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */ | ||
814 | int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4; | ||
815 | |||
816 | int rds_bpl = cx88_sram_channels[SRAM_CH27].fifo_size/AUD_RDS_LINES; | ||
817 | |||
818 | /* If downstream RISC is enabled, bail out; ALSA is managing DMA */ | ||
819 | if (cx_read(MO_AUD_DMACNTRL) & 0x10) | ||
820 | return 0; | ||
821 | |||
822 | /* setup fifo + format */ | ||
823 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0); | ||
824 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0); | ||
825 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], | ||
826 | rds_bpl, 0); | ||
827 | |||
828 | cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */ | ||
829 | cx_write(MO_AUDR_LNGTH, rds_bpl); /* fifo bpl size */ | ||
830 | |||
831 | /* enable Up, Down and Audio RDS fifo */ | ||
832 | cx_write(MO_AUD_DMACNTRL, 0x0007); | ||
833 | |||
834 | return 0; | ||
835 | } | ||
836 | |||
837 | int cx88_stop_audio_dma(struct cx88_core *core) | ||
838 | { | ||
839 | /* If downstream RISC is enabled, bail out; ALSA is managing DMA */ | ||
840 | if (cx_read(MO_AUD_DMACNTRL) & 0x10) | ||
841 | return 0; | ||
842 | |||
843 | /* stop dma */ | ||
844 | cx_write(MO_AUD_DMACNTRL, 0x0000); | ||
845 | |||
846 | return 0; | ||
847 | } | ||
848 | |||
849 | static int set_tvaudio(struct cx88_core *core) | ||
850 | { | ||
851 | v4l2_std_id norm = core->tvnorm; | ||
852 | |||
853 | if (CX88_VMUX_TELEVISION != INPUT(core->input).type && | ||
854 | CX88_VMUX_CABLE != INPUT(core->input).type) | ||
855 | return 0; | ||
856 | |||
857 | if (V4L2_STD_PAL_BG & norm) { | ||
858 | core->tvaudio = WW_BG; | ||
859 | |||
860 | } else if (V4L2_STD_PAL_DK & norm) { | ||
861 | core->tvaudio = WW_DK; | ||
862 | |||
863 | } else if (V4L2_STD_PAL_I & norm) { | ||
864 | core->tvaudio = WW_I; | ||
865 | |||
866 | } else if (V4L2_STD_SECAM_L & norm) { | ||
867 | core->tvaudio = WW_L; | ||
868 | |||
869 | } else if ((V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H) & norm) { | ||
870 | core->tvaudio = WW_BG; | ||
871 | |||
872 | } else if (V4L2_STD_SECAM_DK & norm) { | ||
873 | core->tvaudio = WW_DK; | ||
874 | |||
875 | } else if ((V4L2_STD_NTSC_M & norm) || | ||
876 | (V4L2_STD_PAL_M & norm)) { | ||
877 | core->tvaudio = WW_BTSC; | ||
878 | |||
879 | } else if (V4L2_STD_NTSC_M_JP & norm) { | ||
880 | core->tvaudio = WW_EIAJ; | ||
881 | |||
882 | } else { | ||
883 | printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n", | ||
884 | core->name, v4l2_norm_to_name(core->tvnorm)); | ||
885 | core->tvaudio = WW_NONE; | ||
886 | return 0; | ||
887 | } | ||
888 | |||
889 | cx_andor(MO_AFECFG_IO, 0x1f, 0x0); | ||
890 | cx88_set_tvaudio(core); | ||
891 | /* cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); */ | ||
892 | |||
893 | /* | ||
894 | This should be needed only on cx88-alsa. It seems that some cx88 chips have | ||
895 | bugs and does require DMA enabled for it to work. | ||
896 | */ | ||
897 | cx88_start_audio_dma(core); | ||
898 | return 0; | ||
899 | } | ||
900 | |||
901 | |||
902 | |||
903 | int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm) | ||
904 | { | ||
905 | u32 fsc8; | ||
906 | u32 adc_clock; | ||
907 | u32 vdec_clock; | ||
908 | u32 step_db,step_dr; | ||
909 | u64 tmp64; | ||
910 | u32 bdelay,agcdelay,htotal; | ||
911 | u32 cxiformat, cxoformat; | ||
912 | |||
913 | core->tvnorm = norm; | ||
914 | fsc8 = norm_fsc8(norm); | ||
915 | adc_clock = xtal; | ||
916 | vdec_clock = fsc8; | ||
917 | step_db = fsc8; | ||
918 | step_dr = fsc8; | ||
919 | |||
920 | if (norm & V4L2_STD_NTSC_M_JP) { | ||
921 | cxiformat = VideoFormatNTSCJapan; | ||
922 | cxoformat = 0x181f0008; | ||
923 | } else if (norm & V4L2_STD_NTSC_443) { | ||
924 | cxiformat = VideoFormatNTSC443; | ||
925 | cxoformat = 0x181f0008; | ||
926 | } else if (norm & V4L2_STD_PAL_M) { | ||
927 | cxiformat = VideoFormatPALM; | ||
928 | cxoformat = 0x1c1f0008; | ||
929 | } else if (norm & V4L2_STD_PAL_N) { | ||
930 | cxiformat = VideoFormatPALN; | ||
931 | cxoformat = 0x1c1f0008; | ||
932 | } else if (norm & V4L2_STD_PAL_Nc) { | ||
933 | cxiformat = VideoFormatPALNC; | ||
934 | cxoformat = 0x1c1f0008; | ||
935 | } else if (norm & V4L2_STD_PAL_60) { | ||
936 | cxiformat = VideoFormatPAL60; | ||
937 | cxoformat = 0x181f0008; | ||
938 | } else if (norm & V4L2_STD_NTSC) { | ||
939 | cxiformat = VideoFormatNTSC; | ||
940 | cxoformat = 0x181f0008; | ||
941 | } else if (norm & V4L2_STD_SECAM) { | ||
942 | step_db = 4250000 * 8; | ||
943 | step_dr = 4406250 * 8; | ||
944 | |||
945 | cxiformat = VideoFormatSECAM; | ||
946 | cxoformat = 0x181f0008; | ||
947 | } else { /* PAL */ | ||
948 | cxiformat = VideoFormatPAL; | ||
949 | cxoformat = 0x181f0008; | ||
950 | } | ||
951 | |||
952 | dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n", | ||
953 | v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock, | ||
954 | step_db, step_dr); | ||
955 | set_pll(core,2,vdec_clock); | ||
956 | |||
957 | dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n", | ||
958 | cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); | ||
959 | /* Chroma AGC must be disabled if SECAM is used, we enable it | ||
960 | by default on PAL and NTSC */ | ||
961 | cx_andor(MO_INPUT_FORMAT, 0x40f, | ||
962 | norm & V4L2_STD_SECAM ? cxiformat : cxiformat | 0x400); | ||
963 | |||
964 | // FIXME: as-is from DScaler | ||
965 | dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n", | ||
966 | cxoformat, cx_read(MO_OUTPUT_FORMAT)); | ||
967 | cx_write(MO_OUTPUT_FORMAT, cxoformat); | ||
968 | |||
969 | // MO_SCONV_REG = adc clock / video dec clock * 2^17 | ||
970 | tmp64 = adc_clock * (u64)(1 << 17); | ||
971 | do_div(tmp64, vdec_clock); | ||
972 | dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n", | ||
973 | (u32)tmp64, cx_read(MO_SCONV_REG)); | ||
974 | cx_write(MO_SCONV_REG, (u32)tmp64); | ||
975 | |||
976 | // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22 | ||
977 | tmp64 = step_db * (u64)(1 << 22); | ||
978 | do_div(tmp64, vdec_clock); | ||
979 | dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n", | ||
980 | (u32)tmp64, cx_read(MO_SUB_STEP)); | ||
981 | cx_write(MO_SUB_STEP, (u32)tmp64); | ||
982 | |||
983 | // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22 | ||
984 | tmp64 = step_dr * (u64)(1 << 22); | ||
985 | do_div(tmp64, vdec_clock); | ||
986 | dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n", | ||
987 | (u32)tmp64, cx_read(MO_SUB_STEP_DR)); | ||
988 | cx_write(MO_SUB_STEP_DR, (u32)tmp64); | ||
989 | |||
990 | // bdelay + agcdelay | ||
991 | bdelay = vdec_clock * 65 / 20000000 + 21; | ||
992 | agcdelay = vdec_clock * 68 / 20000000 + 15; | ||
993 | dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n", | ||
994 | (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay); | ||
995 | cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay); | ||
996 | |||
997 | // htotal | ||
998 | tmp64 = norm_htotal(norm) * (u64)vdec_clock; | ||
999 | do_div(tmp64, fsc8); | ||
1000 | htotal = (u32)tmp64; | ||
1001 | dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n", | ||
1002 | htotal, cx_read(MO_HTOTAL), (u32)tmp64); | ||
1003 | cx_andor(MO_HTOTAL, 0x07ff, htotal); | ||
1004 | |||
1005 | // vbi stuff, set vbi offset to 10 (for 20 Clk*2 pixels), this makes | ||
1006 | // the effective vbi offset ~244 samples, the same as the Bt8x8 | ||
1007 | cx_write(MO_VBI_PACKET, (10<<11) | norm_vbipack(norm)); | ||
1008 | |||
1009 | // this is needed as well to set all tvnorm parameter | ||
1010 | cx88_set_scale(core, 320, 240, V4L2_FIELD_INTERLACED); | ||
1011 | |||
1012 | // audio | ||
1013 | set_tvaudio(core); | ||
1014 | |||
1015 | // tell i2c chips | ||
1016 | call_all(core, core, s_std, norm); | ||
1017 | |||
1018 | // done | ||
1019 | return 0; | ||
1020 | } | ||
1021 | |||
1022 | /* ------------------------------------------------------------------ */ | ||
1023 | |||
1024 | struct video_device *cx88_vdev_init(struct cx88_core *core, | ||
1025 | struct pci_dev *pci, | ||
1026 | const struct video_device *template_, | ||
1027 | const char *type) | ||
1028 | { | ||
1029 | struct video_device *vfd; | ||
1030 | |||
1031 | vfd = video_device_alloc(); | ||
1032 | if (NULL == vfd) | ||
1033 | return NULL; | ||
1034 | *vfd = *template_; | ||
1035 | vfd->v4l2_dev = &core->v4l2_dev; | ||
1036 | vfd->parent = &pci->dev; | ||
1037 | vfd->release = video_device_release; | ||
1038 | snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", | ||
1039 | core->name, type, core->board.name); | ||
1040 | return vfd; | ||
1041 | } | ||
1042 | |||
1043 | struct cx88_core* cx88_core_get(struct pci_dev *pci) | ||
1044 | { | ||
1045 | struct cx88_core *core; | ||
1046 | |||
1047 | mutex_lock(&devlist); | ||
1048 | list_for_each_entry(core, &cx88_devlist, devlist) { | ||
1049 | if (pci->bus->number != core->pci_bus) | ||
1050 | continue; | ||
1051 | if (PCI_SLOT(pci->devfn) != core->pci_slot) | ||
1052 | continue; | ||
1053 | |||
1054 | if (0 != cx88_get_resources(core, pci)) { | ||
1055 | mutex_unlock(&devlist); | ||
1056 | return NULL; | ||
1057 | } | ||
1058 | atomic_inc(&core->refcount); | ||
1059 | mutex_unlock(&devlist); | ||
1060 | return core; | ||
1061 | } | ||
1062 | |||
1063 | core = cx88_core_create(pci, cx88_devcount); | ||
1064 | if (NULL != core) { | ||
1065 | cx88_devcount++; | ||
1066 | list_add_tail(&core->devlist, &cx88_devlist); | ||
1067 | } | ||
1068 | |||
1069 | mutex_unlock(&devlist); | ||
1070 | return core; | ||
1071 | } | ||
1072 | |||
1073 | void cx88_core_put(struct cx88_core *core, struct pci_dev *pci) | ||
1074 | { | ||
1075 | release_mem_region(pci_resource_start(pci,0), | ||
1076 | pci_resource_len(pci,0)); | ||
1077 | |||
1078 | if (!atomic_dec_and_test(&core->refcount)) | ||
1079 | return; | ||
1080 | |||
1081 | mutex_lock(&devlist); | ||
1082 | cx88_ir_fini(core); | ||
1083 | if (0 == core->i2c_rc) { | ||
1084 | if (core->i2c_rtc) | ||
1085 | i2c_unregister_device(core->i2c_rtc); | ||
1086 | i2c_del_adapter(&core->i2c_adap); | ||
1087 | } | ||
1088 | list_del(&core->devlist); | ||
1089 | iounmap(core->lmmio); | ||
1090 | cx88_devcount--; | ||
1091 | mutex_unlock(&devlist); | ||
1092 | v4l2_device_unregister(&core->v4l2_dev); | ||
1093 | kfree(core); | ||
1094 | } | ||
1095 | |||
1096 | /* ------------------------------------------------------------------ */ | ||
1097 | |||
1098 | EXPORT_SYMBOL(cx88_print_irqbits); | ||
1099 | |||
1100 | EXPORT_SYMBOL(cx88_core_irq); | ||
1101 | EXPORT_SYMBOL(cx88_wakeup); | ||
1102 | EXPORT_SYMBOL(cx88_reset); | ||
1103 | EXPORT_SYMBOL(cx88_shutdown); | ||
1104 | |||
1105 | EXPORT_SYMBOL(cx88_risc_buffer); | ||
1106 | EXPORT_SYMBOL(cx88_risc_databuffer); | ||
1107 | EXPORT_SYMBOL(cx88_risc_stopper); | ||
1108 | EXPORT_SYMBOL(cx88_free_buffer); | ||
1109 | |||
1110 | EXPORT_SYMBOL(cx88_sram_channels); | ||
1111 | EXPORT_SYMBOL(cx88_sram_channel_setup); | ||
1112 | EXPORT_SYMBOL(cx88_sram_channel_dump); | ||
1113 | |||
1114 | EXPORT_SYMBOL(cx88_set_tvnorm); | ||
1115 | EXPORT_SYMBOL(cx88_set_scale); | ||
1116 | |||
1117 | EXPORT_SYMBOL(cx88_vdev_init); | ||
1118 | EXPORT_SYMBOL(cx88_core_get); | ||
1119 | EXPORT_SYMBOL(cx88_core_put); | ||
1120 | |||
1121 | EXPORT_SYMBOL(cx88_ir_start); | ||
1122 | EXPORT_SYMBOL(cx88_ir_stop); | ||
1123 | |||
1124 | /* | ||
1125 | * Local variables: | ||
1126 | * c-basic-offset: 8 | ||
1127 | * End: | ||
1128 | * 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 | ||
1129 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-dsp.c b/drivers/media/video/cx88/cx88-dsp.c new file mode 100644 index 00000000000..a9907265ff6 --- /dev/null +++ b/drivers/media/video/cx88/cx88-dsp.c | |||
@@ -0,0 +1,322 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Stereo and SAP detection for cx88 | ||
4 | * | ||
5 | * Copyright (c) 2009 Marton Balint <cus@fazekas.hu> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/slab.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/jiffies.h> | ||
26 | #include <asm/div64.h> | ||
27 | |||
28 | #include "cx88.h" | ||
29 | #include "cx88-reg.h" | ||
30 | |||
31 | #define INT_PI ((s32)(3.141592653589 * 32768.0)) | ||
32 | |||
33 | #define compat_remainder(a, b) \ | ||
34 | ((float)(((s32)((a)*100))%((s32)((b)*100)))/100.0) | ||
35 | |||
36 | #define baseband_freq(carrier, srate, tone) ((s32)( \ | ||
37 | (compat_remainder(carrier + tone, srate)) / srate * 2 * INT_PI)) | ||
38 | |||
39 | /* We calculate the baseband frequencies of the carrier and the pilot tones | ||
40 | * based on the the sampling rate of the audio rds fifo. */ | ||
41 | |||
42 | #define FREQ_A2_CARRIER baseband_freq(54687.5, 2689.36, 0.0) | ||
43 | #define FREQ_A2_DUAL baseband_freq(54687.5, 2689.36, 274.1) | ||
44 | #define FREQ_A2_STEREO baseband_freq(54687.5, 2689.36, 117.5) | ||
45 | |||
46 | /* The frequencies below are from the reference driver. They probably need | ||
47 | * further adjustments, because they are not tested at all. You may even need | ||
48 | * to play a bit with the registers of the chip to select the proper signal | ||
49 | * for the input of the audio rds fifo, and measure it's sampling rate to | ||
50 | * calculate the proper baseband frequencies... */ | ||
51 | |||
52 | #define FREQ_A2M_CARRIER ((s32)(2.114516 * 32768.0)) | ||
53 | #define FREQ_A2M_DUAL ((s32)(2.754916 * 32768.0)) | ||
54 | #define FREQ_A2M_STEREO ((s32)(2.462326 * 32768.0)) | ||
55 | |||
56 | #define FREQ_EIAJ_CARRIER ((s32)(1.963495 * 32768.0)) /* 5pi/8 */ | ||
57 | #define FREQ_EIAJ_DUAL ((s32)(2.562118 * 32768.0)) | ||
58 | #define FREQ_EIAJ_STEREO ((s32)(2.601053 * 32768.0)) | ||
59 | |||
60 | #define FREQ_BTSC_DUAL ((s32)(1.963495 * 32768.0)) /* 5pi/8 */ | ||
61 | #define FREQ_BTSC_DUAL_REF ((s32)(1.374446 * 32768.0)) /* 7pi/16 */ | ||
62 | |||
63 | #define FREQ_BTSC_SAP ((s32)(2.471532 * 32768.0)) | ||
64 | #define FREQ_BTSC_SAP_REF ((s32)(1.730072 * 32768.0)) | ||
65 | |||
66 | /* The spectrum of the signal should be empty between these frequencies. */ | ||
67 | #define FREQ_NOISE_START ((s32)(0.100000 * 32768.0)) | ||
68 | #define FREQ_NOISE_END ((s32)(1.200000 * 32768.0)) | ||
69 | |||
70 | static unsigned int dsp_debug; | ||
71 | module_param(dsp_debug, int, 0644); | ||
72 | MODULE_PARM_DESC(dsp_debug, "enable audio dsp debug messages"); | ||
73 | |||
74 | #define dprintk(level, fmt, arg...) if (dsp_debug >= level) \ | ||
75 | printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg) | ||
76 | |||
77 | static s32 int_cos(u32 x) | ||
78 | { | ||
79 | u32 t2, t4, t6, t8; | ||
80 | s32 ret; | ||
81 | u16 period = x / INT_PI; | ||
82 | if (period % 2) | ||
83 | return -int_cos(x - INT_PI); | ||
84 | x = x % INT_PI; | ||
85 | if (x > INT_PI/2) | ||
86 | return -int_cos(INT_PI/2 - (x % (INT_PI/2))); | ||
87 | /* Now x is between 0 and INT_PI/2. | ||
88 | * To calculate cos(x) we use it's Taylor polinom. */ | ||
89 | t2 = x*x/32768/2; | ||
90 | t4 = t2*x/32768*x/32768/3/4; | ||
91 | t6 = t4*x/32768*x/32768/5/6; | ||
92 | t8 = t6*x/32768*x/32768/7/8; | ||
93 | ret = 32768-t2+t4-t6+t8; | ||
94 | return ret; | ||
95 | } | ||
96 | |||
97 | static u32 int_goertzel(s16 x[], u32 N, u32 freq) | ||
98 | { | ||
99 | /* We use the Goertzel algorithm to determine the power of the | ||
100 | * given frequency in the signal */ | ||
101 | s32 s_prev = 0; | ||
102 | s32 s_prev2 = 0; | ||
103 | s32 coeff = 2*int_cos(freq); | ||
104 | u32 i; | ||
105 | |||
106 | u64 tmp; | ||
107 | u32 divisor; | ||
108 | |||
109 | for (i = 0; i < N; i++) { | ||
110 | s32 s = x[i] + ((s64)coeff*s_prev/32768) - s_prev2; | ||
111 | s_prev2 = s_prev; | ||
112 | s_prev = s; | ||
113 | } | ||
114 | |||
115 | tmp = (s64)s_prev2 * s_prev2 + (s64)s_prev * s_prev - | ||
116 | (s64)coeff * s_prev2 * s_prev / 32768; | ||
117 | |||
118 | /* XXX: N must be low enough so that N*N fits in s32. | ||
119 | * Else we need two divisions. */ | ||
120 | divisor = N * N; | ||
121 | do_div(tmp, divisor); | ||
122 | |||
123 | return (u32) tmp; | ||
124 | } | ||
125 | |||
126 | static u32 freq_magnitude(s16 x[], u32 N, u32 freq) | ||
127 | { | ||
128 | u32 sum = int_goertzel(x, N, freq); | ||
129 | return (u32)int_sqrt(sum); | ||
130 | } | ||
131 | |||
132 | static u32 noise_magnitude(s16 x[], u32 N, u32 freq_start, u32 freq_end) | ||
133 | { | ||
134 | int i; | ||
135 | u32 sum = 0; | ||
136 | u32 freq_step; | ||
137 | int samples = 5; | ||
138 | |||
139 | if (N > 192) { | ||
140 | /* The last 192 samples are enough for noise detection */ | ||
141 | x += (N-192); | ||
142 | N = 192; | ||
143 | } | ||
144 | |||
145 | freq_step = (freq_end - freq_start) / (samples - 1); | ||
146 | |||
147 | for (i = 0; i < samples; i++) { | ||
148 | sum += int_goertzel(x, N, freq_start); | ||
149 | freq_start += freq_step; | ||
150 | } | ||
151 | |||
152 | return (u32)int_sqrt(sum / samples); | ||
153 | } | ||
154 | |||
155 | static s32 detect_a2_a2m_eiaj(struct cx88_core *core, s16 x[], u32 N) | ||
156 | { | ||
157 | s32 carrier, stereo, dual, noise; | ||
158 | s32 carrier_freq, stereo_freq, dual_freq; | ||
159 | s32 ret; | ||
160 | |||
161 | switch (core->tvaudio) { | ||
162 | case WW_BG: | ||
163 | case WW_DK: | ||
164 | carrier_freq = FREQ_A2_CARRIER; | ||
165 | stereo_freq = FREQ_A2_STEREO; | ||
166 | dual_freq = FREQ_A2_DUAL; | ||
167 | break; | ||
168 | case WW_M: | ||
169 | carrier_freq = FREQ_A2M_CARRIER; | ||
170 | stereo_freq = FREQ_A2M_STEREO; | ||
171 | dual_freq = FREQ_A2M_DUAL; | ||
172 | break; | ||
173 | case WW_EIAJ: | ||
174 | carrier_freq = FREQ_EIAJ_CARRIER; | ||
175 | stereo_freq = FREQ_EIAJ_STEREO; | ||
176 | dual_freq = FREQ_EIAJ_DUAL; | ||
177 | break; | ||
178 | default: | ||
179 | printk(KERN_WARNING "%s/0: unsupported audio mode %d for %s\n", | ||
180 | core->name, core->tvaudio, __func__); | ||
181 | return UNSET; | ||
182 | } | ||
183 | |||
184 | carrier = freq_magnitude(x, N, carrier_freq); | ||
185 | stereo = freq_magnitude(x, N, stereo_freq); | ||
186 | dual = freq_magnitude(x, N, dual_freq); | ||
187 | noise = noise_magnitude(x, N, FREQ_NOISE_START, FREQ_NOISE_END); | ||
188 | |||
189 | dprintk(1, "detect a2/a2m/eiaj: carrier=%d, stereo=%d, dual=%d, " | ||
190 | "noise=%d\n", carrier, stereo, dual, noise); | ||
191 | |||
192 | if (stereo > dual) | ||
193 | ret = V4L2_TUNER_SUB_STEREO; | ||
194 | else | ||
195 | ret = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; | ||
196 | |||
197 | if (core->tvaudio == WW_EIAJ) { | ||
198 | /* EIAJ checks may need adjustments */ | ||
199 | if ((carrier > max(stereo, dual)*2) && | ||
200 | (carrier < max(stereo, dual)*6) && | ||
201 | (carrier > 20 && carrier < 200) && | ||
202 | (max(stereo, dual) > min(stereo, dual))) { | ||
203 | /* For EIAJ the carrier is always present, | ||
204 | so we probably don't need noise detection */ | ||
205 | return ret; | ||
206 | } | ||
207 | } else { | ||
208 | if ((carrier > max(stereo, dual)*2) && | ||
209 | (carrier < max(stereo, dual)*8) && | ||
210 | (carrier > 20 && carrier < 200) && | ||
211 | (noise < 10) && | ||
212 | (max(stereo, dual) > min(stereo, dual)*2)) { | ||
213 | return ret; | ||
214 | } | ||
215 | } | ||
216 | return V4L2_TUNER_SUB_MONO; | ||
217 | } | ||
218 | |||
219 | static s32 detect_btsc(struct cx88_core *core, s16 x[], u32 N) | ||
220 | { | ||
221 | s32 sap_ref = freq_magnitude(x, N, FREQ_BTSC_SAP_REF); | ||
222 | s32 sap = freq_magnitude(x, N, FREQ_BTSC_SAP); | ||
223 | s32 dual_ref = freq_magnitude(x, N, FREQ_BTSC_DUAL_REF); | ||
224 | s32 dual = freq_magnitude(x, N, FREQ_BTSC_DUAL); | ||
225 | dprintk(1, "detect btsc: dual_ref=%d, dual=%d, sap_ref=%d, sap=%d" | ||
226 | "\n", dual_ref, dual, sap_ref, sap); | ||
227 | /* FIXME: Currently not supported */ | ||
228 | return UNSET; | ||
229 | } | ||
230 | |||
231 | static s16 *read_rds_samples(struct cx88_core *core, u32 *N) | ||
232 | { | ||
233 | const struct sram_channel *srch = &cx88_sram_channels[SRAM_CH27]; | ||
234 | s16 *samples; | ||
235 | |||
236 | unsigned int i; | ||
237 | unsigned int bpl = srch->fifo_size/AUD_RDS_LINES; | ||
238 | unsigned int spl = bpl/4; | ||
239 | unsigned int sample_count = spl*(AUD_RDS_LINES-1); | ||
240 | |||
241 | u32 current_address = cx_read(srch->ptr1_reg); | ||
242 | u32 offset = (current_address - srch->fifo_start + bpl); | ||
243 | |||
244 | dprintk(1, "read RDS samples: current_address=%08x (offset=%08x), " | ||
245 | "sample_count=%d, aud_intstat=%08x\n", current_address, | ||
246 | current_address - srch->fifo_start, sample_count, | ||
247 | cx_read(MO_AUD_INTSTAT)); | ||
248 | |||
249 | samples = kmalloc(sizeof(s16)*sample_count, GFP_KERNEL); | ||
250 | if (!samples) | ||
251 | return NULL; | ||
252 | |||
253 | *N = sample_count; | ||
254 | |||
255 | for (i = 0; i < sample_count; i++) { | ||
256 | offset = offset % (AUD_RDS_LINES*bpl); | ||
257 | samples[i] = cx_read(srch->fifo_start + offset); | ||
258 | offset += 4; | ||
259 | } | ||
260 | |||
261 | if (dsp_debug >= 2) { | ||
262 | dprintk(2, "RDS samples dump: "); | ||
263 | for (i = 0; i < sample_count; i++) | ||
264 | printk("%hd ", samples[i]); | ||
265 | printk(".\n"); | ||
266 | } | ||
267 | |||
268 | return samples; | ||
269 | } | ||
270 | |||
271 | s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core) | ||
272 | { | ||
273 | s16 *samples; | ||
274 | u32 N = 0; | ||
275 | s32 ret = UNSET; | ||
276 | |||
277 | /* If audio RDS fifo is disabled, we can't read the samples */ | ||
278 | if (!(cx_read(MO_AUD_DMACNTRL) & 0x04)) | ||
279 | return ret; | ||
280 | if (!(cx_read(AUD_CTL) & EN_FMRADIO_EN_RDS)) | ||
281 | return ret; | ||
282 | |||
283 | /* Wait at least 500 ms after an audio standard change */ | ||
284 | if (time_before(jiffies, core->last_change + msecs_to_jiffies(500))) | ||
285 | return ret; | ||
286 | |||
287 | samples = read_rds_samples(core, &N); | ||
288 | |||
289 | if (!samples) | ||
290 | return ret; | ||
291 | |||
292 | switch (core->tvaudio) { | ||
293 | case WW_BG: | ||
294 | case WW_DK: | ||
295 | case WW_EIAJ: | ||
296 | case WW_M: | ||
297 | ret = detect_a2_a2m_eiaj(core, samples, N); | ||
298 | break; | ||
299 | case WW_BTSC: | ||
300 | ret = detect_btsc(core, samples, N); | ||
301 | break; | ||
302 | case WW_NONE: | ||
303 | case WW_I: | ||
304 | case WW_L: | ||
305 | case WW_I2SPT: | ||
306 | case WW_FM: | ||
307 | case WW_I2SADC: | ||
308 | break; | ||
309 | } | ||
310 | |||
311 | kfree(samples); | ||
312 | |||
313 | if (UNSET != ret) | ||
314 | dprintk(1, "stereo/sap detection result:%s%s%s\n", | ||
315 | (ret & V4L2_TUNER_SUB_MONO) ? " mono" : "", | ||
316 | (ret & V4L2_TUNER_SUB_STEREO) ? " stereo" : "", | ||
317 | (ret & V4L2_TUNER_SUB_LANG2) ? " dual" : ""); | ||
318 | |||
319 | return ret; | ||
320 | } | ||
321 | EXPORT_SYMBOL(cx88_dsp_detect_stereo_sap); | ||
322 | |||
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c new file mode 100644 index 00000000000..cf3d33ab541 --- /dev/null +++ b/drivers/media/video/cx88/cx88-dvb.c | |||
@@ -0,0 +1,1764 @@ | |||
1 | /* | ||
2 | * | ||
3 | * device driver for Conexant 2388x based TV cards | ||
4 | * MPEG Transport Stream (DVB) routines | ||
5 | * | ||
6 | * (c) 2004, 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> | ||
7 | * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/module.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/device.h> | ||
27 | #include <linux/fs.h> | ||
28 | #include <linux/kthread.h> | ||
29 | #include <linux/file.h> | ||
30 | #include <linux/suspend.h> | ||
31 | |||
32 | #include "cx88.h" | ||
33 | #include "dvb-pll.h" | ||
34 | #include <media/v4l2-common.h> | ||
35 | |||
36 | #include "mt352.h" | ||
37 | #include "mt352_priv.h" | ||
38 | #include "cx88-vp3054-i2c.h" | ||
39 | #include "zl10353.h" | ||
40 | #include "cx22702.h" | ||
41 | #include "or51132.h" | ||
42 | #include "lgdt330x.h" | ||
43 | #include "s5h1409.h" | ||
44 | #include "xc4000.h" | ||
45 | #include "xc5000.h" | ||
46 | #include "nxt200x.h" | ||
47 | #include "cx24123.h" | ||
48 | #include "isl6421.h" | ||
49 | #include "tuner-simple.h" | ||
50 | #include "tda9887.h" | ||
51 | #include "s5h1411.h" | ||
52 | #include "stv0299.h" | ||
53 | #include "z0194a.h" | ||
54 | #include "stv0288.h" | ||
55 | #include "stb6000.h" | ||
56 | #include "cx24116.h" | ||
57 | #include "stv0900.h" | ||
58 | #include "stb6100.h" | ||
59 | #include "stb6100_proc.h" | ||
60 | #include "mb86a16.h" | ||
61 | #include "ds3000.h" | ||
62 | |||
63 | MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); | ||
64 | MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); | ||
65 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
66 | MODULE_LICENSE("GPL"); | ||
67 | MODULE_VERSION(CX88_VERSION); | ||
68 | |||
69 | static unsigned int debug; | ||
70 | module_param(debug, int, 0644); | ||
71 | MODULE_PARM_DESC(debug,"enable debug messages [dvb]"); | ||
72 | |||
73 | static unsigned int dvb_buf_tscnt = 32; | ||
74 | module_param(dvb_buf_tscnt, int, 0644); | ||
75 | MODULE_PARM_DESC(dvb_buf_tscnt, "DVB Buffer TS count [dvb]"); | ||
76 | |||
77 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | ||
78 | |||
79 | #define dprintk(level,fmt, arg...) if (debug >= level) \ | ||
80 | printk(KERN_DEBUG "%s/2-dvb: " fmt, core->name, ## arg) | ||
81 | |||
82 | /* ------------------------------------------------------------------ */ | ||
83 | |||
84 | static int dvb_buf_setup(struct videobuf_queue *q, | ||
85 | unsigned int *count, unsigned int *size) | ||
86 | { | ||
87 | struct cx8802_dev *dev = q->priv_data; | ||
88 | |||
89 | dev->ts_packet_size = 188 * 4; | ||
90 | dev->ts_packet_count = dvb_buf_tscnt; | ||
91 | |||
92 | *size = dev->ts_packet_size * dev->ts_packet_count; | ||
93 | *count = dvb_buf_tscnt; | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static int dvb_buf_prepare(struct videobuf_queue *q, | ||
98 | struct videobuf_buffer *vb, enum v4l2_field field) | ||
99 | { | ||
100 | struct cx8802_dev *dev = q->priv_data; | ||
101 | return cx8802_buf_prepare(q, dev, (struct cx88_buffer*)vb,field); | ||
102 | } | ||
103 | |||
104 | static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
105 | { | ||
106 | struct cx8802_dev *dev = q->priv_data; | ||
107 | cx8802_buf_queue(dev, (struct cx88_buffer*)vb); | ||
108 | } | ||
109 | |||
110 | static void dvb_buf_release(struct videobuf_queue *q, | ||
111 | struct videobuf_buffer *vb) | ||
112 | { | ||
113 | cx88_free_buffer(q, (struct cx88_buffer*)vb); | ||
114 | } | ||
115 | |||
116 | static const struct videobuf_queue_ops dvb_qops = { | ||
117 | .buf_setup = dvb_buf_setup, | ||
118 | .buf_prepare = dvb_buf_prepare, | ||
119 | .buf_queue = dvb_buf_queue, | ||
120 | .buf_release = dvb_buf_release, | ||
121 | }; | ||
122 | |||
123 | /* ------------------------------------------------------------------ */ | ||
124 | |||
125 | static int cx88_dvb_bus_ctrl(struct dvb_frontend* fe, int acquire) | ||
126 | { | ||
127 | struct cx8802_dev *dev= fe->dvb->priv; | ||
128 | struct cx8802_driver *drv = NULL; | ||
129 | int ret = 0; | ||
130 | int fe_id; | ||
131 | |||
132 | fe_id = videobuf_dvb_find_frontend(&dev->frontends, fe); | ||
133 | if (!fe_id) { | ||
134 | printk(KERN_ERR "%s() No frontend found\n", __func__); | ||
135 | return -EINVAL; | ||
136 | } | ||
137 | |||
138 | mutex_lock(&dev->core->lock); | ||
139 | drv = cx8802_get_driver(dev, CX88_MPEG_DVB); | ||
140 | if (drv) { | ||
141 | if (acquire){ | ||
142 | dev->frontends.active_fe_id = fe_id; | ||
143 | ret = drv->request_acquire(drv); | ||
144 | } else { | ||
145 | ret = drv->request_release(drv); | ||
146 | dev->frontends.active_fe_id = 0; | ||
147 | } | ||
148 | } | ||
149 | mutex_unlock(&dev->core->lock); | ||
150 | |||
151 | return ret; | ||
152 | } | ||
153 | |||
154 | static void cx88_dvb_gate_ctrl(struct cx88_core *core, int open) | ||
155 | { | ||
156 | struct videobuf_dvb_frontends *f; | ||
157 | struct videobuf_dvb_frontend *fe; | ||
158 | |||
159 | if (!core->dvbdev) | ||
160 | return; | ||
161 | |||
162 | f = &core->dvbdev->frontends; | ||
163 | |||
164 | if (!f) | ||
165 | return; | ||
166 | |||
167 | if (f->gate <= 1) /* undefined or fe0 */ | ||
168 | fe = videobuf_dvb_get_frontend(f, 1); | ||
169 | else | ||
170 | fe = videobuf_dvb_get_frontend(f, f->gate); | ||
171 | |||
172 | if (fe && fe->dvb.frontend && fe->dvb.frontend->ops.i2c_gate_ctrl) | ||
173 | fe->dvb.frontend->ops.i2c_gate_ctrl(fe->dvb.frontend, open); | ||
174 | } | ||
175 | |||
176 | /* ------------------------------------------------------------------ */ | ||
177 | |||
178 | static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe) | ||
179 | { | ||
180 | static const u8 clock_config [] = { CLOCK_CTL, 0x38, 0x39 }; | ||
181 | static const u8 reset [] = { RESET, 0x80 }; | ||
182 | static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; | ||
183 | static const u8 agc_cfg [] = { AGC_TARGET, 0x24, 0x20 }; | ||
184 | static const u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; | ||
185 | static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; | ||
186 | |||
187 | mt352_write(fe, clock_config, sizeof(clock_config)); | ||
188 | udelay(200); | ||
189 | mt352_write(fe, reset, sizeof(reset)); | ||
190 | mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); | ||
191 | |||
192 | mt352_write(fe, agc_cfg, sizeof(agc_cfg)); | ||
193 | mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); | ||
194 | mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static int dvico_dual_demod_init(struct dvb_frontend *fe) | ||
199 | { | ||
200 | static const u8 clock_config [] = { CLOCK_CTL, 0x38, 0x38 }; | ||
201 | static const u8 reset [] = { RESET, 0x80 }; | ||
202 | static const u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; | ||
203 | static const u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 }; | ||
204 | static const u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 }; | ||
205 | static const u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 }; | ||
206 | |||
207 | mt352_write(fe, clock_config, sizeof(clock_config)); | ||
208 | udelay(200); | ||
209 | mt352_write(fe, reset, sizeof(reset)); | ||
210 | mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); | ||
211 | |||
212 | mt352_write(fe, agc_cfg, sizeof(agc_cfg)); | ||
213 | mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); | ||
214 | mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe) | ||
220 | { | ||
221 | static const u8 clock_config [] = { 0x89, 0x38, 0x39 }; | ||
222 | static const u8 reset [] = { 0x50, 0x80 }; | ||
223 | static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 }; | ||
224 | static const u8 agc_cfg [] = { 0x67, 0x10, 0x23, 0x00, 0xFF, 0xFF, | ||
225 | 0x00, 0xFF, 0x00, 0x40, 0x40 }; | ||
226 | static const u8 dntv_extra[] = { 0xB5, 0x7A }; | ||
227 | static const u8 capt_range_cfg[] = { 0x75, 0x32 }; | ||
228 | |||
229 | mt352_write(fe, clock_config, sizeof(clock_config)); | ||
230 | udelay(2000); | ||
231 | mt352_write(fe, reset, sizeof(reset)); | ||
232 | mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); | ||
233 | |||
234 | mt352_write(fe, agc_cfg, sizeof(agc_cfg)); | ||
235 | udelay(2000); | ||
236 | mt352_write(fe, dntv_extra, sizeof(dntv_extra)); | ||
237 | mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static const struct mt352_config dvico_fusionhdtv = { | ||
243 | .demod_address = 0x0f, | ||
244 | .demod_init = dvico_fusionhdtv_demod_init, | ||
245 | }; | ||
246 | |||
247 | static const struct mt352_config dntv_live_dvbt_config = { | ||
248 | .demod_address = 0x0f, | ||
249 | .demod_init = dntv_live_dvbt_demod_init, | ||
250 | }; | ||
251 | |||
252 | static const struct mt352_config dvico_fusionhdtv_dual = { | ||
253 | .demod_address = 0x0f, | ||
254 | .demod_init = dvico_dual_demod_init, | ||
255 | }; | ||
256 | |||
257 | static const struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = { | ||
258 | .demod_address = (0x1e >> 1), | ||
259 | .no_tuner = 1, | ||
260 | .if2 = 45600, | ||
261 | }; | ||
262 | |||
263 | static struct mb86a16_config twinhan_vp1027 = { | ||
264 | .demod_address = 0x08, | ||
265 | }; | ||
266 | |||
267 | #if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) | ||
268 | static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) | ||
269 | { | ||
270 | static const u8 clock_config [] = { 0x89, 0x38, 0x38 }; | ||
271 | static const u8 reset [] = { 0x50, 0x80 }; | ||
272 | static const u8 adc_ctl_1_cfg [] = { 0x8E, 0x40 }; | ||
273 | static const u8 agc_cfg [] = { 0x67, 0x10, 0x20, 0x00, 0xFF, 0xFF, | ||
274 | 0x00, 0xFF, 0x00, 0x40, 0x40 }; | ||
275 | static const u8 dntv_extra[] = { 0xB5, 0x7A }; | ||
276 | static const u8 capt_range_cfg[] = { 0x75, 0x32 }; | ||
277 | |||
278 | mt352_write(fe, clock_config, sizeof(clock_config)); | ||
279 | udelay(2000); | ||
280 | mt352_write(fe, reset, sizeof(reset)); | ||
281 | mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); | ||
282 | |||
283 | mt352_write(fe, agc_cfg, sizeof(agc_cfg)); | ||
284 | udelay(2000); | ||
285 | mt352_write(fe, dntv_extra, sizeof(dntv_extra)); | ||
286 | mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | static const struct mt352_config dntv_live_dvbt_pro_config = { | ||
292 | .demod_address = 0x0f, | ||
293 | .no_tuner = 1, | ||
294 | .demod_init = dntv_live_dvbt_pro_demod_init, | ||
295 | }; | ||
296 | #endif | ||
297 | |||
298 | static const struct zl10353_config dvico_fusionhdtv_hybrid = { | ||
299 | .demod_address = 0x0f, | ||
300 | .no_tuner = 1, | ||
301 | }; | ||
302 | |||
303 | static const struct zl10353_config dvico_fusionhdtv_xc3028 = { | ||
304 | .demod_address = 0x0f, | ||
305 | .if2 = 45600, | ||
306 | .no_tuner = 1, | ||
307 | }; | ||
308 | |||
309 | static const struct mt352_config dvico_fusionhdtv_mt352_xc3028 = { | ||
310 | .demod_address = 0x0f, | ||
311 | .if2 = 4560, | ||
312 | .no_tuner = 1, | ||
313 | .demod_init = dvico_fusionhdtv_demod_init, | ||
314 | }; | ||
315 | |||
316 | static const struct zl10353_config dvico_fusionhdtv_plus_v1_1 = { | ||
317 | .demod_address = 0x0f, | ||
318 | }; | ||
319 | |||
320 | static const struct cx22702_config connexant_refboard_config = { | ||
321 | .demod_address = 0x43, | ||
322 | .output_mode = CX22702_SERIAL_OUTPUT, | ||
323 | }; | ||
324 | |||
325 | static const struct cx22702_config hauppauge_hvr_config = { | ||
326 | .demod_address = 0x63, | ||
327 | .output_mode = CX22702_SERIAL_OUTPUT, | ||
328 | }; | ||
329 | |||
330 | static int or51132_set_ts_param(struct dvb_frontend* fe, int is_punctured) | ||
331 | { | ||
332 | struct cx8802_dev *dev= fe->dvb->priv; | ||
333 | dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00; | ||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static const struct or51132_config pchdtv_hd3000 = { | ||
338 | .demod_address = 0x15, | ||
339 | .set_ts_params = or51132_set_ts_param, | ||
340 | }; | ||
341 | |||
342 | static int lgdt330x_pll_rf_set(struct dvb_frontend* fe, int index) | ||
343 | { | ||
344 | struct cx8802_dev *dev= fe->dvb->priv; | ||
345 | struct cx88_core *core = dev->core; | ||
346 | |||
347 | dprintk(1, "%s: index = %d\n", __func__, index); | ||
348 | if (index == 0) | ||
349 | cx_clear(MO_GP0_IO, 8); | ||
350 | else | ||
351 | cx_set(MO_GP0_IO, 8); | ||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static int lgdt330x_set_ts_param(struct dvb_frontend* fe, int is_punctured) | ||
356 | { | ||
357 | struct cx8802_dev *dev= fe->dvb->priv; | ||
358 | if (is_punctured) | ||
359 | dev->ts_gen_cntrl |= 0x04; | ||
360 | else | ||
361 | dev->ts_gen_cntrl &= ~0x04; | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | static struct lgdt330x_config fusionhdtv_3_gold = { | ||
366 | .demod_address = 0x0e, | ||
367 | .demod_chip = LGDT3302, | ||
368 | .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */ | ||
369 | .set_ts_params = lgdt330x_set_ts_param, | ||
370 | }; | ||
371 | |||
372 | static const struct lgdt330x_config fusionhdtv_5_gold = { | ||
373 | .demod_address = 0x0e, | ||
374 | .demod_chip = LGDT3303, | ||
375 | .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ | ||
376 | .set_ts_params = lgdt330x_set_ts_param, | ||
377 | }; | ||
378 | |||
379 | static const struct lgdt330x_config pchdtv_hd5500 = { | ||
380 | .demod_address = 0x59, | ||
381 | .demod_chip = LGDT3303, | ||
382 | .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */ | ||
383 | .set_ts_params = lgdt330x_set_ts_param, | ||
384 | }; | ||
385 | |||
386 | static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured) | ||
387 | { | ||
388 | struct cx8802_dev *dev= fe->dvb->priv; | ||
389 | dev->ts_gen_cntrl = is_punctured ? 0x04 : 0x00; | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | static const struct nxt200x_config ati_hdtvwonder = { | ||
394 | .demod_address = 0x0a, | ||
395 | .set_ts_params = nxt200x_set_ts_param, | ||
396 | }; | ||
397 | |||
398 | static int cx24123_set_ts_param(struct dvb_frontend* fe, | ||
399 | int is_punctured) | ||
400 | { | ||
401 | struct cx8802_dev *dev= fe->dvb->priv; | ||
402 | dev->ts_gen_cntrl = 0x02; | ||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, | ||
407 | fe_sec_voltage_t voltage) | ||
408 | { | ||
409 | struct cx8802_dev *dev= fe->dvb->priv; | ||
410 | struct cx88_core *core = dev->core; | ||
411 | |||
412 | if (voltage == SEC_VOLTAGE_OFF) | ||
413 | cx_write(MO_GP0_IO, 0x000006fb); | ||
414 | else | ||
415 | cx_write(MO_GP0_IO, 0x000006f9); | ||
416 | |||
417 | if (core->prev_set_voltage) | ||
418 | return core->prev_set_voltage(fe, voltage); | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe, | ||
423 | fe_sec_voltage_t voltage) | ||
424 | { | ||
425 | struct cx8802_dev *dev= fe->dvb->priv; | ||
426 | struct cx88_core *core = dev->core; | ||
427 | |||
428 | if (voltage == SEC_VOLTAGE_OFF) { | ||
429 | dprintk(1,"LNB Voltage OFF\n"); | ||
430 | cx_write(MO_GP0_IO, 0x0000efff); | ||
431 | } | ||
432 | |||
433 | if (core->prev_set_voltage) | ||
434 | return core->prev_set_voltage(fe, voltage); | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static int tevii_dvbs_set_voltage(struct dvb_frontend *fe, | ||
439 | fe_sec_voltage_t voltage) | ||
440 | { | ||
441 | struct cx8802_dev *dev= fe->dvb->priv; | ||
442 | struct cx88_core *core = dev->core; | ||
443 | |||
444 | cx_set(MO_GP0_IO, 0x6040); | ||
445 | switch (voltage) { | ||
446 | case SEC_VOLTAGE_13: | ||
447 | cx_clear(MO_GP0_IO, 0x20); | ||
448 | break; | ||
449 | case SEC_VOLTAGE_18: | ||
450 | cx_set(MO_GP0_IO, 0x20); | ||
451 | break; | ||
452 | case SEC_VOLTAGE_OFF: | ||
453 | cx_clear(MO_GP0_IO, 0x20); | ||
454 | break; | ||
455 | } | ||
456 | |||
457 | if (core->prev_set_voltage) | ||
458 | return core->prev_set_voltage(fe, voltage); | ||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static int vp1027_set_voltage(struct dvb_frontend *fe, | ||
463 | fe_sec_voltage_t voltage) | ||
464 | { | ||
465 | struct cx8802_dev *dev = fe->dvb->priv; | ||
466 | struct cx88_core *core = dev->core; | ||
467 | |||
468 | switch (voltage) { | ||
469 | case SEC_VOLTAGE_13: | ||
470 | dprintk(1, "LNB SEC Voltage=13\n"); | ||
471 | cx_write(MO_GP0_IO, 0x00001220); | ||
472 | break; | ||
473 | case SEC_VOLTAGE_18: | ||
474 | dprintk(1, "LNB SEC Voltage=18\n"); | ||
475 | cx_write(MO_GP0_IO, 0x00001222); | ||
476 | break; | ||
477 | case SEC_VOLTAGE_OFF: | ||
478 | dprintk(1, "LNB Voltage OFF\n"); | ||
479 | cx_write(MO_GP0_IO, 0x00001230); | ||
480 | break; | ||
481 | } | ||
482 | |||
483 | if (core->prev_set_voltage) | ||
484 | return core->prev_set_voltage(fe, voltage); | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | static const struct cx24123_config geniatech_dvbs_config = { | ||
489 | .demod_address = 0x55, | ||
490 | .set_ts_params = cx24123_set_ts_param, | ||
491 | }; | ||
492 | |||
493 | static const struct cx24123_config hauppauge_novas_config = { | ||
494 | .demod_address = 0x55, | ||
495 | .set_ts_params = cx24123_set_ts_param, | ||
496 | }; | ||
497 | |||
498 | static const struct cx24123_config kworld_dvbs_100_config = { | ||
499 | .demod_address = 0x15, | ||
500 | .set_ts_params = cx24123_set_ts_param, | ||
501 | .lnb_polarity = 1, | ||
502 | }; | ||
503 | |||
504 | static const struct s5h1409_config pinnacle_pctv_hd_800i_config = { | ||
505 | .demod_address = 0x32 >> 1, | ||
506 | .output_mode = S5H1409_PARALLEL_OUTPUT, | ||
507 | .gpio = S5H1409_GPIO_ON, | ||
508 | .qam_if = 44000, | ||
509 | .inversion = S5H1409_INVERSION_OFF, | ||
510 | .status_mode = S5H1409_DEMODLOCKING, | ||
511 | .mpeg_timing = S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK, | ||
512 | }; | ||
513 | |||
514 | static const struct s5h1409_config dvico_hdtv5_pci_nano_config = { | ||
515 | .demod_address = 0x32 >> 1, | ||
516 | .output_mode = S5H1409_SERIAL_OUTPUT, | ||
517 | .gpio = S5H1409_GPIO_OFF, | ||
518 | .inversion = S5H1409_INVERSION_OFF, | ||
519 | .status_mode = S5H1409_DEMODLOCKING, | ||
520 | .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, | ||
521 | }; | ||
522 | |||
523 | static const struct s5h1409_config kworld_atsc_120_config = { | ||
524 | .demod_address = 0x32 >> 1, | ||
525 | .output_mode = S5H1409_SERIAL_OUTPUT, | ||
526 | .gpio = S5H1409_GPIO_OFF, | ||
527 | .inversion = S5H1409_INVERSION_OFF, | ||
528 | .status_mode = S5H1409_DEMODLOCKING, | ||
529 | .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, | ||
530 | }; | ||
531 | |||
532 | static const struct xc5000_config pinnacle_pctv_hd_800i_tuner_config = { | ||
533 | .i2c_address = 0x64, | ||
534 | .if_khz = 5380, | ||
535 | }; | ||
536 | |||
537 | static const struct zl10353_config cx88_pinnacle_hybrid_pctv = { | ||
538 | .demod_address = (0x1e >> 1), | ||
539 | .no_tuner = 1, | ||
540 | .if2 = 45600, | ||
541 | }; | ||
542 | |||
543 | static const struct zl10353_config cx88_geniatech_x8000_mt = { | ||
544 | .demod_address = (0x1e >> 1), | ||
545 | .no_tuner = 1, | ||
546 | .disable_i2c_gate_ctrl = 1, | ||
547 | }; | ||
548 | |||
549 | static const struct s5h1411_config dvico_fusionhdtv7_config = { | ||
550 | .output_mode = S5H1411_SERIAL_OUTPUT, | ||
551 | .gpio = S5H1411_GPIO_ON, | ||
552 | .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, | ||
553 | .qam_if = S5H1411_IF_44000, | ||
554 | .vsb_if = S5H1411_IF_44000, | ||
555 | .inversion = S5H1411_INVERSION_OFF, | ||
556 | .status_mode = S5H1411_DEMODLOCKING | ||
557 | }; | ||
558 | |||
559 | static const struct xc5000_config dvico_fusionhdtv7_tuner_config = { | ||
560 | .i2c_address = 0xc2 >> 1, | ||
561 | .if_khz = 5380, | ||
562 | }; | ||
563 | |||
564 | static int attach_xc3028(u8 addr, struct cx8802_dev *dev) | ||
565 | { | ||
566 | struct dvb_frontend *fe; | ||
567 | struct videobuf_dvb_frontend *fe0 = NULL; | ||
568 | struct xc2028_ctrl ctl; | ||
569 | struct xc2028_config cfg = { | ||
570 | .i2c_adap = &dev->core->i2c_adap, | ||
571 | .i2c_addr = addr, | ||
572 | .ctrl = &ctl, | ||
573 | }; | ||
574 | |||
575 | /* Get the first frontend */ | ||
576 | fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); | ||
577 | if (!fe0) | ||
578 | return -EINVAL; | ||
579 | |||
580 | if (!fe0->dvb.frontend) { | ||
581 | printk(KERN_ERR "%s/2: dvb frontend not attached. " | ||
582 | "Can't attach xc3028\n", | ||
583 | dev->core->name); | ||
584 | return -EINVAL; | ||
585 | } | ||
586 | |||
587 | /* | ||
588 | * Some xc3028 devices may be hidden by an I2C gate. This is known | ||
589 | * to happen with some s5h1409-based devices. | ||
590 | * Now that I2C gate is open, sets up xc3028 configuration | ||
591 | */ | ||
592 | cx88_setup_xc3028(dev->core, &ctl); | ||
593 | |||
594 | fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg); | ||
595 | if (!fe) { | ||
596 | printk(KERN_ERR "%s/2: xc3028 attach failed\n", | ||
597 | dev->core->name); | ||
598 | dvb_frontend_detach(fe0->dvb.frontend); | ||
599 | dvb_unregister_frontend(fe0->dvb.frontend); | ||
600 | fe0->dvb.frontend = NULL; | ||
601 | return -EINVAL; | ||
602 | } | ||
603 | |||
604 | printk(KERN_INFO "%s/2: xc3028 attached\n", | ||
605 | dev->core->name); | ||
606 | |||
607 | return 0; | ||
608 | } | ||
609 | |||
610 | static int attach_xc4000(struct cx8802_dev *dev, struct xc4000_config *cfg) | ||
611 | { | ||
612 | struct dvb_frontend *fe; | ||
613 | struct videobuf_dvb_frontend *fe0 = NULL; | ||
614 | |||
615 | /* Get the first frontend */ | ||
616 | fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); | ||
617 | if (!fe0) | ||
618 | return -EINVAL; | ||
619 | |||
620 | if (!fe0->dvb.frontend) { | ||
621 | printk(KERN_ERR "%s/2: dvb frontend not attached. " | ||
622 | "Can't attach xc4000\n", | ||
623 | dev->core->name); | ||
624 | return -EINVAL; | ||
625 | } | ||
626 | |||
627 | fe = dvb_attach(xc4000_attach, fe0->dvb.frontend, &dev->core->i2c_adap, | ||
628 | cfg); | ||
629 | if (!fe) { | ||
630 | printk(KERN_ERR "%s/2: xc4000 attach failed\n", | ||
631 | dev->core->name); | ||
632 | dvb_frontend_detach(fe0->dvb.frontend); | ||
633 | dvb_unregister_frontend(fe0->dvb.frontend); | ||
634 | fe0->dvb.frontend = NULL; | ||
635 | return -EINVAL; | ||
636 | } | ||
637 | |||
638 | printk(KERN_INFO "%s/2: xc4000 attached\n", dev->core->name); | ||
639 | |||
640 | return 0; | ||
641 | } | ||
642 | |||
643 | static int cx24116_set_ts_param(struct dvb_frontend *fe, | ||
644 | int is_punctured) | ||
645 | { | ||
646 | struct cx8802_dev *dev = fe->dvb->priv; | ||
647 | dev->ts_gen_cntrl = 0x2; | ||
648 | |||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | static int stv0900_set_ts_param(struct dvb_frontend *fe, | ||
653 | int is_punctured) | ||
654 | { | ||
655 | struct cx8802_dev *dev = fe->dvb->priv; | ||
656 | dev->ts_gen_cntrl = 0; | ||
657 | |||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | static int cx24116_reset_device(struct dvb_frontend *fe) | ||
662 | { | ||
663 | struct cx8802_dev *dev = fe->dvb->priv; | ||
664 | struct cx88_core *core = dev->core; | ||
665 | |||
666 | /* Reset the part */ | ||
667 | /* Put the cx24116 into reset */ | ||
668 | cx_write(MO_SRST_IO, 0); | ||
669 | msleep(10); | ||
670 | /* Take the cx24116 out of reset */ | ||
671 | cx_write(MO_SRST_IO, 1); | ||
672 | msleep(10); | ||
673 | |||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | static const struct cx24116_config hauppauge_hvr4000_config = { | ||
678 | .demod_address = 0x05, | ||
679 | .set_ts_params = cx24116_set_ts_param, | ||
680 | .reset_device = cx24116_reset_device, | ||
681 | }; | ||
682 | |||
683 | static const struct cx24116_config tevii_s460_config = { | ||
684 | .demod_address = 0x55, | ||
685 | .set_ts_params = cx24116_set_ts_param, | ||
686 | .reset_device = cx24116_reset_device, | ||
687 | }; | ||
688 | |||
689 | static int ds3000_set_ts_param(struct dvb_frontend *fe, | ||
690 | int is_punctured) | ||
691 | { | ||
692 | struct cx8802_dev *dev = fe->dvb->priv; | ||
693 | dev->ts_gen_cntrl = 4; | ||
694 | |||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | static struct ds3000_config tevii_ds3000_config = { | ||
699 | .demod_address = 0x68, | ||
700 | .set_ts_params = ds3000_set_ts_param, | ||
701 | }; | ||
702 | |||
703 | static const struct stv0900_config prof_7301_stv0900_config = { | ||
704 | .demod_address = 0x6a, | ||
705 | /* demod_mode = 0,*/ | ||
706 | .xtal = 27000000, | ||
707 | .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ | ||
708 | .diseqc_mode = 2,/* 2/3 PWM */ | ||
709 | .tun1_maddress = 0,/* 0x60 */ | ||
710 | .tun1_adc = 0,/* 2 Vpp */ | ||
711 | .path1_mode = 3, | ||
712 | .set_ts_params = stv0900_set_ts_param, | ||
713 | }; | ||
714 | |||
715 | static const struct stb6100_config prof_7301_stb6100_config = { | ||
716 | .tuner_address = 0x60, | ||
717 | .refclock = 27000000, | ||
718 | }; | ||
719 | |||
720 | static const struct stv0299_config tevii_tuner_sharp_config = { | ||
721 | .demod_address = 0x68, | ||
722 | .inittab = sharp_z0194a_inittab, | ||
723 | .mclk = 88000000UL, | ||
724 | .invert = 1, | ||
725 | .skip_reinit = 0, | ||
726 | .lock_output = 1, | ||
727 | .volt13_op0_op1 = STV0299_VOLT13_OP1, | ||
728 | .min_delay_ms = 100, | ||
729 | .set_symbol_rate = sharp_z0194a_set_symbol_rate, | ||
730 | .set_ts_params = cx24116_set_ts_param, | ||
731 | }; | ||
732 | |||
733 | static const struct stv0288_config tevii_tuner_earda_config = { | ||
734 | .demod_address = 0x68, | ||
735 | .min_delay_ms = 100, | ||
736 | .set_ts_params = cx24116_set_ts_param, | ||
737 | }; | ||
738 | |||
739 | static int cx8802_alloc_frontends(struct cx8802_dev *dev) | ||
740 | { | ||
741 | struct cx88_core *core = dev->core; | ||
742 | struct videobuf_dvb_frontend *fe = NULL; | ||
743 | int i; | ||
744 | |||
745 | mutex_init(&dev->frontends.lock); | ||
746 | INIT_LIST_HEAD(&dev->frontends.felist); | ||
747 | |||
748 | if (!core->board.num_frontends) | ||
749 | return -ENODEV; | ||
750 | |||
751 | printk(KERN_INFO "%s() allocating %d frontend(s)\n", __func__, | ||
752 | core->board.num_frontends); | ||
753 | for (i = 1; i <= core->board.num_frontends; i++) { | ||
754 | fe = videobuf_dvb_alloc_frontend(&dev->frontends, i); | ||
755 | if (!fe) { | ||
756 | printk(KERN_ERR "%s() failed to alloc\n", __func__); | ||
757 | videobuf_dvb_dealloc_frontends(&dev->frontends); | ||
758 | return -ENOMEM; | ||
759 | } | ||
760 | } | ||
761 | return 0; | ||
762 | } | ||
763 | |||
764 | |||
765 | |||
766 | static const u8 samsung_smt_7020_inittab[] = { | ||
767 | 0x01, 0x15, | ||
768 | 0x02, 0x00, | ||
769 | 0x03, 0x00, | ||
770 | 0x04, 0x7D, | ||
771 | 0x05, 0x0F, | ||
772 | 0x06, 0x02, | ||
773 | 0x07, 0x00, | ||
774 | 0x08, 0x60, | ||
775 | |||
776 | 0x0A, 0xC2, | ||
777 | 0x0B, 0x00, | ||
778 | 0x0C, 0x01, | ||
779 | 0x0D, 0x81, | ||
780 | 0x0E, 0x44, | ||
781 | 0x0F, 0x09, | ||
782 | 0x10, 0x3C, | ||
783 | 0x11, 0x84, | ||
784 | 0x12, 0xDA, | ||
785 | 0x13, 0x99, | ||
786 | 0x14, 0x8D, | ||
787 | 0x15, 0xCE, | ||
788 | 0x16, 0xE8, | ||
789 | 0x17, 0x43, | ||
790 | 0x18, 0x1C, | ||
791 | 0x19, 0x1B, | ||
792 | 0x1A, 0x1D, | ||
793 | |||
794 | 0x1C, 0x12, | ||
795 | 0x1D, 0x00, | ||
796 | 0x1E, 0x00, | ||
797 | 0x1F, 0x00, | ||
798 | 0x20, 0x00, | ||
799 | 0x21, 0x00, | ||
800 | 0x22, 0x00, | ||
801 | 0x23, 0x00, | ||
802 | |||
803 | 0x28, 0x02, | ||
804 | 0x29, 0x28, | ||
805 | 0x2A, 0x14, | ||
806 | 0x2B, 0x0F, | ||
807 | 0x2C, 0x09, | ||
808 | 0x2D, 0x05, | ||
809 | |||
810 | 0x31, 0x1F, | ||
811 | 0x32, 0x19, | ||
812 | 0x33, 0xFC, | ||
813 | 0x34, 0x13, | ||
814 | 0xff, 0xff, | ||
815 | }; | ||
816 | |||
817 | |||
818 | static int samsung_smt_7020_tuner_set_params(struct dvb_frontend *fe, | ||
819 | struct dvb_frontend_parameters *params) | ||
820 | { | ||
821 | struct cx8802_dev *dev = fe->dvb->priv; | ||
822 | u8 buf[4]; | ||
823 | u32 div; | ||
824 | struct i2c_msg msg = { | ||
825 | .addr = 0x61, | ||
826 | .flags = 0, | ||
827 | .buf = buf, | ||
828 | .len = sizeof(buf) }; | ||
829 | |||
830 | div = params->frequency / 125; | ||
831 | |||
832 | buf[0] = (div >> 8) & 0x7f; | ||
833 | buf[1] = div & 0xff; | ||
834 | buf[2] = 0x84; /* 0xC4 */ | ||
835 | buf[3] = 0x00; | ||
836 | |||
837 | if (params->frequency < 1500000) | ||
838 | buf[3] |= 0x10; | ||
839 | |||
840 | if (fe->ops.i2c_gate_ctrl) | ||
841 | fe->ops.i2c_gate_ctrl(fe, 1); | ||
842 | |||
843 | if (i2c_transfer(&dev->core->i2c_adap, &msg, 1) != 1) | ||
844 | return -EIO; | ||
845 | |||
846 | return 0; | ||
847 | } | ||
848 | |||
849 | static int samsung_smt_7020_set_tone(struct dvb_frontend *fe, | ||
850 | fe_sec_tone_mode_t tone) | ||
851 | { | ||
852 | struct cx8802_dev *dev = fe->dvb->priv; | ||
853 | struct cx88_core *core = dev->core; | ||
854 | |||
855 | cx_set(MO_GP0_IO, 0x0800); | ||
856 | |||
857 | switch (tone) { | ||
858 | case SEC_TONE_ON: | ||
859 | cx_set(MO_GP0_IO, 0x08); | ||
860 | break; | ||
861 | case SEC_TONE_OFF: | ||
862 | cx_clear(MO_GP0_IO, 0x08); | ||
863 | break; | ||
864 | default: | ||
865 | return -EINVAL; | ||
866 | } | ||
867 | |||
868 | return 0; | ||
869 | } | ||
870 | |||
871 | static int samsung_smt_7020_set_voltage(struct dvb_frontend *fe, | ||
872 | fe_sec_voltage_t voltage) | ||
873 | { | ||
874 | struct cx8802_dev *dev = fe->dvb->priv; | ||
875 | struct cx88_core *core = dev->core; | ||
876 | |||
877 | u8 data; | ||
878 | struct i2c_msg msg = { | ||
879 | .addr = 8, | ||
880 | .flags = 0, | ||
881 | .buf = &data, | ||
882 | .len = sizeof(data) }; | ||
883 | |||
884 | cx_set(MO_GP0_IO, 0x8000); | ||
885 | |||
886 | switch (voltage) { | ||
887 | case SEC_VOLTAGE_OFF: | ||
888 | break; | ||
889 | case SEC_VOLTAGE_13: | ||
890 | data = ISL6421_EN1 | ISL6421_LLC1; | ||
891 | cx_clear(MO_GP0_IO, 0x80); | ||
892 | break; | ||
893 | case SEC_VOLTAGE_18: | ||
894 | data = ISL6421_EN1 | ISL6421_LLC1 | ISL6421_VSEL1; | ||
895 | cx_clear(MO_GP0_IO, 0x80); | ||
896 | break; | ||
897 | default: | ||
898 | return -EINVAL; | ||
899 | }; | ||
900 | |||
901 | return (i2c_transfer(&dev->core->i2c_adap, &msg, 1) == 1) ? 0 : -EIO; | ||
902 | } | ||
903 | |||
904 | static int samsung_smt_7020_stv0299_set_symbol_rate(struct dvb_frontend *fe, | ||
905 | u32 srate, u32 ratio) | ||
906 | { | ||
907 | u8 aclk = 0; | ||
908 | u8 bclk = 0; | ||
909 | |||
910 | if (srate < 1500000) { | ||
911 | aclk = 0xb7; | ||
912 | bclk = 0x47; | ||
913 | } else if (srate < 3000000) { | ||
914 | aclk = 0xb7; | ||
915 | bclk = 0x4b; | ||
916 | } else if (srate < 7000000) { | ||
917 | aclk = 0xb7; | ||
918 | bclk = 0x4f; | ||
919 | } else if (srate < 14000000) { | ||
920 | aclk = 0xb7; | ||
921 | bclk = 0x53; | ||
922 | } else if (srate < 30000000) { | ||
923 | aclk = 0xb6; | ||
924 | bclk = 0x53; | ||
925 | } else if (srate < 45000000) { | ||
926 | aclk = 0xb4; | ||
927 | bclk = 0x51; | ||
928 | } | ||
929 | |||
930 | stv0299_writereg(fe, 0x13, aclk); | ||
931 | stv0299_writereg(fe, 0x14, bclk); | ||
932 | stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); | ||
933 | stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); | ||
934 | stv0299_writereg(fe, 0x21, ratio & 0xf0); | ||
935 | |||
936 | return 0; | ||
937 | } | ||
938 | |||
939 | |||
940 | static const struct stv0299_config samsung_stv0299_config = { | ||
941 | .demod_address = 0x68, | ||
942 | .inittab = samsung_smt_7020_inittab, | ||
943 | .mclk = 88000000UL, | ||
944 | .invert = 0, | ||
945 | .skip_reinit = 0, | ||
946 | .lock_output = STV0299_LOCKOUTPUT_LK, | ||
947 | .volt13_op0_op1 = STV0299_VOLT13_OP1, | ||
948 | .min_delay_ms = 100, | ||
949 | .set_symbol_rate = samsung_smt_7020_stv0299_set_symbol_rate, | ||
950 | }; | ||
951 | |||
952 | static int dvb_register(struct cx8802_dev *dev) | ||
953 | { | ||
954 | struct cx88_core *core = dev->core; | ||
955 | struct videobuf_dvb_frontend *fe0, *fe1 = NULL; | ||
956 | int mfe_shared = 0; /* bus not shared by default */ | ||
957 | |||
958 | if (0 != core->i2c_rc) { | ||
959 | printk(KERN_ERR "%s/2: no i2c-bus available, cannot attach dvb drivers\n", core->name); | ||
960 | goto frontend_detach; | ||
961 | } | ||
962 | |||
963 | /* Get the first frontend */ | ||
964 | fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); | ||
965 | if (!fe0) | ||
966 | goto frontend_detach; | ||
967 | |||
968 | /* multi-frontend gate control is undefined or defaults to fe0 */ | ||
969 | dev->frontends.gate = 0; | ||
970 | |||
971 | /* Sets the gate control callback to be used by i2c command calls */ | ||
972 | core->gate_ctrl = cx88_dvb_gate_ctrl; | ||
973 | |||
974 | /* init frontend(s) */ | ||
975 | switch (core->boardnr) { | ||
976 | case CX88_BOARD_HAUPPAUGE_DVB_T1: | ||
977 | fe0->dvb.frontend = dvb_attach(cx22702_attach, | ||
978 | &connexant_refboard_config, | ||
979 | &core->i2c_adap); | ||
980 | if (fe0->dvb.frontend != NULL) { | ||
981 | if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, | ||
982 | 0x61, &core->i2c_adap, | ||
983 | DVB_PLL_THOMSON_DTT759X)) | ||
984 | goto frontend_detach; | ||
985 | } | ||
986 | break; | ||
987 | case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: | ||
988 | case CX88_BOARD_CONEXANT_DVB_T1: | ||
989 | case CX88_BOARD_KWORLD_DVB_T_CX22702: | ||
990 | case CX88_BOARD_WINFAST_DTV1000: | ||
991 | fe0->dvb.frontend = dvb_attach(cx22702_attach, | ||
992 | &connexant_refboard_config, | ||
993 | &core->i2c_adap); | ||
994 | if (fe0->dvb.frontend != NULL) { | ||
995 | if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, | ||
996 | 0x60, &core->i2c_adap, | ||
997 | DVB_PLL_THOMSON_DTT7579)) | ||
998 | goto frontend_detach; | ||
999 | } | ||
1000 | break; | ||
1001 | case CX88_BOARD_WINFAST_DTV2000H: | ||
1002 | case CX88_BOARD_WINFAST_DTV2000H_J: | ||
1003 | case CX88_BOARD_HAUPPAUGE_HVR1100: | ||
1004 | case CX88_BOARD_HAUPPAUGE_HVR1100LP: | ||
1005 | case CX88_BOARD_HAUPPAUGE_HVR1300: | ||
1006 | fe0->dvb.frontend = dvb_attach(cx22702_attach, | ||
1007 | &hauppauge_hvr_config, | ||
1008 | &core->i2c_adap); | ||
1009 | if (fe0->dvb.frontend != NULL) { | ||
1010 | if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, | ||
1011 | &core->i2c_adap, 0x61, | ||
1012 | TUNER_PHILIPS_FMD1216ME_MK3)) | ||
1013 | goto frontend_detach; | ||
1014 | } | ||
1015 | break; | ||
1016 | case CX88_BOARD_HAUPPAUGE_HVR3000: | ||
1017 | /* MFE frontend 1 */ | ||
1018 | mfe_shared = 1; | ||
1019 | dev->frontends.gate = 2; | ||
1020 | /* DVB-S init */ | ||
1021 | fe0->dvb.frontend = dvb_attach(cx24123_attach, | ||
1022 | &hauppauge_novas_config, | ||
1023 | &dev->core->i2c_adap); | ||
1024 | if (fe0->dvb.frontend) { | ||
1025 | if (!dvb_attach(isl6421_attach, | ||
1026 | fe0->dvb.frontend, | ||
1027 | &dev->core->i2c_adap, | ||
1028 | 0x08, ISL6421_DCL, 0x00)) | ||
1029 | goto frontend_detach; | ||
1030 | } | ||
1031 | /* MFE frontend 2 */ | ||
1032 | fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2); | ||
1033 | if (!fe1) | ||
1034 | goto frontend_detach; | ||
1035 | /* DVB-T init */ | ||
1036 | fe1->dvb.frontend = dvb_attach(cx22702_attach, | ||
1037 | &hauppauge_hvr_config, | ||
1038 | &dev->core->i2c_adap); | ||
1039 | if (fe1->dvb.frontend) { | ||
1040 | fe1->dvb.frontend->id = 1; | ||
1041 | if (!dvb_attach(simple_tuner_attach, | ||
1042 | fe1->dvb.frontend, | ||
1043 | &dev->core->i2c_adap, | ||
1044 | 0x61, TUNER_PHILIPS_FMD1216ME_MK3)) | ||
1045 | goto frontend_detach; | ||
1046 | } | ||
1047 | break; | ||
1048 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: | ||
1049 | fe0->dvb.frontend = dvb_attach(mt352_attach, | ||
1050 | &dvico_fusionhdtv, | ||
1051 | &core->i2c_adap); | ||
1052 | if (fe0->dvb.frontend != NULL) { | ||
1053 | if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, | ||
1054 | 0x60, NULL, DVB_PLL_THOMSON_DTT7579)) | ||
1055 | goto frontend_detach; | ||
1056 | break; | ||
1057 | } | ||
1058 | /* ZL10353 replaces MT352 on later cards */ | ||
1059 | fe0->dvb.frontend = dvb_attach(zl10353_attach, | ||
1060 | &dvico_fusionhdtv_plus_v1_1, | ||
1061 | &core->i2c_adap); | ||
1062 | if (fe0->dvb.frontend != NULL) { | ||
1063 | if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, | ||
1064 | 0x60, NULL, DVB_PLL_THOMSON_DTT7579)) | ||
1065 | goto frontend_detach; | ||
1066 | } | ||
1067 | break; | ||
1068 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: | ||
1069 | /* The tin box says DEE1601, but it seems to be DTT7579 | ||
1070 | * compatible, with a slightly different MT352 AGC gain. */ | ||
1071 | fe0->dvb.frontend = dvb_attach(mt352_attach, | ||
1072 | &dvico_fusionhdtv_dual, | ||
1073 | &core->i2c_adap); | ||
1074 | if (fe0->dvb.frontend != NULL) { | ||
1075 | if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, | ||
1076 | 0x61, NULL, DVB_PLL_THOMSON_DTT7579)) | ||
1077 | goto frontend_detach; | ||
1078 | break; | ||
1079 | } | ||
1080 | /* ZL10353 replaces MT352 on later cards */ | ||
1081 | fe0->dvb.frontend = dvb_attach(zl10353_attach, | ||
1082 | &dvico_fusionhdtv_plus_v1_1, | ||
1083 | &core->i2c_adap); | ||
1084 | if (fe0->dvb.frontend != NULL) { | ||
1085 | if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, | ||
1086 | 0x61, NULL, DVB_PLL_THOMSON_DTT7579)) | ||
1087 | goto frontend_detach; | ||
1088 | } | ||
1089 | break; | ||
1090 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: | ||
1091 | fe0->dvb.frontend = dvb_attach(mt352_attach, | ||
1092 | &dvico_fusionhdtv, | ||
1093 | &core->i2c_adap); | ||
1094 | if (fe0->dvb.frontend != NULL) { | ||
1095 | if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, | ||
1096 | 0x61, NULL, DVB_PLL_LG_Z201)) | ||
1097 | goto frontend_detach; | ||
1098 | } | ||
1099 | break; | ||
1100 | case CX88_BOARD_KWORLD_DVB_T: | ||
1101 | case CX88_BOARD_DNTV_LIVE_DVB_T: | ||
1102 | case CX88_BOARD_ADSTECH_DVB_T_PCI: | ||
1103 | fe0->dvb.frontend = dvb_attach(mt352_attach, | ||
1104 | &dntv_live_dvbt_config, | ||
1105 | &core->i2c_adap); | ||
1106 | if (fe0->dvb.frontend != NULL) { | ||
1107 | if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, | ||
1108 | 0x61, NULL, DVB_PLL_UNKNOWN_1)) | ||
1109 | goto frontend_detach; | ||
1110 | } | ||
1111 | break; | ||
1112 | case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: | ||
1113 | #if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) | ||
1114 | /* MT352 is on a secondary I2C bus made from some GPIO lines */ | ||
1115 | fe0->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config, | ||
1116 | &dev->vp3054->adap); | ||
1117 | if (fe0->dvb.frontend != NULL) { | ||
1118 | if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, | ||
1119 | &core->i2c_adap, 0x61, | ||
1120 | TUNER_PHILIPS_FMD1216ME_MK3)) | ||
1121 | goto frontend_detach; | ||
1122 | } | ||
1123 | #else | ||
1124 | printk(KERN_ERR "%s/2: built without vp3054 support\n", | ||
1125 | core->name); | ||
1126 | #endif | ||
1127 | break; | ||
1128 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID: | ||
1129 | fe0->dvb.frontend = dvb_attach(zl10353_attach, | ||
1130 | &dvico_fusionhdtv_hybrid, | ||
1131 | &core->i2c_adap); | ||
1132 | if (fe0->dvb.frontend != NULL) { | ||
1133 | if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, | ||
1134 | &core->i2c_adap, 0x61, | ||
1135 | TUNER_THOMSON_FE6600)) | ||
1136 | goto frontend_detach; | ||
1137 | } | ||
1138 | break; | ||
1139 | case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: | ||
1140 | fe0->dvb.frontend = dvb_attach(zl10353_attach, | ||
1141 | &dvico_fusionhdtv_xc3028, | ||
1142 | &core->i2c_adap); | ||
1143 | if (fe0->dvb.frontend == NULL) | ||
1144 | fe0->dvb.frontend = dvb_attach(mt352_attach, | ||
1145 | &dvico_fusionhdtv_mt352_xc3028, | ||
1146 | &core->i2c_adap); | ||
1147 | /* | ||
1148 | * On this board, the demod provides the I2C bus pullup. | ||
1149 | * We must not permit gate_ctrl to be performed, or | ||
1150 | * the xc3028 cannot communicate on the bus. | ||
1151 | */ | ||
1152 | if (fe0->dvb.frontend) | ||
1153 | fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; | ||
1154 | if (attach_xc3028(0x61, dev) < 0) | ||
1155 | goto frontend_detach; | ||
1156 | break; | ||
1157 | case CX88_BOARD_PCHDTV_HD3000: | ||
1158 | fe0->dvb.frontend = dvb_attach(or51132_attach, &pchdtv_hd3000, | ||
1159 | &core->i2c_adap); | ||
1160 | if (fe0->dvb.frontend != NULL) { | ||
1161 | if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, | ||
1162 | &core->i2c_adap, 0x61, | ||
1163 | TUNER_THOMSON_DTT761X)) | ||
1164 | goto frontend_detach; | ||
1165 | } | ||
1166 | break; | ||
1167 | case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: | ||
1168 | dev->ts_gen_cntrl = 0x08; | ||
1169 | |||
1170 | /* Do a hardware reset of chip before using it. */ | ||
1171 | cx_clear(MO_GP0_IO, 1); | ||
1172 | mdelay(100); | ||
1173 | cx_set(MO_GP0_IO, 1); | ||
1174 | mdelay(200); | ||
1175 | |||
1176 | /* Select RF connector callback */ | ||
1177 | fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set; | ||
1178 | fe0->dvb.frontend = dvb_attach(lgdt330x_attach, | ||
1179 | &fusionhdtv_3_gold, | ||
1180 | &core->i2c_adap); | ||
1181 | if (fe0->dvb.frontend != NULL) { | ||
1182 | if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, | ||
1183 | &core->i2c_adap, 0x61, | ||
1184 | TUNER_MICROTUNE_4042FI5)) | ||
1185 | goto frontend_detach; | ||
1186 | } | ||
1187 | break; | ||
1188 | case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: | ||
1189 | dev->ts_gen_cntrl = 0x08; | ||
1190 | |||
1191 | /* Do a hardware reset of chip before using it. */ | ||
1192 | cx_clear(MO_GP0_IO, 1); | ||
1193 | mdelay(100); | ||
1194 | cx_set(MO_GP0_IO, 9); | ||
1195 | mdelay(200); | ||
1196 | fe0->dvb.frontend = dvb_attach(lgdt330x_attach, | ||
1197 | &fusionhdtv_3_gold, | ||
1198 | &core->i2c_adap); | ||
1199 | if (fe0->dvb.frontend != NULL) { | ||
1200 | if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, | ||
1201 | &core->i2c_adap, 0x61, | ||
1202 | TUNER_THOMSON_DTT761X)) | ||
1203 | goto frontend_detach; | ||
1204 | } | ||
1205 | break; | ||
1206 | case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: | ||
1207 | dev->ts_gen_cntrl = 0x08; | ||
1208 | |||
1209 | /* Do a hardware reset of chip before using it. */ | ||
1210 | cx_clear(MO_GP0_IO, 1); | ||
1211 | mdelay(100); | ||
1212 | cx_set(MO_GP0_IO, 1); | ||
1213 | mdelay(200); | ||
1214 | fe0->dvb.frontend = dvb_attach(lgdt330x_attach, | ||
1215 | &fusionhdtv_5_gold, | ||
1216 | &core->i2c_adap); | ||
1217 | if (fe0->dvb.frontend != NULL) { | ||
1218 | if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, | ||
1219 | &core->i2c_adap, 0x61, | ||
1220 | TUNER_LG_TDVS_H06XF)) | ||
1221 | goto frontend_detach; | ||
1222 | if (!dvb_attach(tda9887_attach, fe0->dvb.frontend, | ||
1223 | &core->i2c_adap, 0x43)) | ||
1224 | goto frontend_detach; | ||
1225 | } | ||
1226 | break; | ||
1227 | case CX88_BOARD_PCHDTV_HD5500: | ||
1228 | dev->ts_gen_cntrl = 0x08; | ||
1229 | |||
1230 | /* Do a hardware reset of chip before using it. */ | ||
1231 | cx_clear(MO_GP0_IO, 1); | ||
1232 | mdelay(100); | ||
1233 | cx_set(MO_GP0_IO, 1); | ||
1234 | mdelay(200); | ||
1235 | fe0->dvb.frontend = dvb_attach(lgdt330x_attach, | ||
1236 | &pchdtv_hd5500, | ||
1237 | &core->i2c_adap); | ||
1238 | if (fe0->dvb.frontend != NULL) { | ||
1239 | if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, | ||
1240 | &core->i2c_adap, 0x61, | ||
1241 | TUNER_LG_TDVS_H06XF)) | ||
1242 | goto frontend_detach; | ||
1243 | if (!dvb_attach(tda9887_attach, fe0->dvb.frontend, | ||
1244 | &core->i2c_adap, 0x43)) | ||
1245 | goto frontend_detach; | ||
1246 | } | ||
1247 | break; | ||
1248 | case CX88_BOARD_ATI_HDTVWONDER: | ||
1249 | fe0->dvb.frontend = dvb_attach(nxt200x_attach, | ||
1250 | &ati_hdtvwonder, | ||
1251 | &core->i2c_adap); | ||
1252 | if (fe0->dvb.frontend != NULL) { | ||
1253 | if (!dvb_attach(simple_tuner_attach, fe0->dvb.frontend, | ||
1254 | &core->i2c_adap, 0x61, | ||
1255 | TUNER_PHILIPS_TUV1236D)) | ||
1256 | goto frontend_detach; | ||
1257 | } | ||
1258 | break; | ||
1259 | case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: | ||
1260 | case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: | ||
1261 | fe0->dvb.frontend = dvb_attach(cx24123_attach, | ||
1262 | &hauppauge_novas_config, | ||
1263 | &core->i2c_adap); | ||
1264 | if (fe0->dvb.frontend) { | ||
1265 | if (!dvb_attach(isl6421_attach, fe0->dvb.frontend, | ||
1266 | &core->i2c_adap, 0x08, ISL6421_DCL, 0x00)) | ||
1267 | goto frontend_detach; | ||
1268 | } | ||
1269 | break; | ||
1270 | case CX88_BOARD_KWORLD_DVBS_100: | ||
1271 | fe0->dvb.frontend = dvb_attach(cx24123_attach, | ||
1272 | &kworld_dvbs_100_config, | ||
1273 | &core->i2c_adap); | ||
1274 | if (fe0->dvb.frontend) { | ||
1275 | core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; | ||
1276 | fe0->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage; | ||
1277 | } | ||
1278 | break; | ||
1279 | case CX88_BOARD_GENIATECH_DVBS: | ||
1280 | fe0->dvb.frontend = dvb_attach(cx24123_attach, | ||
1281 | &geniatech_dvbs_config, | ||
1282 | &core->i2c_adap); | ||
1283 | if (fe0->dvb.frontend) { | ||
1284 | core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; | ||
1285 | fe0->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; | ||
1286 | } | ||
1287 | break; | ||
1288 | case CX88_BOARD_PINNACLE_PCTV_HD_800i: | ||
1289 | fe0->dvb.frontend = dvb_attach(s5h1409_attach, | ||
1290 | &pinnacle_pctv_hd_800i_config, | ||
1291 | &core->i2c_adap); | ||
1292 | if (fe0->dvb.frontend != NULL) { | ||
1293 | if (!dvb_attach(xc5000_attach, fe0->dvb.frontend, | ||
1294 | &core->i2c_adap, | ||
1295 | &pinnacle_pctv_hd_800i_tuner_config)) | ||
1296 | goto frontend_detach; | ||
1297 | } | ||
1298 | break; | ||
1299 | case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: | ||
1300 | fe0->dvb.frontend = dvb_attach(s5h1409_attach, | ||
1301 | &dvico_hdtv5_pci_nano_config, | ||
1302 | &core->i2c_adap); | ||
1303 | if (fe0->dvb.frontend != NULL) { | ||
1304 | struct dvb_frontend *fe; | ||
1305 | struct xc2028_config cfg = { | ||
1306 | .i2c_adap = &core->i2c_adap, | ||
1307 | .i2c_addr = 0x61, | ||
1308 | }; | ||
1309 | static struct xc2028_ctrl ctl = { | ||
1310 | .fname = XC2028_DEFAULT_FIRMWARE, | ||
1311 | .max_len = 64, | ||
1312 | .scode_table = XC3028_FE_OREN538, | ||
1313 | }; | ||
1314 | |||
1315 | fe = dvb_attach(xc2028_attach, | ||
1316 | fe0->dvb.frontend, &cfg); | ||
1317 | if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) | ||
1318 | fe->ops.tuner_ops.set_config(fe, &ctl); | ||
1319 | } | ||
1320 | break; | ||
1321 | case CX88_BOARD_PINNACLE_HYBRID_PCTV: | ||
1322 | case CX88_BOARD_WINFAST_DTV1800H: | ||
1323 | fe0->dvb.frontend = dvb_attach(zl10353_attach, | ||
1324 | &cx88_pinnacle_hybrid_pctv, | ||
1325 | &core->i2c_adap); | ||
1326 | if (fe0->dvb.frontend) { | ||
1327 | fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; | ||
1328 | if (attach_xc3028(0x61, dev) < 0) | ||
1329 | goto frontend_detach; | ||
1330 | } | ||
1331 | break; | ||
1332 | case CX88_BOARD_WINFAST_DTV1800H_XC4000: | ||
1333 | case CX88_BOARD_WINFAST_DTV2000H_PLUS: | ||
1334 | fe0->dvb.frontend = dvb_attach(zl10353_attach, | ||
1335 | &cx88_pinnacle_hybrid_pctv, | ||
1336 | &core->i2c_adap); | ||
1337 | if (fe0->dvb.frontend) { | ||
1338 | struct xc4000_config cfg = { | ||
1339 | .i2c_address = 0x61, | ||
1340 | .default_pm = 0, | ||
1341 | .dvb_amplitude = 134, | ||
1342 | .set_smoothedcvbs = 1, | ||
1343 | .if_khz = 4560 | ||
1344 | }; | ||
1345 | fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; | ||
1346 | if (attach_xc4000(dev, &cfg) < 0) | ||
1347 | goto frontend_detach; | ||
1348 | } | ||
1349 | break; | ||
1350 | case CX88_BOARD_GENIATECH_X8000_MT: | ||
1351 | dev->ts_gen_cntrl = 0x00; | ||
1352 | |||
1353 | fe0->dvb.frontend = dvb_attach(zl10353_attach, | ||
1354 | &cx88_geniatech_x8000_mt, | ||
1355 | &core->i2c_adap); | ||
1356 | if (attach_xc3028(0x61, dev) < 0) | ||
1357 | goto frontend_detach; | ||
1358 | break; | ||
1359 | case CX88_BOARD_KWORLD_ATSC_120: | ||
1360 | fe0->dvb.frontend = dvb_attach(s5h1409_attach, | ||
1361 | &kworld_atsc_120_config, | ||
1362 | &core->i2c_adap); | ||
1363 | if (attach_xc3028(0x61, dev) < 0) | ||
1364 | goto frontend_detach; | ||
1365 | break; | ||
1366 | case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: | ||
1367 | fe0->dvb.frontend = dvb_attach(s5h1411_attach, | ||
1368 | &dvico_fusionhdtv7_config, | ||
1369 | &core->i2c_adap); | ||
1370 | if (fe0->dvb.frontend != NULL) { | ||
1371 | if (!dvb_attach(xc5000_attach, fe0->dvb.frontend, | ||
1372 | &core->i2c_adap, | ||
1373 | &dvico_fusionhdtv7_tuner_config)) | ||
1374 | goto frontend_detach; | ||
1375 | } | ||
1376 | break; | ||
1377 | case CX88_BOARD_HAUPPAUGE_HVR4000: | ||
1378 | /* MFE frontend 1 */ | ||
1379 | mfe_shared = 1; | ||
1380 | dev->frontends.gate = 2; | ||
1381 | /* DVB-S/S2 Init */ | ||
1382 | fe0->dvb.frontend = dvb_attach(cx24116_attach, | ||
1383 | &hauppauge_hvr4000_config, | ||
1384 | &dev->core->i2c_adap); | ||
1385 | if (fe0->dvb.frontend) { | ||
1386 | if (!dvb_attach(isl6421_attach, | ||
1387 | fe0->dvb.frontend, | ||
1388 | &dev->core->i2c_adap, | ||
1389 | 0x08, ISL6421_DCL, 0x00)) | ||
1390 | goto frontend_detach; | ||
1391 | } | ||
1392 | /* MFE frontend 2 */ | ||
1393 | fe1 = videobuf_dvb_get_frontend(&dev->frontends, 2); | ||
1394 | if (!fe1) | ||
1395 | goto frontend_detach; | ||
1396 | /* DVB-T Init */ | ||
1397 | fe1->dvb.frontend = dvb_attach(cx22702_attach, | ||
1398 | &hauppauge_hvr_config, | ||
1399 | &dev->core->i2c_adap); | ||
1400 | if (fe1->dvb.frontend) { | ||
1401 | fe1->dvb.frontend->id = 1; | ||
1402 | if (!dvb_attach(simple_tuner_attach, | ||
1403 | fe1->dvb.frontend, | ||
1404 | &dev->core->i2c_adap, | ||
1405 | 0x61, TUNER_PHILIPS_FMD1216ME_MK3)) | ||
1406 | goto frontend_detach; | ||
1407 | } | ||
1408 | break; | ||
1409 | case CX88_BOARD_HAUPPAUGE_HVR4000LITE: | ||
1410 | fe0->dvb.frontend = dvb_attach(cx24116_attach, | ||
1411 | &hauppauge_hvr4000_config, | ||
1412 | &dev->core->i2c_adap); | ||
1413 | if (fe0->dvb.frontend) { | ||
1414 | if (!dvb_attach(isl6421_attach, | ||
1415 | fe0->dvb.frontend, | ||
1416 | &dev->core->i2c_adap, | ||
1417 | 0x08, ISL6421_DCL, 0x00)) | ||
1418 | goto frontend_detach; | ||
1419 | } | ||
1420 | break; | ||
1421 | case CX88_BOARD_PROF_6200: | ||
1422 | case CX88_BOARD_TBS_8910: | ||
1423 | case CX88_BOARD_TEVII_S420: | ||
1424 | fe0->dvb.frontend = dvb_attach(stv0299_attach, | ||
1425 | &tevii_tuner_sharp_config, | ||
1426 | &core->i2c_adap); | ||
1427 | if (fe0->dvb.frontend != NULL) { | ||
1428 | if (!dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60, | ||
1429 | &core->i2c_adap, DVB_PLL_OPERA1)) | ||
1430 | goto frontend_detach; | ||
1431 | core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; | ||
1432 | fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; | ||
1433 | |||
1434 | } else { | ||
1435 | fe0->dvb.frontend = dvb_attach(stv0288_attach, | ||
1436 | &tevii_tuner_earda_config, | ||
1437 | &core->i2c_adap); | ||
1438 | if (fe0->dvb.frontend != NULL) { | ||
1439 | if (!dvb_attach(stb6000_attach, fe0->dvb.frontend, 0x61, | ||
1440 | &core->i2c_adap)) | ||
1441 | goto frontend_detach; | ||
1442 | core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; | ||
1443 | fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; | ||
1444 | } | ||
1445 | } | ||
1446 | break; | ||
1447 | case CX88_BOARD_TEVII_S460: | ||
1448 | fe0->dvb.frontend = dvb_attach(cx24116_attach, | ||
1449 | &tevii_s460_config, | ||
1450 | &core->i2c_adap); | ||
1451 | if (fe0->dvb.frontend != NULL) | ||
1452 | fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; | ||
1453 | break; | ||
1454 | case CX88_BOARD_TEVII_S464: | ||
1455 | fe0->dvb.frontend = dvb_attach(ds3000_attach, | ||
1456 | &tevii_ds3000_config, | ||
1457 | &core->i2c_adap); | ||
1458 | if (fe0->dvb.frontend != NULL) | ||
1459 | fe0->dvb.frontend->ops.set_voltage = | ||
1460 | tevii_dvbs_set_voltage; | ||
1461 | break; | ||
1462 | case CX88_BOARD_OMICOM_SS4_PCI: | ||
1463 | case CX88_BOARD_TBS_8920: | ||
1464 | case CX88_BOARD_PROF_7300: | ||
1465 | case CX88_BOARD_SATTRADE_ST4200: | ||
1466 | fe0->dvb.frontend = dvb_attach(cx24116_attach, | ||
1467 | &hauppauge_hvr4000_config, | ||
1468 | &core->i2c_adap); | ||
1469 | if (fe0->dvb.frontend != NULL) | ||
1470 | fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; | ||
1471 | break; | ||
1472 | case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: | ||
1473 | fe0->dvb.frontend = dvb_attach(zl10353_attach, | ||
1474 | &cx88_terratec_cinergy_ht_pci_mkii_config, | ||
1475 | &core->i2c_adap); | ||
1476 | if (fe0->dvb.frontend) { | ||
1477 | fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; | ||
1478 | if (attach_xc3028(0x61, dev) < 0) | ||
1479 | goto frontend_detach; | ||
1480 | } | ||
1481 | break; | ||
1482 | case CX88_BOARD_PROF_7301:{ | ||
1483 | struct dvb_tuner_ops *tuner_ops = NULL; | ||
1484 | |||
1485 | fe0->dvb.frontend = dvb_attach(stv0900_attach, | ||
1486 | &prof_7301_stv0900_config, | ||
1487 | &core->i2c_adap, 0); | ||
1488 | if (fe0->dvb.frontend != NULL) { | ||
1489 | if (!dvb_attach(stb6100_attach, fe0->dvb.frontend, | ||
1490 | &prof_7301_stb6100_config, | ||
1491 | &core->i2c_adap)) | ||
1492 | goto frontend_detach; | ||
1493 | |||
1494 | tuner_ops = &fe0->dvb.frontend->ops.tuner_ops; | ||
1495 | tuner_ops->set_frequency = stb6100_set_freq; | ||
1496 | tuner_ops->get_frequency = stb6100_get_freq; | ||
1497 | tuner_ops->set_bandwidth = stb6100_set_bandw; | ||
1498 | tuner_ops->get_bandwidth = stb6100_get_bandw; | ||
1499 | |||
1500 | core->prev_set_voltage = | ||
1501 | fe0->dvb.frontend->ops.set_voltage; | ||
1502 | fe0->dvb.frontend->ops.set_voltage = | ||
1503 | tevii_dvbs_set_voltage; | ||
1504 | } | ||
1505 | break; | ||
1506 | } | ||
1507 | case CX88_BOARD_SAMSUNG_SMT_7020: | ||
1508 | dev->ts_gen_cntrl = 0x08; | ||
1509 | |||
1510 | cx_set(MO_GP0_IO, 0x0101); | ||
1511 | |||
1512 | cx_clear(MO_GP0_IO, 0x01); | ||
1513 | mdelay(100); | ||
1514 | cx_set(MO_GP0_IO, 0x01); | ||
1515 | mdelay(200); | ||
1516 | |||
1517 | fe0->dvb.frontend = dvb_attach(stv0299_attach, | ||
1518 | &samsung_stv0299_config, | ||
1519 | &dev->core->i2c_adap); | ||
1520 | if (fe0->dvb.frontend) { | ||
1521 | fe0->dvb.frontend->ops.tuner_ops.set_params = | ||
1522 | samsung_smt_7020_tuner_set_params; | ||
1523 | fe0->dvb.frontend->tuner_priv = | ||
1524 | &dev->core->i2c_adap; | ||
1525 | fe0->dvb.frontend->ops.set_voltage = | ||
1526 | samsung_smt_7020_set_voltage; | ||
1527 | fe0->dvb.frontend->ops.set_tone = | ||
1528 | samsung_smt_7020_set_tone; | ||
1529 | } | ||
1530 | |||
1531 | break; | ||
1532 | case CX88_BOARD_TWINHAN_VP1027_DVBS: | ||
1533 | dev->ts_gen_cntrl = 0x00; | ||
1534 | fe0->dvb.frontend = dvb_attach(mb86a16_attach, | ||
1535 | &twinhan_vp1027, | ||
1536 | &core->i2c_adap); | ||
1537 | if (fe0->dvb.frontend) { | ||
1538 | core->prev_set_voltage = | ||
1539 | fe0->dvb.frontend->ops.set_voltage; | ||
1540 | fe0->dvb.frontend->ops.set_voltage = | ||
1541 | vp1027_set_voltage; | ||
1542 | } | ||
1543 | break; | ||
1544 | |||
1545 | default: | ||
1546 | printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", | ||
1547 | core->name); | ||
1548 | break; | ||
1549 | } | ||
1550 | |||
1551 | if ( (NULL == fe0->dvb.frontend) || (fe1 && NULL == fe1->dvb.frontend) ) { | ||
1552 | printk(KERN_ERR | ||
1553 | "%s/2: frontend initialization failed\n", | ||
1554 | core->name); | ||
1555 | goto frontend_detach; | ||
1556 | } | ||
1557 | /* define general-purpose callback pointer */ | ||
1558 | fe0->dvb.frontend->callback = cx88_tuner_callback; | ||
1559 | |||
1560 | /* Ensure all frontends negotiate bus access */ | ||
1561 | fe0->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; | ||
1562 | if (fe1) | ||
1563 | fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; | ||
1564 | |||
1565 | /* Put the analog decoder in standby to keep it quiet */ | ||
1566 | call_all(core, core, s_power, 0); | ||
1567 | |||
1568 | /* register everything */ | ||
1569 | return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, | ||
1570 | &dev->pci->dev, adapter_nr, mfe_shared, NULL); | ||
1571 | |||
1572 | frontend_detach: | ||
1573 | core->gate_ctrl = NULL; | ||
1574 | videobuf_dvb_dealloc_frontends(&dev->frontends); | ||
1575 | return -EINVAL; | ||
1576 | } | ||
1577 | |||
1578 | /* ----------------------------------------------------------- */ | ||
1579 | |||
1580 | /* CX8802 MPEG -> mini driver - We have been given the hardware */ | ||
1581 | static int cx8802_dvb_advise_acquire(struct cx8802_driver *drv) | ||
1582 | { | ||
1583 | struct cx88_core *core = drv->core; | ||
1584 | int err = 0; | ||
1585 | dprintk( 1, "%s\n", __func__); | ||
1586 | |||
1587 | switch (core->boardnr) { | ||
1588 | case CX88_BOARD_HAUPPAUGE_HVR1300: | ||
1589 | /* We arrive here with either the cx23416 or the cx22702 | ||
1590 | * on the bus. Take the bus from the cx23416 and enable the | ||
1591 | * cx22702 demod | ||
1592 | */ | ||
1593 | /* Toggle reset on cx22702 leaving i2c active */ | ||
1594 | cx_set(MO_GP0_IO, 0x00000080); | ||
1595 | udelay(1000); | ||
1596 | cx_clear(MO_GP0_IO, 0x00000080); | ||
1597 | udelay(50); | ||
1598 | cx_set(MO_GP0_IO, 0x00000080); | ||
1599 | udelay(1000); | ||
1600 | /* enable the cx22702 pins */ | ||
1601 | cx_clear(MO_GP0_IO, 0x00000004); | ||
1602 | udelay(1000); | ||
1603 | break; | ||
1604 | |||
1605 | case CX88_BOARD_HAUPPAUGE_HVR3000: | ||
1606 | case CX88_BOARD_HAUPPAUGE_HVR4000: | ||
1607 | /* Toggle reset on cx22702 leaving i2c active */ | ||
1608 | cx_set(MO_GP0_IO, 0x00000080); | ||
1609 | udelay(1000); | ||
1610 | cx_clear(MO_GP0_IO, 0x00000080); | ||
1611 | udelay(50); | ||
1612 | cx_set(MO_GP0_IO, 0x00000080); | ||
1613 | udelay(1000); | ||
1614 | switch (core->dvbdev->frontends.active_fe_id) { | ||
1615 | case 1: /* DVB-S/S2 Enabled */ | ||
1616 | /* tri-state the cx22702 pins */ | ||
1617 | cx_set(MO_GP0_IO, 0x00000004); | ||
1618 | /* Take the cx24116/cx24123 out of reset */ | ||
1619 | cx_write(MO_SRST_IO, 1); | ||
1620 | core->dvbdev->ts_gen_cntrl = 0x02; /* Parallel IO */ | ||
1621 | break; | ||
1622 | case 2: /* DVB-T Enabled */ | ||
1623 | /* Put the cx24116/cx24123 into reset */ | ||
1624 | cx_write(MO_SRST_IO, 0); | ||
1625 | /* enable the cx22702 pins */ | ||
1626 | cx_clear(MO_GP0_IO, 0x00000004); | ||
1627 | core->dvbdev->ts_gen_cntrl = 0x0c; /* Serial IO */ | ||
1628 | break; | ||
1629 | } | ||
1630 | udelay(1000); | ||
1631 | break; | ||
1632 | |||
1633 | case CX88_BOARD_WINFAST_DTV2000H_PLUS: | ||
1634 | /* set RF input to AIR for DVB-T (GPIO 16) */ | ||
1635 | cx_write(MO_GP2_IO, 0x0101); | ||
1636 | break; | ||
1637 | |||
1638 | default: | ||
1639 | err = -ENODEV; | ||
1640 | } | ||
1641 | return err; | ||
1642 | } | ||
1643 | |||
1644 | /* CX8802 MPEG -> mini driver - We no longer have the hardware */ | ||
1645 | static int cx8802_dvb_advise_release(struct cx8802_driver *drv) | ||
1646 | { | ||
1647 | struct cx88_core *core = drv->core; | ||
1648 | int err = 0; | ||
1649 | dprintk( 1, "%s\n", __func__); | ||
1650 | |||
1651 | switch (core->boardnr) { | ||
1652 | case CX88_BOARD_HAUPPAUGE_HVR1300: | ||
1653 | /* Do Nothing, leave the cx22702 on the bus. */ | ||
1654 | break; | ||
1655 | case CX88_BOARD_HAUPPAUGE_HVR3000: | ||
1656 | case CX88_BOARD_HAUPPAUGE_HVR4000: | ||
1657 | break; | ||
1658 | default: | ||
1659 | err = -ENODEV; | ||
1660 | } | ||
1661 | return err; | ||
1662 | } | ||
1663 | |||
1664 | static int cx8802_dvb_probe(struct cx8802_driver *drv) | ||
1665 | { | ||
1666 | struct cx88_core *core = drv->core; | ||
1667 | struct cx8802_dev *dev = drv->core->dvbdev; | ||
1668 | int err; | ||
1669 | struct videobuf_dvb_frontend *fe; | ||
1670 | int i; | ||
1671 | |||
1672 | dprintk( 1, "%s\n", __func__); | ||
1673 | dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n", | ||
1674 | core->boardnr, | ||
1675 | core->name, | ||
1676 | core->pci_bus, | ||
1677 | core->pci_slot); | ||
1678 | |||
1679 | err = -ENODEV; | ||
1680 | if (!(core->board.mpeg & CX88_MPEG_DVB)) | ||
1681 | goto fail_core; | ||
1682 | |||
1683 | /* If vp3054 isn't enabled, a stub will just return 0 */ | ||
1684 | err = vp3054_i2c_probe(dev); | ||
1685 | if (0 != err) | ||
1686 | goto fail_core; | ||
1687 | |||
1688 | /* dvb stuff */ | ||
1689 | printk(KERN_INFO "%s/2: cx2388x based DVB/ATSC card\n", core->name); | ||
1690 | dev->ts_gen_cntrl = 0x0c; | ||
1691 | |||
1692 | err = cx8802_alloc_frontends(dev); | ||
1693 | if (err) | ||
1694 | goto fail_core; | ||
1695 | |||
1696 | err = -ENODEV; | ||
1697 | for (i = 1; i <= core->board.num_frontends; i++) { | ||
1698 | fe = videobuf_dvb_get_frontend(&core->dvbdev->frontends, i); | ||
1699 | if (fe == NULL) { | ||
1700 | printk(KERN_ERR "%s() failed to get frontend(%d)\n", | ||
1701 | __func__, i); | ||
1702 | goto fail_probe; | ||
1703 | } | ||
1704 | videobuf_queue_sg_init(&fe->dvb.dvbq, &dvb_qops, | ||
1705 | &dev->pci->dev, &dev->slock, | ||
1706 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
1707 | V4L2_FIELD_TOP, | ||
1708 | sizeof(struct cx88_buffer), | ||
1709 | dev, NULL); | ||
1710 | /* init struct videobuf_dvb */ | ||
1711 | fe->dvb.name = dev->core->name; | ||
1712 | } | ||
1713 | |||
1714 | err = dvb_register(dev); | ||
1715 | if (err) | ||
1716 | /* frontends/adapter de-allocated in dvb_register */ | ||
1717 | printk(KERN_ERR "%s/2: dvb_register failed (err = %d)\n", | ||
1718 | core->name, err); | ||
1719 | return err; | ||
1720 | fail_probe: | ||
1721 | videobuf_dvb_dealloc_frontends(&core->dvbdev->frontends); | ||
1722 | fail_core: | ||
1723 | return err; | ||
1724 | } | ||
1725 | |||
1726 | static int cx8802_dvb_remove(struct cx8802_driver *drv) | ||
1727 | { | ||
1728 | struct cx88_core *core = drv->core; | ||
1729 | struct cx8802_dev *dev = drv->core->dvbdev; | ||
1730 | |||
1731 | dprintk( 1, "%s\n", __func__); | ||
1732 | |||
1733 | videobuf_dvb_unregister_bus(&dev->frontends); | ||
1734 | |||
1735 | vp3054_i2c_remove(dev); | ||
1736 | |||
1737 | core->gate_ctrl = NULL; | ||
1738 | |||
1739 | return 0; | ||
1740 | } | ||
1741 | |||
1742 | static struct cx8802_driver cx8802_dvb_driver = { | ||
1743 | .type_id = CX88_MPEG_DVB, | ||
1744 | .hw_access = CX8802_DRVCTL_SHARED, | ||
1745 | .probe = cx8802_dvb_probe, | ||
1746 | .remove = cx8802_dvb_remove, | ||
1747 | .advise_acquire = cx8802_dvb_advise_acquire, | ||
1748 | .advise_release = cx8802_dvb_advise_release, | ||
1749 | }; | ||
1750 | |||
1751 | static int __init dvb_init(void) | ||
1752 | { | ||
1753 | printk(KERN_INFO "cx88/2: cx2388x dvb driver version %s loaded\n", | ||
1754 | CX88_VERSION); | ||
1755 | return cx8802_register_driver(&cx8802_dvb_driver); | ||
1756 | } | ||
1757 | |||
1758 | static void __exit dvb_fini(void) | ||
1759 | { | ||
1760 | cx8802_unregister_driver(&cx8802_dvb_driver); | ||
1761 | } | ||
1762 | |||
1763 | module_init(dvb_init); | ||
1764 | module_exit(dvb_fini); | ||
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c new file mode 100644 index 00000000000..a1fe0abb6e4 --- /dev/null +++ b/drivers/media/video/cx88/cx88-i2c.c | |||
@@ -0,0 +1,184 @@ | |||
1 | |||
2 | /* | ||
3 | |||
4 | cx88-i2c.c -- all the i2c code is here | ||
5 | |||
6 | Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) | ||
7 | & Marcus Metzler (mocm@thp.uni-koeln.de) | ||
8 | (c) 2002 Yurij Sysoev <yurij@naturesoft.net> | ||
9 | (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org> | ||
10 | |||
11 | (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org> | ||
12 | - Multituner support and i2c address binding | ||
13 | |||
14 | This program is free software; you can redistribute it and/or modify | ||
15 | it under the terms of the GNU General Public License as published by | ||
16 | the Free Software Foundation; either version 2 of the License, or | ||
17 | (at your option) any later version. | ||
18 | |||
19 | This program is distributed in the hope that it will be useful, | ||
20 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
22 | GNU General Public License for more details. | ||
23 | |||
24 | You should have received a copy of the GNU General Public License | ||
25 | along with this program; if not, write to the Free Software | ||
26 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
27 | |||
28 | */ | ||
29 | |||
30 | #include <linux/module.h> | ||
31 | #include <linux/init.h> | ||
32 | |||
33 | #include <asm/io.h> | ||
34 | |||
35 | #include "cx88.h" | ||
36 | #include <media/v4l2-common.h> | ||
37 | |||
38 | static unsigned int i2c_debug; | ||
39 | module_param(i2c_debug, int, 0644); | ||
40 | MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]"); | ||
41 | |||
42 | static unsigned int i2c_scan; | ||
43 | module_param(i2c_scan, int, 0444); | ||
44 | MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); | ||
45 | |||
46 | static unsigned int i2c_udelay = 5; | ||
47 | module_param(i2c_udelay, int, 0644); | ||
48 | MODULE_PARM_DESC(i2c_udelay,"i2c delay at insmod time, in usecs " | ||
49 | "(should be 5 or higher). Lower value means higher bus speed."); | ||
50 | |||
51 | #define dprintk(level,fmt, arg...) if (i2c_debug >= level) \ | ||
52 | printk(KERN_DEBUG "%s: " fmt, core->name , ## arg) | ||
53 | |||
54 | /* ----------------------------------------------------------------------- */ | ||
55 | |||
56 | static void cx8800_bit_setscl(void *data, int state) | ||
57 | { | ||
58 | struct cx88_core *core = data; | ||
59 | |||
60 | if (state) | ||
61 | core->i2c_state |= 0x02; | ||
62 | else | ||
63 | core->i2c_state &= ~0x02; | ||
64 | cx_write(MO_I2C, core->i2c_state); | ||
65 | cx_read(MO_I2C); | ||
66 | } | ||
67 | |||
68 | static void cx8800_bit_setsda(void *data, int state) | ||
69 | { | ||
70 | struct cx88_core *core = data; | ||
71 | |||
72 | if (state) | ||
73 | core->i2c_state |= 0x01; | ||
74 | else | ||
75 | core->i2c_state &= ~0x01; | ||
76 | cx_write(MO_I2C, core->i2c_state); | ||
77 | cx_read(MO_I2C); | ||
78 | } | ||
79 | |||
80 | static int cx8800_bit_getscl(void *data) | ||
81 | { | ||
82 | struct cx88_core *core = data; | ||
83 | u32 state; | ||
84 | |||
85 | state = cx_read(MO_I2C); | ||
86 | return state & 0x02 ? 1 : 0; | ||
87 | } | ||
88 | |||
89 | static int cx8800_bit_getsda(void *data) | ||
90 | { | ||
91 | struct cx88_core *core = data; | ||
92 | u32 state; | ||
93 | |||
94 | state = cx_read(MO_I2C); | ||
95 | return state & 0x01; | ||
96 | } | ||
97 | |||
98 | /* ----------------------------------------------------------------------- */ | ||
99 | |||
100 | static const struct i2c_algo_bit_data cx8800_i2c_algo_template = { | ||
101 | .setsda = cx8800_bit_setsda, | ||
102 | .setscl = cx8800_bit_setscl, | ||
103 | .getsda = cx8800_bit_getsda, | ||
104 | .getscl = cx8800_bit_getscl, | ||
105 | .udelay = 16, | ||
106 | .timeout = 200, | ||
107 | }; | ||
108 | |||
109 | /* ----------------------------------------------------------------------- */ | ||
110 | |||
111 | static const char * const i2c_devs[128] = { | ||
112 | [ 0x1c >> 1 ] = "lgdt330x", | ||
113 | [ 0x86 >> 1 ] = "tda9887/cx22702", | ||
114 | [ 0xa0 >> 1 ] = "eeprom", | ||
115 | [ 0xc0 >> 1 ] = "tuner (analog)", | ||
116 | [ 0xc2 >> 1 ] = "tuner (analog/dvb)", | ||
117 | [ 0xc8 >> 1 ] = "xc5000", | ||
118 | }; | ||
119 | |||
120 | static void do_i2c_scan(const char *name, struct i2c_client *c) | ||
121 | { | ||
122 | unsigned char buf; | ||
123 | int i,rc; | ||
124 | |||
125 | for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { | ||
126 | c->addr = i; | ||
127 | rc = i2c_master_recv(c,&buf,0); | ||
128 | if (rc < 0) | ||
129 | continue; | ||
130 | printk("%s: i2c scan: found device @ 0x%x [%s]\n", | ||
131 | name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | /* init + register i2c algo-bit adapter */ | ||
136 | int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) | ||
137 | { | ||
138 | /* Prevents usage of invalid delay values */ | ||
139 | if (i2c_udelay<5) | ||
140 | i2c_udelay=5; | ||
141 | |||
142 | memcpy(&core->i2c_algo, &cx8800_i2c_algo_template, | ||
143 | sizeof(core->i2c_algo)); | ||
144 | |||
145 | |||
146 | core->i2c_adap.dev.parent = &pci->dev; | ||
147 | strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name)); | ||
148 | core->i2c_adap.owner = THIS_MODULE; | ||
149 | core->i2c_algo.udelay = i2c_udelay; | ||
150 | core->i2c_algo.data = core; | ||
151 | i2c_set_adapdata(&core->i2c_adap, &core->v4l2_dev); | ||
152 | core->i2c_adap.algo_data = &core->i2c_algo; | ||
153 | core->i2c_client.adapter = &core->i2c_adap; | ||
154 | strlcpy(core->i2c_client.name, "cx88xx internal", I2C_NAME_SIZE); | ||
155 | |||
156 | cx8800_bit_setscl(core,1); | ||
157 | cx8800_bit_setsda(core,1); | ||
158 | |||
159 | core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap); | ||
160 | if (0 == core->i2c_rc) { | ||
161 | static u8 tuner_data[] = | ||
162 | { 0x0b, 0xdc, 0x86, 0x52 }; | ||
163 | static struct i2c_msg tuner_msg = | ||
164 | { .flags = 0, .addr = 0xc2 >> 1, .buf = tuner_data, .len = 4 }; | ||
165 | |||
166 | dprintk(1, "i2c register ok\n"); | ||
167 | switch( core->boardnr ) { | ||
168 | case CX88_BOARD_HAUPPAUGE_HVR1300: | ||
169 | case CX88_BOARD_HAUPPAUGE_HVR3000: | ||
170 | case CX88_BOARD_HAUPPAUGE_HVR4000: | ||
171 | printk("%s: i2c init: enabling analog demod on HVR1300/3000/4000 tuner\n", | ||
172 | core->name); | ||
173 | i2c_transfer(core->i2c_client.adapter, &tuner_msg, 1); | ||
174 | break; | ||
175 | default: | ||
176 | break; | ||
177 | } | ||
178 | if (i2c_scan) | ||
179 | do_i2c_scan(core->name,&core->i2c_client); | ||
180 | } else | ||
181 | printk("%s: i2c register FAILED\n", core->name); | ||
182 | |||
183 | return core->i2c_rc; | ||
184 | } | ||
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c new file mode 100644 index 00000000000..e614201b5ed --- /dev/null +++ b/drivers/media/video/cx88/cx88-input.c | |||
@@ -0,0 +1,631 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Device driver for GPIO attached remote control interfaces | ||
4 | * on Conexant 2388x based TV/DVB cards. | ||
5 | * | ||
6 | * Copyright (c) 2003 Pavel Machek | ||
7 | * Copyright (c) 2004 Gerd Knorr | ||
8 | * Copyright (c) 2004, 2005 Chris Pascoe | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include <linux/init.h> | ||
26 | #include <linux/hrtimer.h> | ||
27 | #include <linux/pci.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/module.h> | ||
30 | |||
31 | #include "cx88.h" | ||
32 | #include <media/rc-core.h> | ||
33 | |||
34 | #define MODULE_NAME "cx88xx" | ||
35 | |||
36 | /* ---------------------------------------------------------------------- */ | ||
37 | |||
38 | struct cx88_IR { | ||
39 | struct cx88_core *core; | ||
40 | struct rc_dev *dev; | ||
41 | |||
42 | int users; | ||
43 | |||
44 | char name[32]; | ||
45 | char phys[32]; | ||
46 | |||
47 | /* sample from gpio pin 16 */ | ||
48 | u32 sampling; | ||
49 | |||
50 | /* poll external decoder */ | ||
51 | int polling; | ||
52 | struct hrtimer timer; | ||
53 | u32 gpio_addr; | ||
54 | u32 last_gpio; | ||
55 | u32 mask_keycode; | ||
56 | u32 mask_keydown; | ||
57 | u32 mask_keyup; | ||
58 | }; | ||
59 | |||
60 | static unsigned ir_samplerate = 4; | ||
61 | module_param(ir_samplerate, uint, 0444); | ||
62 | MODULE_PARM_DESC(ir_samplerate, "IR samplerate in kHz, 1 - 20, default 4"); | ||
63 | |||
64 | static int ir_debug; | ||
65 | module_param(ir_debug, int, 0644); /* debug level [IR] */ | ||
66 | MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); | ||
67 | |||
68 | #define ir_dprintk(fmt, arg...) if (ir_debug) \ | ||
69 | printk(KERN_DEBUG "%s IR: " fmt , ir->core->name , ##arg) | ||
70 | |||
71 | #define dprintk(fmt, arg...) if (ir_debug) \ | ||
72 | printk(KERN_DEBUG "cx88 IR: " fmt , ##arg) | ||
73 | |||
74 | /* ---------------------------------------------------------------------- */ | ||
75 | |||
76 | static void cx88_ir_handle_key(struct cx88_IR *ir) | ||
77 | { | ||
78 | struct cx88_core *core = ir->core; | ||
79 | u32 gpio, data, auxgpio; | ||
80 | |||
81 | /* read gpio value */ | ||
82 | gpio = cx_read(ir->gpio_addr); | ||
83 | switch (core->boardnr) { | ||
84 | case CX88_BOARD_NPGTECH_REALTV_TOP10FM: | ||
85 | /* This board apparently uses a combination of 2 GPIO | ||
86 | to represent the keys. Additionally, the second GPIO | ||
87 | can be used for parity. | ||
88 | |||
89 | Example: | ||
90 | |||
91 | for key "5" | ||
92 | gpio = 0x758, auxgpio = 0xe5 or 0xf5 | ||
93 | for key "Power" | ||
94 | gpio = 0x758, auxgpio = 0xed or 0xfd | ||
95 | */ | ||
96 | |||
97 | auxgpio = cx_read(MO_GP1_IO); | ||
98 | /* Take out the parity part */ | ||
99 | gpio=(gpio & 0x7fd) + (auxgpio & 0xef); | ||
100 | break; | ||
101 | case CX88_BOARD_WINFAST_DTV1000: | ||
102 | case CX88_BOARD_WINFAST_DTV1800H: | ||
103 | case CX88_BOARD_WINFAST_DTV1800H_XC4000: | ||
104 | case CX88_BOARD_WINFAST_DTV2000H_PLUS: | ||
105 | case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: | ||
106 | gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900); | ||
107 | auxgpio = gpio; | ||
108 | break; | ||
109 | default: | ||
110 | auxgpio = gpio; | ||
111 | } | ||
112 | if (ir->polling) { | ||
113 | if (ir->last_gpio == auxgpio) | ||
114 | return; | ||
115 | ir->last_gpio = auxgpio; | ||
116 | } | ||
117 | |||
118 | /* extract data */ | ||
119 | data = ir_extract_bits(gpio, ir->mask_keycode); | ||
120 | ir_dprintk("irq gpio=0x%x code=%d | %s%s%s\n", | ||
121 | gpio, data, | ||
122 | ir->polling ? "poll" : "irq", | ||
123 | (gpio & ir->mask_keydown) ? " down" : "", | ||
124 | (gpio & ir->mask_keyup) ? " up" : ""); | ||
125 | |||
126 | if (ir->core->boardnr == CX88_BOARD_NORWOOD_MICRO) { | ||
127 | u32 gpio_key = cx_read(MO_GP0_IO); | ||
128 | |||
129 | data = (data << 4) | ((gpio_key & 0xf0) >> 4); | ||
130 | |||
131 | rc_keydown(ir->dev, data, 0); | ||
132 | |||
133 | } else if (ir->mask_keydown) { | ||
134 | /* bit set on keydown */ | ||
135 | if (gpio & ir->mask_keydown) | ||
136 | rc_keydown_notimeout(ir->dev, data, 0); | ||
137 | else | ||
138 | rc_keyup(ir->dev); | ||
139 | |||
140 | } else if (ir->mask_keyup) { | ||
141 | /* bit cleared on keydown */ | ||
142 | if (0 == (gpio & ir->mask_keyup)) | ||
143 | rc_keydown_notimeout(ir->dev, data, 0); | ||
144 | else | ||
145 | rc_keyup(ir->dev); | ||
146 | |||
147 | } else { | ||
148 | /* can't distinguish keydown/up :-/ */ | ||
149 | rc_keydown_notimeout(ir->dev, data, 0); | ||
150 | rc_keyup(ir->dev); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | static enum hrtimer_restart cx88_ir_work(struct hrtimer *timer) | ||
155 | { | ||
156 | unsigned long missed; | ||
157 | struct cx88_IR *ir = container_of(timer, struct cx88_IR, timer); | ||
158 | |||
159 | cx88_ir_handle_key(ir); | ||
160 | missed = hrtimer_forward_now(&ir->timer, | ||
161 | ktime_set(0, ir->polling * 1000000)); | ||
162 | if (missed > 1) | ||
163 | ir_dprintk("Missed ticks %ld\n", missed - 1); | ||
164 | |||
165 | return HRTIMER_RESTART; | ||
166 | } | ||
167 | |||
168 | static int __cx88_ir_start(void *priv) | ||
169 | { | ||
170 | struct cx88_core *core = priv; | ||
171 | struct cx88_IR *ir; | ||
172 | |||
173 | if (!core || !core->ir) | ||
174 | return -EINVAL; | ||
175 | |||
176 | ir = core->ir; | ||
177 | |||
178 | if (ir->polling) { | ||
179 | hrtimer_init(&ir->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
180 | ir->timer.function = cx88_ir_work; | ||
181 | hrtimer_start(&ir->timer, | ||
182 | ktime_set(0, ir->polling * 1000000), | ||
183 | HRTIMER_MODE_REL); | ||
184 | } | ||
185 | if (ir->sampling) { | ||
186 | core->pci_irqmask |= PCI_INT_IR_SMPINT; | ||
187 | cx_write(MO_DDS_IO, 0x33F286 * ir_samplerate); /* samplerate */ | ||
188 | cx_write(MO_DDSCFG_IO, 0x5); /* enable */ | ||
189 | } | ||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static void __cx88_ir_stop(void *priv) | ||
194 | { | ||
195 | struct cx88_core *core = priv; | ||
196 | struct cx88_IR *ir; | ||
197 | |||
198 | if (!core || !core->ir) | ||
199 | return; | ||
200 | |||
201 | ir = core->ir; | ||
202 | if (ir->sampling) { | ||
203 | cx_write(MO_DDSCFG_IO, 0x0); | ||
204 | core->pci_irqmask &= ~PCI_INT_IR_SMPINT; | ||
205 | } | ||
206 | |||
207 | if (ir->polling) | ||
208 | hrtimer_cancel(&ir->timer); | ||
209 | } | ||
210 | |||
211 | int cx88_ir_start(struct cx88_core *core) | ||
212 | { | ||
213 | if (core->ir->users) | ||
214 | return __cx88_ir_start(core); | ||
215 | |||
216 | return 0; | ||
217 | } | ||
218 | |||
219 | void cx88_ir_stop(struct cx88_core *core) | ||
220 | { | ||
221 | if (core->ir->users) | ||
222 | __cx88_ir_stop(core); | ||
223 | } | ||
224 | |||
225 | static int cx88_ir_open(struct rc_dev *rc) | ||
226 | { | ||
227 | struct cx88_core *core = rc->priv; | ||
228 | |||
229 | core->ir->users++; | ||
230 | return __cx88_ir_start(core); | ||
231 | } | ||
232 | |||
233 | static void cx88_ir_close(struct rc_dev *rc) | ||
234 | { | ||
235 | struct cx88_core *core = rc->priv; | ||
236 | |||
237 | core->ir->users--; | ||
238 | if (!core->ir->users) | ||
239 | __cx88_ir_stop(core); | ||
240 | } | ||
241 | |||
242 | /* ---------------------------------------------------------------------- */ | ||
243 | |||
244 | int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) | ||
245 | { | ||
246 | struct cx88_IR *ir; | ||
247 | struct rc_dev *dev; | ||
248 | char *ir_codes = NULL; | ||
249 | u64 rc_type = RC_TYPE_OTHER; | ||
250 | int err = -ENOMEM; | ||
251 | u32 hardware_mask = 0; /* For devices with a hardware mask, when | ||
252 | * used with a full-code IR table | ||
253 | */ | ||
254 | |||
255 | ir = kzalloc(sizeof(*ir), GFP_KERNEL); | ||
256 | dev = rc_allocate_device(); | ||
257 | if (!ir || !dev) | ||
258 | goto err_out_free; | ||
259 | |||
260 | ir->dev = dev; | ||
261 | |||
262 | /* detect & configure */ | ||
263 | switch (core->boardnr) { | ||
264 | case CX88_BOARD_DNTV_LIVE_DVB_T: | ||
265 | case CX88_BOARD_KWORLD_DVB_T: | ||
266 | case CX88_BOARD_KWORLD_DVB_T_CX22702: | ||
267 | ir_codes = RC_MAP_DNTV_LIVE_DVB_T; | ||
268 | ir->gpio_addr = MO_GP1_IO; | ||
269 | ir->mask_keycode = 0x1f; | ||
270 | ir->mask_keyup = 0x60; | ||
271 | ir->polling = 50; /* ms */ | ||
272 | break; | ||
273 | case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: | ||
274 | ir_codes = RC_MAP_CINERGY_1400; | ||
275 | ir->sampling = 0xeb04; /* address */ | ||
276 | break; | ||
277 | case CX88_BOARD_HAUPPAUGE: | ||
278 | case CX88_BOARD_HAUPPAUGE_DVB_T1: | ||
279 | case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: | ||
280 | case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: | ||
281 | case CX88_BOARD_HAUPPAUGE_HVR1100: | ||
282 | case CX88_BOARD_HAUPPAUGE_HVR3000: | ||
283 | case CX88_BOARD_HAUPPAUGE_HVR4000: | ||
284 | case CX88_BOARD_HAUPPAUGE_HVR4000LITE: | ||
285 | case CX88_BOARD_PCHDTV_HD3000: | ||
286 | case CX88_BOARD_PCHDTV_HD5500: | ||
287 | case CX88_BOARD_HAUPPAUGE_IRONLY: | ||
288 | ir_codes = RC_MAP_HAUPPAUGE; | ||
289 | ir->sampling = 1; | ||
290 | break; | ||
291 | case CX88_BOARD_WINFAST_DTV2000H: | ||
292 | case CX88_BOARD_WINFAST_DTV2000H_J: | ||
293 | case CX88_BOARD_WINFAST_DTV1800H: | ||
294 | case CX88_BOARD_WINFAST_DTV1800H_XC4000: | ||
295 | case CX88_BOARD_WINFAST_DTV2000H_PLUS: | ||
296 | ir_codes = RC_MAP_WINFAST; | ||
297 | ir->gpio_addr = MO_GP0_IO; | ||
298 | ir->mask_keycode = 0x8f8; | ||
299 | ir->mask_keyup = 0x100; | ||
300 | ir->polling = 50; /* ms */ | ||
301 | break; | ||
302 | case CX88_BOARD_WINFAST2000XP_EXPERT: | ||
303 | case CX88_BOARD_WINFAST_DTV1000: | ||
304 | case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: | ||
305 | ir_codes = RC_MAP_WINFAST; | ||
306 | ir->gpio_addr = MO_GP0_IO; | ||
307 | ir->mask_keycode = 0x8f8; | ||
308 | ir->mask_keyup = 0x100; | ||
309 | ir->polling = 1; /* ms */ | ||
310 | break; | ||
311 | case CX88_BOARD_IODATA_GVBCTV7E: | ||
312 | ir_codes = RC_MAP_IODATA_BCTV7E; | ||
313 | ir->gpio_addr = MO_GP0_IO; | ||
314 | ir->mask_keycode = 0xfd; | ||
315 | ir->mask_keydown = 0x02; | ||
316 | ir->polling = 5; /* ms */ | ||
317 | break; | ||
318 | case CX88_BOARD_PROLINK_PLAYTVPVR: | ||
319 | case CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO: | ||
320 | /* | ||
321 | * It seems that this hardware is paired with NEC extended | ||
322 | * address 0x866b. So, unfortunately, its usage with other | ||
323 | * IR's with different address won't work. Still, there are | ||
324 | * other IR's from the same manufacturer that works, like the | ||
325 | * 002-T mini RC, provided with newer PV hardware | ||
326 | */ | ||
327 | ir_codes = RC_MAP_PIXELVIEW_MK12; | ||
328 | ir->gpio_addr = MO_GP1_IO; | ||
329 | ir->mask_keyup = 0x80; | ||
330 | ir->polling = 10; /* ms */ | ||
331 | hardware_mask = 0x3f; /* Hardware returns only 6 bits from command part */ | ||
332 | break; | ||
333 | case CX88_BOARD_PROLINK_PV_8000GT: | ||
334 | case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: | ||
335 | ir_codes = RC_MAP_PIXELVIEW_NEW; | ||
336 | ir->gpio_addr = MO_GP1_IO; | ||
337 | ir->mask_keycode = 0x3f; | ||
338 | ir->mask_keyup = 0x80; | ||
339 | ir->polling = 1; /* ms */ | ||
340 | break; | ||
341 | case CX88_BOARD_KWORLD_LTV883: | ||
342 | ir_codes = RC_MAP_PIXELVIEW; | ||
343 | ir->gpio_addr = MO_GP1_IO; | ||
344 | ir->mask_keycode = 0x1f; | ||
345 | ir->mask_keyup = 0x60; | ||
346 | ir->polling = 1; /* ms */ | ||
347 | break; | ||
348 | case CX88_BOARD_ADSTECH_DVB_T_PCI: | ||
349 | ir_codes = RC_MAP_ADSTECH_DVB_T_PCI; | ||
350 | ir->gpio_addr = MO_GP1_IO; | ||
351 | ir->mask_keycode = 0xbf; | ||
352 | ir->mask_keyup = 0x40; | ||
353 | ir->polling = 50; /* ms */ | ||
354 | break; | ||
355 | case CX88_BOARD_MSI_TVANYWHERE_MASTER: | ||
356 | ir_codes = RC_MAP_MSI_TVANYWHERE; | ||
357 | ir->gpio_addr = MO_GP1_IO; | ||
358 | ir->mask_keycode = 0x1f; | ||
359 | ir->mask_keyup = 0x40; | ||
360 | ir->polling = 1; /* ms */ | ||
361 | break; | ||
362 | case CX88_BOARD_AVERTV_303: | ||
363 | case CX88_BOARD_AVERTV_STUDIO_303: | ||
364 | ir_codes = RC_MAP_AVERTV_303; | ||
365 | ir->gpio_addr = MO_GP2_IO; | ||
366 | ir->mask_keycode = 0xfb; | ||
367 | ir->mask_keydown = 0x02; | ||
368 | ir->polling = 50; /* ms */ | ||
369 | break; | ||
370 | case CX88_BOARD_OMICOM_SS4_PCI: | ||
371 | case CX88_BOARD_SATTRADE_ST4200: | ||
372 | case CX88_BOARD_TBS_8920: | ||
373 | case CX88_BOARD_TBS_8910: | ||
374 | case CX88_BOARD_PROF_7300: | ||
375 | case CX88_BOARD_PROF_7301: | ||
376 | case CX88_BOARD_PROF_6200: | ||
377 | ir_codes = RC_MAP_TBS_NEC; | ||
378 | ir->sampling = 0xff00; /* address */ | ||
379 | break; | ||
380 | case CX88_BOARD_TEVII_S464: | ||
381 | case CX88_BOARD_TEVII_S460: | ||
382 | case CX88_BOARD_TEVII_S420: | ||
383 | ir_codes = RC_MAP_TEVII_NEC; | ||
384 | ir->sampling = 0xff00; /* address */ | ||
385 | break; | ||
386 | case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: | ||
387 | ir_codes = RC_MAP_DNTV_LIVE_DVBT_PRO; | ||
388 | ir->sampling = 0xff00; /* address */ | ||
389 | break; | ||
390 | case CX88_BOARD_NORWOOD_MICRO: | ||
391 | ir_codes = RC_MAP_NORWOOD; | ||
392 | ir->gpio_addr = MO_GP1_IO; | ||
393 | ir->mask_keycode = 0x0e; | ||
394 | ir->mask_keyup = 0x80; | ||
395 | ir->polling = 50; /* ms */ | ||
396 | break; | ||
397 | case CX88_BOARD_NPGTECH_REALTV_TOP10FM: | ||
398 | ir_codes = RC_MAP_NPGTECH; | ||
399 | ir->gpio_addr = MO_GP0_IO; | ||
400 | ir->mask_keycode = 0xfa; | ||
401 | ir->polling = 50; /* ms */ | ||
402 | break; | ||
403 | case CX88_BOARD_PINNACLE_PCTV_HD_800i: | ||
404 | ir_codes = RC_MAP_PINNACLE_PCTV_HD; | ||
405 | ir->sampling = 1; | ||
406 | break; | ||
407 | case CX88_BOARD_POWERCOLOR_REAL_ANGEL: | ||
408 | ir_codes = RC_MAP_POWERCOLOR_REAL_ANGEL; | ||
409 | ir->gpio_addr = MO_GP2_IO; | ||
410 | ir->mask_keycode = 0x7e; | ||
411 | ir->polling = 100; /* ms */ | ||
412 | break; | ||
413 | case CX88_BOARD_TWINHAN_VP1027_DVBS: | ||
414 | ir_codes = RC_MAP_TWINHAN_VP1027_DVBS; | ||
415 | rc_type = RC_TYPE_NEC; | ||
416 | ir->sampling = 0xff00; /* address */ | ||
417 | break; | ||
418 | } | ||
419 | |||
420 | if (!ir_codes) { | ||
421 | err = -ENODEV; | ||
422 | goto err_out_free; | ||
423 | } | ||
424 | |||
425 | /* | ||
426 | * The usage of mask_keycode were very convenient, due to several | ||
427 | * reasons. Among others, the scancode tables were using the scancode | ||
428 | * as the index elements. So, the less bits it was used, the smaller | ||
429 | * the table were stored. After the input changes, the better is to use | ||
430 | * the full scancodes, since it allows replacing the IR remote by | ||
431 | * another one. Unfortunately, there are still some hardware, like | ||
432 | * Pixelview Ultra Pro, where only part of the scancode is sent via | ||
433 | * GPIO. So, there's no way to get the full scancode. Due to that, | ||
434 | * hardware_mask were introduced here: it represents those hardware | ||
435 | * that has such limits. | ||
436 | */ | ||
437 | if (hardware_mask && !ir->mask_keycode) | ||
438 | ir->mask_keycode = hardware_mask; | ||
439 | |||
440 | /* init input device */ | ||
441 | snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); | ||
442 | snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); | ||
443 | |||
444 | dev->input_name = ir->name; | ||
445 | dev->input_phys = ir->phys; | ||
446 | dev->input_id.bustype = BUS_PCI; | ||
447 | dev->input_id.version = 1; | ||
448 | if (pci->subsystem_vendor) { | ||
449 | dev->input_id.vendor = pci->subsystem_vendor; | ||
450 | dev->input_id.product = pci->subsystem_device; | ||
451 | } else { | ||
452 | dev->input_id.vendor = pci->vendor; | ||
453 | dev->input_id.product = pci->device; | ||
454 | } | ||
455 | dev->dev.parent = &pci->dev; | ||
456 | dev->map_name = ir_codes; | ||
457 | dev->driver_name = MODULE_NAME; | ||
458 | dev->priv = core; | ||
459 | dev->open = cx88_ir_open; | ||
460 | dev->close = cx88_ir_close; | ||
461 | dev->scanmask = hardware_mask; | ||
462 | |||
463 | if (ir->sampling) { | ||
464 | dev->driver_type = RC_DRIVER_IR_RAW; | ||
465 | dev->timeout = 10 * 1000 * 1000; /* 10 ms */ | ||
466 | } else { | ||
467 | dev->driver_type = RC_DRIVER_SCANCODE; | ||
468 | dev->allowed_protos = rc_type; | ||
469 | } | ||
470 | |||
471 | ir->core = core; | ||
472 | core->ir = ir; | ||
473 | |||
474 | /* all done */ | ||
475 | err = rc_register_device(dev); | ||
476 | if (err) | ||
477 | goto err_out_free; | ||
478 | |||
479 | return 0; | ||
480 | |||
481 | err_out_free: | ||
482 | rc_free_device(dev); | ||
483 | core->ir = NULL; | ||
484 | kfree(ir); | ||
485 | return err; | ||
486 | } | ||
487 | |||
488 | int cx88_ir_fini(struct cx88_core *core) | ||
489 | { | ||
490 | struct cx88_IR *ir = core->ir; | ||
491 | |||
492 | /* skip detach on non attached boards */ | ||
493 | if (NULL == ir) | ||
494 | return 0; | ||
495 | |||
496 | cx88_ir_stop(core); | ||
497 | rc_unregister_device(ir->dev); | ||
498 | kfree(ir); | ||
499 | |||
500 | /* done */ | ||
501 | core->ir = NULL; | ||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | /* ---------------------------------------------------------------------- */ | ||
506 | |||
507 | void cx88_ir_irq(struct cx88_core *core) | ||
508 | { | ||
509 | struct cx88_IR *ir = core->ir; | ||
510 | u32 samples; | ||
511 | unsigned todo, bits; | ||
512 | struct ir_raw_event ev; | ||
513 | |||
514 | if (!ir || !ir->sampling) | ||
515 | return; | ||
516 | |||
517 | /* | ||
518 | * Samples are stored in a 32 bit register, oldest sample in | ||
519 | * the msb. A set bit represents space and an unset bit | ||
520 | * represents a pulse. | ||
521 | */ | ||
522 | samples = cx_read(MO_SAMPLE_IO); | ||
523 | |||
524 | if (samples == 0xff && ir->dev->idle) | ||
525 | return; | ||
526 | |||
527 | init_ir_raw_event(&ev); | ||
528 | for (todo = 32; todo > 0; todo -= bits) { | ||
529 | ev.pulse = samples & 0x80000000 ? false : true; | ||
530 | bits = min(todo, 32U - fls(ev.pulse ? samples : ~samples)); | ||
531 | ev.duration = (bits * (NSEC_PER_SEC / 1000)) / ir_samplerate; | ||
532 | ir_raw_event_store_with_filter(ir->dev, &ev); | ||
533 | samples <<= bits; | ||
534 | } | ||
535 | ir_raw_event_handle(ir->dev); | ||
536 | } | ||
537 | |||
538 | static int get_key_pvr2000(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) | ||
539 | { | ||
540 | int flags, code; | ||
541 | |||
542 | /* poll IR chip */ | ||
543 | flags = i2c_smbus_read_byte_data(ir->c, 0x10); | ||
544 | if (flags < 0) { | ||
545 | dprintk("read error\n"); | ||
546 | return 0; | ||
547 | } | ||
548 | /* key pressed ? */ | ||
549 | if (0 == (flags & 0x80)) | ||
550 | return 0; | ||
551 | |||
552 | /* read actual key code */ | ||
553 | code = i2c_smbus_read_byte_data(ir->c, 0x00); | ||
554 | if (code < 0) { | ||
555 | dprintk("read error\n"); | ||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", | ||
560 | code & 0xff, flags & 0xff); | ||
561 | |||
562 | *ir_key = code & 0xff; | ||
563 | *ir_raw = code; | ||
564 | return 1; | ||
565 | } | ||
566 | |||
567 | void cx88_i2c_init_ir(struct cx88_core *core) | ||
568 | { | ||
569 | struct i2c_board_info info; | ||
570 | const unsigned short default_addr_list[] = { | ||
571 | 0x18, 0x6b, 0x71, | ||
572 | I2C_CLIENT_END | ||
573 | }; | ||
574 | const unsigned short pvr2000_addr_list[] = { | ||
575 | 0x18, 0x1a, | ||
576 | I2C_CLIENT_END | ||
577 | }; | ||
578 | const unsigned short *addr_list = default_addr_list; | ||
579 | const unsigned short *addrp; | ||
580 | /* Instantiate the IR receiver device, if present */ | ||
581 | if (0 != core->i2c_rc) | ||
582 | return; | ||
583 | |||
584 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
585 | strlcpy(info.type, "ir_video", I2C_NAME_SIZE); | ||
586 | |||
587 | switch (core->boardnr) { | ||
588 | case CX88_BOARD_LEADTEK_PVR2000: | ||
589 | addr_list = pvr2000_addr_list; | ||
590 | core->init_data.name = "cx88 Leadtek PVR 2000 remote"; | ||
591 | core->init_data.type = RC_TYPE_UNKNOWN; | ||
592 | core->init_data.get_key = get_key_pvr2000; | ||
593 | core->init_data.ir_codes = RC_MAP_EMPTY; | ||
594 | break; | ||
595 | } | ||
596 | |||
597 | /* | ||
598 | * We can't call i2c_new_probed_device() because it uses | ||
599 | * quick writes for probing and at least some RC receiver | ||
600 | * devices only reply to reads. | ||
601 | * Also, Hauppauge XVR needs to be specified, as address 0x71 | ||
602 | * conflicts with another remote type used with saa7134 | ||
603 | */ | ||
604 | for (addrp = addr_list; *addrp != I2C_CLIENT_END; addrp++) { | ||
605 | info.platform_data = NULL; | ||
606 | memset(&core->init_data, 0, sizeof(core->init_data)); | ||
607 | |||
608 | if (*addrp == 0x71) { | ||
609 | /* Hauppauge XVR */ | ||
610 | core->init_data.name = "cx88 Hauppauge XVR remote"; | ||
611 | core->init_data.ir_codes = RC_MAP_HAUPPAUGE; | ||
612 | core->init_data.type = RC_TYPE_RC5; | ||
613 | core->init_data.internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; | ||
614 | |||
615 | info.platform_data = &core->init_data; | ||
616 | } | ||
617 | if (i2c_smbus_xfer(&core->i2c_adap, *addrp, 0, | ||
618 | I2C_SMBUS_READ, 0, | ||
619 | I2C_SMBUS_QUICK, NULL) >= 0) { | ||
620 | info.addr = *addrp; | ||
621 | i2c_new_device(&core->i2c_adap, &info); | ||
622 | break; | ||
623 | } | ||
624 | } | ||
625 | } | ||
626 | |||
627 | /* ---------------------------------------------------------------------- */ | ||
628 | |||
629 | MODULE_AUTHOR("Gerd Knorr, Pavel Machek, Chris Pascoe"); | ||
630 | MODULE_DESCRIPTION("input driver for cx88 GPIO-based IR remote controls"); | ||
631 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c new file mode 100644 index 00000000000..cd5386ee210 --- /dev/null +++ b/drivers/media/video/cx88/cx88-mpeg.c | |||
@@ -0,0 +1,929 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Support for the mpeg transport stream transfers | ||
4 | * PCI function #2 of the cx2388x. | ||
5 | * | ||
6 | * (c) 2004 Jelle Foks <jelle@foks.us> | ||
7 | * (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au> | ||
8 | * (c) 2004 Gerd Knorr <kraxel@bytesex.org> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | */ | ||
24 | |||
25 | #include <linux/module.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/device.h> | ||
29 | #include <linux/dma-mapping.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <asm/delay.h> | ||
32 | |||
33 | #include "cx88.h" | ||
34 | |||
35 | /* ------------------------------------------------------------------ */ | ||
36 | |||
37 | MODULE_DESCRIPTION("mpeg driver for cx2388x based TV cards"); | ||
38 | MODULE_AUTHOR("Jelle Foks <jelle@foks.us>"); | ||
39 | MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); | ||
40 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
41 | MODULE_LICENSE("GPL"); | ||
42 | MODULE_VERSION(CX88_VERSION); | ||
43 | |||
44 | static unsigned int debug; | ||
45 | module_param(debug,int,0644); | ||
46 | MODULE_PARM_DESC(debug,"enable debug messages [mpeg]"); | ||
47 | |||
48 | #define dprintk(level,fmt, arg...) if (debug >= level) \ | ||
49 | printk(KERN_DEBUG "%s/2-mpeg: " fmt, dev->core->name, ## arg) | ||
50 | |||
51 | #define mpeg_dbg(level,fmt, arg...) if (debug >= level) \ | ||
52 | printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg) | ||
53 | |||
54 | #if defined(CONFIG_MODULES) && defined(MODULE) | ||
55 | static void request_module_async(struct work_struct *work) | ||
56 | { | ||
57 | struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk); | ||
58 | |||
59 | if (dev->core->board.mpeg & CX88_MPEG_DVB) | ||
60 | request_module("cx88-dvb"); | ||
61 | if (dev->core->board.mpeg & CX88_MPEG_BLACKBIRD) | ||
62 | request_module("cx88-blackbird"); | ||
63 | } | ||
64 | |||
65 | static void request_modules(struct cx8802_dev *dev) | ||
66 | { | ||
67 | INIT_WORK(&dev->request_module_wk, request_module_async); | ||
68 | schedule_work(&dev->request_module_wk); | ||
69 | } | ||
70 | |||
71 | static void flush_request_modules(struct cx8802_dev *dev) | ||
72 | { | ||
73 | flush_work_sync(&dev->request_module_wk); | ||
74 | } | ||
75 | #else | ||
76 | #define request_modules(dev) | ||
77 | #define flush_request_modules(dev) | ||
78 | #endif /* CONFIG_MODULES */ | ||
79 | |||
80 | |||
81 | static LIST_HEAD(cx8802_devlist); | ||
82 | static DEFINE_MUTEX(cx8802_mutex); | ||
83 | /* ------------------------------------------------------------------ */ | ||
84 | |||
85 | static int cx8802_start_dma(struct cx8802_dev *dev, | ||
86 | struct cx88_dmaqueue *q, | ||
87 | struct cx88_buffer *buf) | ||
88 | { | ||
89 | struct cx88_core *core = dev->core; | ||
90 | |||
91 | dprintk(1, "cx8802_start_dma w: %d, h: %d, f: %d\n", | ||
92 | buf->vb.width, buf->vb.height, buf->vb.field); | ||
93 | |||
94 | /* setup fifo + format */ | ||
95 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], | ||
96 | dev->ts_packet_size, buf->risc.dma); | ||
97 | |||
98 | /* write TS length to chip */ | ||
99 | cx_write(MO_TS_LNGTH, buf->vb.width); | ||
100 | |||
101 | /* FIXME: this needs a review. | ||
102 | * also: move to cx88-blackbird + cx88-dvb source files? */ | ||
103 | |||
104 | dprintk( 1, "core->active_type_id = 0x%08x\n", core->active_type_id); | ||
105 | |||
106 | if ( (core->active_type_id == CX88_MPEG_DVB) && | ||
107 | (core->board.mpeg & CX88_MPEG_DVB) ) { | ||
108 | |||
109 | dprintk( 1, "cx8802_start_dma doing .dvb\n"); | ||
110 | /* negedge driven & software reset */ | ||
111 | cx_write(TS_GEN_CNTRL, 0x0040 | dev->ts_gen_cntrl); | ||
112 | udelay(100); | ||
113 | cx_write(MO_PINMUX_IO, 0x00); | ||
114 | cx_write(TS_HW_SOP_CNTRL, 0x47<<16|188<<4|0x01); | ||
115 | switch (core->boardnr) { | ||
116 | case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: | ||
117 | case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T: | ||
118 | case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: | ||
119 | case CX88_BOARD_PCHDTV_HD5500: | ||
120 | cx_write(TS_SOP_STAT, 1<<13); | ||
121 | break; | ||
122 | case CX88_BOARD_SAMSUNG_SMT_7020: | ||
123 | cx_write(TS_SOP_STAT, 0x00); | ||
124 | break; | ||
125 | case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: | ||
126 | case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: | ||
127 | cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */ | ||
128 | udelay(100); | ||
129 | break; | ||
130 | case CX88_BOARD_HAUPPAUGE_HVR1300: | ||
131 | /* Enable MPEG parallel IO and video signal pins */ | ||
132 | cx_write(MO_PINMUX_IO, 0x88); | ||
133 | cx_write(TS_SOP_STAT, 0); | ||
134 | cx_write(TS_VALERR_CNTRL, 0); | ||
135 | break; | ||
136 | case CX88_BOARD_PINNACLE_PCTV_HD_800i: | ||
137 | /* Enable MPEG parallel IO and video signal pins */ | ||
138 | cx_write(MO_PINMUX_IO, 0x88); | ||
139 | cx_write(TS_HW_SOP_CNTRL, (0x47 << 16) | (188 << 4)); | ||
140 | dev->ts_gen_cntrl = 5; | ||
141 | cx_write(TS_SOP_STAT, 0); | ||
142 | cx_write(TS_VALERR_CNTRL, 0); | ||
143 | udelay(100); | ||
144 | break; | ||
145 | default: | ||
146 | cx_write(TS_SOP_STAT, 0x00); | ||
147 | break; | ||
148 | } | ||
149 | cx_write(TS_GEN_CNTRL, dev->ts_gen_cntrl); | ||
150 | udelay(100); | ||
151 | } else if ( (core->active_type_id == CX88_MPEG_BLACKBIRD) && | ||
152 | (core->board.mpeg & CX88_MPEG_BLACKBIRD) ) { | ||
153 | dprintk( 1, "cx8802_start_dma doing .blackbird\n"); | ||
154 | cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */ | ||
155 | |||
156 | cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */ | ||
157 | udelay(100); | ||
158 | |||
159 | cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */ | ||
160 | cx_write(TS_VALERR_CNTRL, 0x2000); | ||
161 | |||
162 | cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */ | ||
163 | udelay(100); | ||
164 | } else { | ||
165 | printk( "%s() Failed. Unsupported value in .mpeg (0x%08x)\n", __func__, | ||
166 | core->board.mpeg ); | ||
167 | return -EINVAL; | ||
168 | } | ||
169 | |||
170 | /* reset counter */ | ||
171 | cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET); | ||
172 | q->count = 1; | ||
173 | |||
174 | /* enable irqs */ | ||
175 | dprintk( 1, "setting the interrupt mask\n" ); | ||
176 | cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_TSINT); | ||
177 | cx_set(MO_TS_INTMSK, 0x1f0011); | ||
178 | |||
179 | /* start dma */ | ||
180 | cx_set(MO_DEV_CNTRL2, (1<<5)); | ||
181 | cx_set(MO_TS_DMACNTRL, 0x11); | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static int cx8802_stop_dma(struct cx8802_dev *dev) | ||
186 | { | ||
187 | struct cx88_core *core = dev->core; | ||
188 | dprintk( 1, "cx8802_stop_dma\n" ); | ||
189 | |||
190 | /* stop dma */ | ||
191 | cx_clear(MO_TS_DMACNTRL, 0x11); | ||
192 | |||
193 | /* disable irqs */ | ||
194 | cx_clear(MO_PCI_INTMSK, PCI_INT_TSINT); | ||
195 | cx_clear(MO_TS_INTMSK, 0x1f0011); | ||
196 | |||
197 | /* Reset the controller */ | ||
198 | cx_write(TS_GEN_CNTRL, 0xcd); | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int cx8802_restart_queue(struct cx8802_dev *dev, | ||
203 | struct cx88_dmaqueue *q) | ||
204 | { | ||
205 | struct cx88_buffer *buf; | ||
206 | |||
207 | dprintk( 1, "cx8802_restart_queue\n" ); | ||
208 | if (list_empty(&q->active)) | ||
209 | { | ||
210 | struct cx88_buffer *prev; | ||
211 | prev = NULL; | ||
212 | |||
213 | dprintk(1, "cx8802_restart_queue: queue is empty\n" ); | ||
214 | |||
215 | for (;;) { | ||
216 | if (list_empty(&q->queued)) | ||
217 | return 0; | ||
218 | buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue); | ||
219 | if (NULL == prev) { | ||
220 | list_del(&buf->vb.queue); | ||
221 | list_add_tail(&buf->vb.queue,&q->active); | ||
222 | cx8802_start_dma(dev, q, buf); | ||
223 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
224 | buf->count = q->count++; | ||
225 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
226 | dprintk(1,"[%p/%d] restart_queue - first active\n", | ||
227 | buf,buf->vb.i); | ||
228 | |||
229 | } else if (prev->vb.width == buf->vb.width && | ||
230 | prev->vb.height == buf->vb.height && | ||
231 | prev->fmt == buf->fmt) { | ||
232 | list_del(&buf->vb.queue); | ||
233 | list_add_tail(&buf->vb.queue,&q->active); | ||
234 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
235 | buf->count = q->count++; | ||
236 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
237 | dprintk(1,"[%p/%d] restart_queue - move to active\n", | ||
238 | buf,buf->vb.i); | ||
239 | } else { | ||
240 | return 0; | ||
241 | } | ||
242 | prev = buf; | ||
243 | } | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
248 | dprintk(2,"restart_queue [%p/%d]: restart dma\n", | ||
249 | buf, buf->vb.i); | ||
250 | cx8802_start_dma(dev, q, buf); | ||
251 | list_for_each_entry(buf, &q->active, vb.queue) | ||
252 | buf->count = q->count++; | ||
253 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | /* ------------------------------------------------------------------ */ | ||
258 | |||
259 | int cx8802_buf_prepare(struct videobuf_queue *q, struct cx8802_dev *dev, | ||
260 | struct cx88_buffer *buf, enum v4l2_field field) | ||
261 | { | ||
262 | int size = dev->ts_packet_size * dev->ts_packet_count; | ||
263 | struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); | ||
264 | int rc; | ||
265 | |||
266 | dprintk(1, "%s: %p\n", __func__, buf); | ||
267 | if (0 != buf->vb.baddr && buf->vb.bsize < size) | ||
268 | return -EINVAL; | ||
269 | |||
270 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { | ||
271 | buf->vb.width = dev->ts_packet_size; | ||
272 | buf->vb.height = dev->ts_packet_count; | ||
273 | buf->vb.size = size; | ||
274 | buf->vb.field = field /*V4L2_FIELD_TOP*/; | ||
275 | |||
276 | if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) | ||
277 | goto fail; | ||
278 | cx88_risc_databuffer(dev->pci, &buf->risc, | ||
279 | dma->sglist, | ||
280 | buf->vb.width, buf->vb.height, 0); | ||
281 | } | ||
282 | buf->vb.state = VIDEOBUF_PREPARED; | ||
283 | return 0; | ||
284 | |||
285 | fail: | ||
286 | cx88_free_buffer(q,buf); | ||
287 | return rc; | ||
288 | } | ||
289 | |||
290 | void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf) | ||
291 | { | ||
292 | struct cx88_buffer *prev; | ||
293 | struct cx88_dmaqueue *cx88q = &dev->mpegq; | ||
294 | |||
295 | dprintk( 1, "cx8802_buf_queue\n" ); | ||
296 | /* add jump to stopper */ | ||
297 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); | ||
298 | buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma); | ||
299 | |||
300 | if (list_empty(&cx88q->active)) { | ||
301 | dprintk( 1, "queue is empty - first active\n" ); | ||
302 | list_add_tail(&buf->vb.queue,&cx88q->active); | ||
303 | cx8802_start_dma(dev, cx88q, buf); | ||
304 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
305 | buf->count = cx88q->count++; | ||
306 | mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT); | ||
307 | dprintk(1,"[%p/%d] %s - first active\n", | ||
308 | buf, buf->vb.i, __func__); | ||
309 | |||
310 | } else { | ||
311 | dprintk( 1, "queue is not empty - append to active\n" ); | ||
312 | prev = list_entry(cx88q->active.prev, struct cx88_buffer, vb.queue); | ||
313 | list_add_tail(&buf->vb.queue,&cx88q->active); | ||
314 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
315 | buf->count = cx88q->count++; | ||
316 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
317 | dprintk( 1, "[%p/%d] %s - append to active\n", | ||
318 | buf, buf->vb.i, __func__); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | /* ----------------------------------------------------------- */ | ||
323 | |||
324 | static void do_cancel_buffers(struct cx8802_dev *dev, const char *reason, int restart) | ||
325 | { | ||
326 | struct cx88_dmaqueue *q = &dev->mpegq; | ||
327 | struct cx88_buffer *buf; | ||
328 | unsigned long flags; | ||
329 | |||
330 | spin_lock_irqsave(&dev->slock,flags); | ||
331 | while (!list_empty(&q->active)) { | ||
332 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
333 | list_del(&buf->vb.queue); | ||
334 | buf->vb.state = VIDEOBUF_ERROR; | ||
335 | wake_up(&buf->vb.done); | ||
336 | dprintk(1,"[%p/%d] %s - dma=0x%08lx\n", | ||
337 | buf, buf->vb.i, reason, (unsigned long)buf->risc.dma); | ||
338 | } | ||
339 | if (restart) | ||
340 | { | ||
341 | dprintk(1, "restarting queue\n" ); | ||
342 | cx8802_restart_queue(dev,q); | ||
343 | } | ||
344 | spin_unlock_irqrestore(&dev->slock,flags); | ||
345 | } | ||
346 | |||
347 | void cx8802_cancel_buffers(struct cx8802_dev *dev) | ||
348 | { | ||
349 | struct cx88_dmaqueue *q = &dev->mpegq; | ||
350 | |||
351 | dprintk( 1, "cx8802_cancel_buffers" ); | ||
352 | del_timer_sync(&q->timeout); | ||
353 | cx8802_stop_dma(dev); | ||
354 | do_cancel_buffers(dev,"cancel",0); | ||
355 | } | ||
356 | |||
357 | static void cx8802_timeout(unsigned long data) | ||
358 | { | ||
359 | struct cx8802_dev *dev = (struct cx8802_dev*)data; | ||
360 | |||
361 | dprintk(1, "%s\n",__func__); | ||
362 | |||
363 | if (debug) | ||
364 | cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); | ||
365 | cx8802_stop_dma(dev); | ||
366 | do_cancel_buffers(dev,"timeout",1); | ||
367 | } | ||
368 | |||
369 | static const char * cx88_mpeg_irqs[32] = { | ||
370 | "ts_risci1", NULL, NULL, NULL, | ||
371 | "ts_risci2", NULL, NULL, NULL, | ||
372 | "ts_oflow", NULL, NULL, NULL, | ||
373 | "ts_sync", NULL, NULL, NULL, | ||
374 | "opc_err", "par_err", "rip_err", "pci_abort", | ||
375 | "ts_err?", | ||
376 | }; | ||
377 | |||
378 | static void cx8802_mpeg_irq(struct cx8802_dev *dev) | ||
379 | { | ||
380 | struct cx88_core *core = dev->core; | ||
381 | u32 status, mask, count; | ||
382 | |||
383 | dprintk( 1, "cx8802_mpeg_irq\n" ); | ||
384 | status = cx_read(MO_TS_INTSTAT); | ||
385 | mask = cx_read(MO_TS_INTMSK); | ||
386 | if (0 == (status & mask)) | ||
387 | return; | ||
388 | |||
389 | cx_write(MO_TS_INTSTAT, status); | ||
390 | |||
391 | if (debug || (status & mask & ~0xff)) | ||
392 | cx88_print_irqbits(core->name, "irq mpeg ", | ||
393 | cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs), | ||
394 | status, mask); | ||
395 | |||
396 | /* risc op code error */ | ||
397 | if (status & (1 << 16)) { | ||
398 | printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name); | ||
399 | cx_clear(MO_TS_DMACNTRL, 0x11); | ||
400 | cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); | ||
401 | } | ||
402 | |||
403 | /* risc1 y */ | ||
404 | if (status & 0x01) { | ||
405 | dprintk( 1, "wake up\n" ); | ||
406 | spin_lock(&dev->slock); | ||
407 | count = cx_read(MO_TS_GPCNT); | ||
408 | cx88_wakeup(dev->core, &dev->mpegq, count); | ||
409 | spin_unlock(&dev->slock); | ||
410 | } | ||
411 | |||
412 | /* risc2 y */ | ||
413 | if (status & 0x10) { | ||
414 | spin_lock(&dev->slock); | ||
415 | cx8802_restart_queue(dev,&dev->mpegq); | ||
416 | spin_unlock(&dev->slock); | ||
417 | } | ||
418 | |||
419 | /* other general errors */ | ||
420 | if (status & 0x1f0100) { | ||
421 | dprintk( 0, "general errors: 0x%08x\n", status & 0x1f0100 ); | ||
422 | spin_lock(&dev->slock); | ||
423 | cx8802_stop_dma(dev); | ||
424 | cx8802_restart_queue(dev,&dev->mpegq); | ||
425 | spin_unlock(&dev->slock); | ||
426 | } | ||
427 | } | ||
428 | |||
429 | #define MAX_IRQ_LOOP 10 | ||
430 | |||
431 | static irqreturn_t cx8802_irq(int irq, void *dev_id) | ||
432 | { | ||
433 | struct cx8802_dev *dev = dev_id; | ||
434 | struct cx88_core *core = dev->core; | ||
435 | u32 status; | ||
436 | int loop, handled = 0; | ||
437 | |||
438 | for (loop = 0; loop < MAX_IRQ_LOOP; loop++) { | ||
439 | status = cx_read(MO_PCI_INTSTAT) & | ||
440 | (core->pci_irqmask | PCI_INT_TSINT); | ||
441 | if (0 == status) | ||
442 | goto out; | ||
443 | dprintk( 1, "cx8802_irq\n" ); | ||
444 | dprintk( 1, " loop: %d/%d\n", loop, MAX_IRQ_LOOP ); | ||
445 | dprintk( 1, " status: %d\n", status ); | ||
446 | handled = 1; | ||
447 | cx_write(MO_PCI_INTSTAT, status); | ||
448 | |||
449 | if (status & core->pci_irqmask) | ||
450 | cx88_core_irq(core,status); | ||
451 | if (status & PCI_INT_TSINT) | ||
452 | cx8802_mpeg_irq(dev); | ||
453 | }; | ||
454 | if (MAX_IRQ_LOOP == loop) { | ||
455 | dprintk( 0, "clearing mask\n" ); | ||
456 | printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", | ||
457 | core->name); | ||
458 | cx_write(MO_PCI_INTMSK,0); | ||
459 | } | ||
460 | |||
461 | out: | ||
462 | return IRQ_RETVAL(handled); | ||
463 | } | ||
464 | |||
465 | static int cx8802_init_common(struct cx8802_dev *dev) | ||
466 | { | ||
467 | struct cx88_core *core = dev->core; | ||
468 | int err; | ||
469 | |||
470 | /* pci init */ | ||
471 | if (pci_enable_device(dev->pci)) | ||
472 | return -EIO; | ||
473 | pci_set_master(dev->pci); | ||
474 | if (!pci_dma_supported(dev->pci,DMA_BIT_MASK(32))) { | ||
475 | printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name); | ||
476 | return -EIO; | ||
477 | } | ||
478 | |||
479 | dev->pci_rev = dev->pci->revision; | ||
480 | pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat); | ||
481 | printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, " | ||
482 | "latency: %d, mmio: 0x%llx\n", dev->core->name, | ||
483 | pci_name(dev->pci), dev->pci_rev, dev->pci->irq, | ||
484 | dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0)); | ||
485 | |||
486 | /* initialize driver struct */ | ||
487 | spin_lock_init(&dev->slock); | ||
488 | |||
489 | /* init dma queue */ | ||
490 | INIT_LIST_HEAD(&dev->mpegq.active); | ||
491 | INIT_LIST_HEAD(&dev->mpegq.queued); | ||
492 | dev->mpegq.timeout.function = cx8802_timeout; | ||
493 | dev->mpegq.timeout.data = (unsigned long)dev; | ||
494 | init_timer(&dev->mpegq.timeout); | ||
495 | cx88_risc_stopper(dev->pci,&dev->mpegq.stopper, | ||
496 | MO_TS_DMACNTRL,0x11,0x00); | ||
497 | |||
498 | /* get irq */ | ||
499 | err = request_irq(dev->pci->irq, cx8802_irq, | ||
500 | IRQF_SHARED | IRQF_DISABLED, dev->core->name, dev); | ||
501 | if (err < 0) { | ||
502 | printk(KERN_ERR "%s: can't get IRQ %d\n", | ||
503 | dev->core->name, dev->pci->irq); | ||
504 | return err; | ||
505 | } | ||
506 | cx_set(MO_PCI_INTMSK, core->pci_irqmask); | ||
507 | |||
508 | /* everything worked */ | ||
509 | pci_set_drvdata(dev->pci,dev); | ||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | static void cx8802_fini_common(struct cx8802_dev *dev) | ||
514 | { | ||
515 | dprintk( 2, "cx8802_fini_common\n" ); | ||
516 | cx8802_stop_dma(dev); | ||
517 | pci_disable_device(dev->pci); | ||
518 | |||
519 | /* unregister stuff */ | ||
520 | free_irq(dev->pci->irq, dev); | ||
521 | pci_set_drvdata(dev->pci, NULL); | ||
522 | |||
523 | /* free memory */ | ||
524 | btcx_riscmem_free(dev->pci,&dev->mpegq.stopper); | ||
525 | } | ||
526 | |||
527 | /* ----------------------------------------------------------- */ | ||
528 | |||
529 | static int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state) | ||
530 | { | ||
531 | struct cx8802_dev *dev = pci_get_drvdata(pci_dev); | ||
532 | struct cx88_core *core = dev->core; | ||
533 | |||
534 | /* stop mpeg dma */ | ||
535 | spin_lock(&dev->slock); | ||
536 | if (!list_empty(&dev->mpegq.active)) { | ||
537 | dprintk( 2, "suspend\n" ); | ||
538 | printk("%s: suspend mpeg\n", core->name); | ||
539 | cx8802_stop_dma(dev); | ||
540 | del_timer(&dev->mpegq.timeout); | ||
541 | } | ||
542 | spin_unlock(&dev->slock); | ||
543 | |||
544 | /* FIXME -- shutdown device */ | ||
545 | cx88_shutdown(dev->core); | ||
546 | |||
547 | pci_save_state(pci_dev); | ||
548 | if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) { | ||
549 | pci_disable_device(pci_dev); | ||
550 | dev->state.disabled = 1; | ||
551 | } | ||
552 | return 0; | ||
553 | } | ||
554 | |||
555 | static int cx8802_resume_common(struct pci_dev *pci_dev) | ||
556 | { | ||
557 | struct cx8802_dev *dev = pci_get_drvdata(pci_dev); | ||
558 | struct cx88_core *core = dev->core; | ||
559 | int err; | ||
560 | |||
561 | if (dev->state.disabled) { | ||
562 | err=pci_enable_device(pci_dev); | ||
563 | if (err) { | ||
564 | printk(KERN_ERR "%s: can't enable device\n", | ||
565 | dev->core->name); | ||
566 | return err; | ||
567 | } | ||
568 | dev->state.disabled = 0; | ||
569 | } | ||
570 | err=pci_set_power_state(pci_dev, PCI_D0); | ||
571 | if (err) { | ||
572 | printk(KERN_ERR "%s: can't enable device\n", | ||
573 | dev->core->name); | ||
574 | pci_disable_device(pci_dev); | ||
575 | dev->state.disabled = 1; | ||
576 | |||
577 | return err; | ||
578 | } | ||
579 | pci_restore_state(pci_dev); | ||
580 | |||
581 | /* FIXME: re-initialize hardware */ | ||
582 | cx88_reset(dev->core); | ||
583 | |||
584 | /* restart video+vbi capture */ | ||
585 | spin_lock(&dev->slock); | ||
586 | if (!list_empty(&dev->mpegq.active)) { | ||
587 | printk("%s: resume mpeg\n", core->name); | ||
588 | cx8802_restart_queue(dev,&dev->mpegq); | ||
589 | } | ||
590 | spin_unlock(&dev->slock); | ||
591 | |||
592 | return 0; | ||
593 | } | ||
594 | |||
595 | struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype) | ||
596 | { | ||
597 | struct cx8802_driver *d; | ||
598 | |||
599 | list_for_each_entry(d, &dev->drvlist, drvlist) | ||
600 | if (d->type_id == btype) | ||
601 | return d; | ||
602 | |||
603 | return NULL; | ||
604 | } | ||
605 | |||
606 | /* Driver asked for hardware access. */ | ||
607 | static int cx8802_request_acquire(struct cx8802_driver *drv) | ||
608 | { | ||
609 | struct cx88_core *core = drv->core; | ||
610 | unsigned int i; | ||
611 | |||
612 | /* Fail a request for hardware if the device is busy. */ | ||
613 | if (core->active_type_id != CX88_BOARD_NONE && | ||
614 | core->active_type_id != drv->type_id) | ||
615 | return -EBUSY; | ||
616 | |||
617 | if (drv->type_id == CX88_MPEG_DVB) { | ||
618 | /* When switching to DVB, always set the input to the tuner */ | ||
619 | core->last_analog_input = core->input; | ||
620 | core->input = 0; | ||
621 | for (i = 0; | ||
622 | i < (sizeof(core->board.input) / sizeof(struct cx88_input)); | ||
623 | i++) { | ||
624 | if (core->board.input[i].type == CX88_VMUX_DVB) { | ||
625 | core->input = i; | ||
626 | break; | ||
627 | } | ||
628 | } | ||
629 | } | ||
630 | |||
631 | if (drv->advise_acquire) | ||
632 | { | ||
633 | core->active_ref++; | ||
634 | if (core->active_type_id == CX88_BOARD_NONE) { | ||
635 | core->active_type_id = drv->type_id; | ||
636 | drv->advise_acquire(drv); | ||
637 | } | ||
638 | |||
639 | mpeg_dbg(1,"%s() Post acquire GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); | ||
640 | } | ||
641 | |||
642 | return 0; | ||
643 | } | ||
644 | |||
645 | /* Driver asked to release hardware. */ | ||
646 | static int cx8802_request_release(struct cx8802_driver *drv) | ||
647 | { | ||
648 | struct cx88_core *core = drv->core; | ||
649 | |||
650 | if (drv->advise_release && --core->active_ref == 0) | ||
651 | { | ||
652 | if (drv->type_id == CX88_MPEG_DVB) { | ||
653 | /* If the DVB driver is releasing, reset the input | ||
654 | state to the last configured analog input */ | ||
655 | core->input = core->last_analog_input; | ||
656 | } | ||
657 | |||
658 | drv->advise_release(drv); | ||
659 | core->active_type_id = CX88_BOARD_NONE; | ||
660 | mpeg_dbg(1,"%s() Post release GPIO=%x\n", __func__, cx_read(MO_GP0_IO)); | ||
661 | } | ||
662 | |||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static int cx8802_check_driver(struct cx8802_driver *drv) | ||
667 | { | ||
668 | if (drv == NULL) | ||
669 | return -ENODEV; | ||
670 | |||
671 | if ((drv->type_id != CX88_MPEG_DVB) && | ||
672 | (drv->type_id != CX88_MPEG_BLACKBIRD)) | ||
673 | return -EINVAL; | ||
674 | |||
675 | if ((drv->hw_access != CX8802_DRVCTL_SHARED) && | ||
676 | (drv->hw_access != CX8802_DRVCTL_EXCLUSIVE)) | ||
677 | return -EINVAL; | ||
678 | |||
679 | if ((drv->probe == NULL) || | ||
680 | (drv->remove == NULL) || | ||
681 | (drv->advise_acquire == NULL) || | ||
682 | (drv->advise_release == NULL)) | ||
683 | return -EINVAL; | ||
684 | |||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | int cx8802_register_driver(struct cx8802_driver *drv) | ||
689 | { | ||
690 | struct cx8802_dev *dev; | ||
691 | struct cx8802_driver *driver; | ||
692 | int err, i = 0; | ||
693 | |||
694 | printk(KERN_INFO | ||
695 | "cx88/2: registering cx8802 driver, type: %s access: %s\n", | ||
696 | drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", | ||
697 | drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive"); | ||
698 | |||
699 | if ((err = cx8802_check_driver(drv)) != 0) { | ||
700 | printk(KERN_ERR "cx88/2: cx8802_driver is invalid\n"); | ||
701 | return err; | ||
702 | } | ||
703 | |||
704 | mutex_lock(&cx8802_mutex); | ||
705 | |||
706 | list_for_each_entry(dev, &cx8802_devlist, devlist) { | ||
707 | printk(KERN_INFO | ||
708 | "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n", | ||
709 | dev->core->name, dev->pci->subsystem_vendor, | ||
710 | dev->pci->subsystem_device, dev->core->board.name, | ||
711 | dev->core->boardnr); | ||
712 | |||
713 | /* Bring up a new struct for each driver instance */ | ||
714 | driver = kzalloc(sizeof(*drv),GFP_KERNEL); | ||
715 | if (driver == NULL) { | ||
716 | err = -ENOMEM; | ||
717 | goto out; | ||
718 | } | ||
719 | |||
720 | /* Snapshot of the driver registration data */ | ||
721 | drv->core = dev->core; | ||
722 | drv->suspend = cx8802_suspend_common; | ||
723 | drv->resume = cx8802_resume_common; | ||
724 | drv->request_acquire = cx8802_request_acquire; | ||
725 | drv->request_release = cx8802_request_release; | ||
726 | memcpy(driver, drv, sizeof(*driver)); | ||
727 | |||
728 | mutex_lock(&drv->core->lock); | ||
729 | err = drv->probe(driver); | ||
730 | if (err == 0) { | ||
731 | i++; | ||
732 | list_add_tail(&driver->drvlist, &dev->drvlist); | ||
733 | } else { | ||
734 | printk(KERN_ERR | ||
735 | "%s/2: cx8802 probe failed, err = %d\n", | ||
736 | dev->core->name, err); | ||
737 | } | ||
738 | mutex_unlock(&drv->core->lock); | ||
739 | } | ||
740 | |||
741 | err = i ? 0 : -ENODEV; | ||
742 | out: | ||
743 | mutex_unlock(&cx8802_mutex); | ||
744 | return err; | ||
745 | } | ||
746 | |||
747 | int cx8802_unregister_driver(struct cx8802_driver *drv) | ||
748 | { | ||
749 | struct cx8802_dev *dev; | ||
750 | struct cx8802_driver *d, *dtmp; | ||
751 | int err = 0; | ||
752 | |||
753 | printk(KERN_INFO | ||
754 | "cx88/2: unregistering cx8802 driver, type: %s access: %s\n", | ||
755 | drv->type_id == CX88_MPEG_DVB ? "dvb" : "blackbird", | ||
756 | drv->hw_access == CX8802_DRVCTL_SHARED ? "shared" : "exclusive"); | ||
757 | |||
758 | mutex_lock(&cx8802_mutex); | ||
759 | |||
760 | list_for_each_entry(dev, &cx8802_devlist, devlist) { | ||
761 | printk(KERN_INFO | ||
762 | "%s/2: subsystem: %04x:%04x, board: %s [card=%d]\n", | ||
763 | dev->core->name, dev->pci->subsystem_vendor, | ||
764 | dev->pci->subsystem_device, dev->core->board.name, | ||
765 | dev->core->boardnr); | ||
766 | |||
767 | mutex_lock(&dev->core->lock); | ||
768 | |||
769 | list_for_each_entry_safe(d, dtmp, &dev->drvlist, drvlist) { | ||
770 | /* only unregister the correct driver type */ | ||
771 | if (d->type_id != drv->type_id) | ||
772 | continue; | ||
773 | |||
774 | err = d->remove(d); | ||
775 | if (err == 0) { | ||
776 | list_del(&d->drvlist); | ||
777 | kfree(d); | ||
778 | } else | ||
779 | printk(KERN_ERR "%s/2: cx8802 driver remove " | ||
780 | "failed (%d)\n", dev->core->name, err); | ||
781 | } | ||
782 | |||
783 | mutex_unlock(&dev->core->lock); | ||
784 | } | ||
785 | |||
786 | mutex_unlock(&cx8802_mutex); | ||
787 | |||
788 | return err; | ||
789 | } | ||
790 | |||
791 | /* ----------------------------------------------------------- */ | ||
792 | static int __devinit cx8802_probe(struct pci_dev *pci_dev, | ||
793 | const struct pci_device_id *pci_id) | ||
794 | { | ||
795 | struct cx8802_dev *dev; | ||
796 | struct cx88_core *core; | ||
797 | int err; | ||
798 | |||
799 | /* general setup */ | ||
800 | core = cx88_core_get(pci_dev); | ||
801 | if (NULL == core) | ||
802 | return -EINVAL; | ||
803 | |||
804 | printk("%s/2: cx2388x 8802 Driver Manager\n", core->name); | ||
805 | |||
806 | err = -ENODEV; | ||
807 | if (!core->board.mpeg) | ||
808 | goto fail_core; | ||
809 | |||
810 | err = -ENOMEM; | ||
811 | dev = kzalloc(sizeof(*dev),GFP_KERNEL); | ||
812 | if (NULL == dev) | ||
813 | goto fail_core; | ||
814 | dev->pci = pci_dev; | ||
815 | dev->core = core; | ||
816 | |||
817 | /* Maintain a reference so cx88-video can query the 8802 device. */ | ||
818 | core->dvbdev = dev; | ||
819 | |||
820 | err = cx8802_init_common(dev); | ||
821 | if (err != 0) | ||
822 | goto fail_free; | ||
823 | |||
824 | INIT_LIST_HEAD(&dev->drvlist); | ||
825 | mutex_lock(&cx8802_mutex); | ||
826 | list_add_tail(&dev->devlist,&cx8802_devlist); | ||
827 | mutex_unlock(&cx8802_mutex); | ||
828 | |||
829 | /* now autoload cx88-dvb or cx88-blackbird */ | ||
830 | request_modules(dev); | ||
831 | return 0; | ||
832 | |||
833 | fail_free: | ||
834 | kfree(dev); | ||
835 | fail_core: | ||
836 | core->dvbdev = NULL; | ||
837 | cx88_core_put(core,pci_dev); | ||
838 | return err; | ||
839 | } | ||
840 | |||
841 | static void __devexit cx8802_remove(struct pci_dev *pci_dev) | ||
842 | { | ||
843 | struct cx8802_dev *dev; | ||
844 | |||
845 | dev = pci_get_drvdata(pci_dev); | ||
846 | |||
847 | dprintk( 1, "%s\n", __func__); | ||
848 | |||
849 | flush_request_modules(dev); | ||
850 | |||
851 | mutex_lock(&dev->core->lock); | ||
852 | |||
853 | if (!list_empty(&dev->drvlist)) { | ||
854 | struct cx8802_driver *drv, *tmp; | ||
855 | int err; | ||
856 | |||
857 | printk(KERN_WARNING "%s/2: Trying to remove cx8802 driver " | ||
858 | "while cx8802 sub-drivers still loaded?!\n", | ||
859 | dev->core->name); | ||
860 | |||
861 | list_for_each_entry_safe(drv, tmp, &dev->drvlist, drvlist) { | ||
862 | err = drv->remove(drv); | ||
863 | if (err == 0) { | ||
864 | list_del(&drv->drvlist); | ||
865 | } else | ||
866 | printk(KERN_ERR "%s/2: cx8802 driver remove " | ||
867 | "failed (%d)\n", dev->core->name, err); | ||
868 | kfree(drv); | ||
869 | } | ||
870 | } | ||
871 | |||
872 | mutex_unlock(&dev->core->lock); | ||
873 | |||
874 | /* Destroy any 8802 reference. */ | ||
875 | dev->core->dvbdev = NULL; | ||
876 | |||
877 | /* common */ | ||
878 | cx8802_fini_common(dev); | ||
879 | cx88_core_put(dev->core,dev->pci); | ||
880 | kfree(dev); | ||
881 | } | ||
882 | |||
883 | static const struct pci_device_id cx8802_pci_tbl[] = { | ||
884 | { | ||
885 | .vendor = 0x14f1, | ||
886 | .device = 0x8802, | ||
887 | .subvendor = PCI_ANY_ID, | ||
888 | .subdevice = PCI_ANY_ID, | ||
889 | },{ | ||
890 | /* --- end of list --- */ | ||
891 | } | ||
892 | }; | ||
893 | MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); | ||
894 | |||
895 | static struct pci_driver cx8802_pci_driver = { | ||
896 | .name = "cx88-mpeg driver manager", | ||
897 | .id_table = cx8802_pci_tbl, | ||
898 | .probe = cx8802_probe, | ||
899 | .remove = __devexit_p(cx8802_remove), | ||
900 | }; | ||
901 | |||
902 | static int __init cx8802_init(void) | ||
903 | { | ||
904 | printk(KERN_INFO "cx88/2: cx2388x MPEG-TS Driver Manager version %s loaded\n", | ||
905 | CX88_VERSION); | ||
906 | return pci_register_driver(&cx8802_pci_driver); | ||
907 | } | ||
908 | |||
909 | static void __exit cx8802_fini(void) | ||
910 | { | ||
911 | pci_unregister_driver(&cx8802_pci_driver); | ||
912 | } | ||
913 | |||
914 | module_init(cx8802_init); | ||
915 | module_exit(cx8802_fini); | ||
916 | EXPORT_SYMBOL(cx8802_buf_prepare); | ||
917 | EXPORT_SYMBOL(cx8802_buf_queue); | ||
918 | EXPORT_SYMBOL(cx8802_cancel_buffers); | ||
919 | |||
920 | EXPORT_SYMBOL(cx8802_register_driver); | ||
921 | EXPORT_SYMBOL(cx8802_unregister_driver); | ||
922 | EXPORT_SYMBOL(cx8802_get_driver); | ||
923 | /* ----------------------------------------------------------- */ | ||
924 | /* | ||
925 | * Local variables: | ||
926 | * c-basic-offset: 8 | ||
927 | * End: | ||
928 | * 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 | ||
929 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-reg.h b/drivers/media/video/cx88/cx88-reg.h new file mode 100644 index 00000000000..2ec52d1cdea --- /dev/null +++ b/drivers/media/video/cx88/cx88-reg.h | |||
@@ -0,0 +1,836 @@ | |||
1 | /* | ||
2 | |||
3 | cx88x-hw.h - CX2388x register offsets | ||
4 | |||
5 | Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) | ||
6 | 2001 Michael Eskin | ||
7 | 2002 Yurij Sysoev <yurij@naturesoft.net> | ||
8 | 2003 Gerd Knorr <kraxel@bytesex.org> | ||
9 | |||
10 | This program is free software; you can redistribute it and/or modify | ||
11 | it under the terms of the GNU General Public License as published by | ||
12 | the Free Software Foundation; either version 2 of the License, or | ||
13 | (at your option) any later version. | ||
14 | |||
15 | This program is distributed in the hope that it will be useful, | ||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | GNU General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU General Public License | ||
21 | along with this program; if not, write to the Free Software | ||
22 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | */ | ||
24 | |||
25 | #ifndef _CX88_REG_H_ | ||
26 | #define _CX88_REG_H_ | ||
27 | |||
28 | /* ---------------------------------------------------------------------- */ | ||
29 | /* PCI IDs and config space */ | ||
30 | |||
31 | #ifndef PCI_VENDOR_ID_CONEXANT | ||
32 | # define PCI_VENDOR_ID_CONEXANT 0x14F1 | ||
33 | #endif | ||
34 | #ifndef PCI_DEVICE_ID_CX2300_VID | ||
35 | # define PCI_DEVICE_ID_CX2300_VID 0x8800 | ||
36 | #endif | ||
37 | |||
38 | #define CX88X_DEVCTRL 0x40 | ||
39 | #define CX88X_EN_TBFX 0x02 | ||
40 | #define CX88X_EN_VSFX 0x04 | ||
41 | |||
42 | /* ---------------------------------------------------------------------- */ | ||
43 | /* PCI controller registers */ | ||
44 | |||
45 | /* Command and Status Register */ | ||
46 | #define F0_CMD_STAT_MM 0x2f0004 | ||
47 | #define F1_CMD_STAT_MM 0x2f0104 | ||
48 | #define F2_CMD_STAT_MM 0x2f0204 | ||
49 | #define F3_CMD_STAT_MM 0x2f0304 | ||
50 | #define F4_CMD_STAT_MM 0x2f0404 | ||
51 | |||
52 | /* Device Control #1 */ | ||
53 | #define F0_DEV_CNTRL1_MM 0x2f0040 | ||
54 | #define F1_DEV_CNTRL1_MM 0x2f0140 | ||
55 | #define F2_DEV_CNTRL1_MM 0x2f0240 | ||
56 | #define F3_DEV_CNTRL1_MM 0x2f0340 | ||
57 | #define F4_DEV_CNTRL1_MM 0x2f0440 | ||
58 | |||
59 | /* Device Control #1 */ | ||
60 | #define F0_BAR0_MM 0x2f0010 | ||
61 | #define F1_BAR0_MM 0x2f0110 | ||
62 | #define F2_BAR0_MM 0x2f0210 | ||
63 | #define F3_BAR0_MM 0x2f0310 | ||
64 | #define F4_BAR0_MM 0x2f0410 | ||
65 | |||
66 | /* ---------------------------------------------------------------------- */ | ||
67 | /* DMA Controller registers */ | ||
68 | |||
69 | #define MO_PDMA_STHRSH 0x200000 // Source threshold | ||
70 | #define MO_PDMA_STADRS 0x200004 // Source target address | ||
71 | #define MO_PDMA_SIADRS 0x200008 // Source internal address | ||
72 | #define MO_PDMA_SCNTRL 0x20000C // Source control | ||
73 | #define MO_PDMA_DTHRSH 0x200010 // Destination threshold | ||
74 | #define MO_PDMA_DTADRS 0x200014 // Destination target address | ||
75 | #define MO_PDMA_DIADRS 0x200018 // Destination internal address | ||
76 | #define MO_PDMA_DCNTRL 0x20001C // Destination control | ||
77 | #define MO_LD_SSID 0x200030 // Load subsystem ID | ||
78 | #define MO_DEV_CNTRL2 0x200034 // Device control | ||
79 | #define MO_PCI_INTMSK 0x200040 // PCI interrupt mask | ||
80 | #define MO_PCI_INTSTAT 0x200044 // PCI interrupt status | ||
81 | #define MO_PCI_INTMSTAT 0x200048 // PCI interrupt masked status | ||
82 | #define MO_VID_INTMSK 0x200050 // Video interrupt mask | ||
83 | #define MO_VID_INTSTAT 0x200054 // Video interrupt status | ||
84 | #define MO_VID_INTMSTAT 0x200058 // Video interrupt masked status | ||
85 | #define MO_VID_INTSSTAT 0x20005C // Video interrupt set status | ||
86 | #define MO_AUD_INTMSK 0x200060 // Audio interrupt mask | ||
87 | #define MO_AUD_INTSTAT 0x200064 // Audio interrupt status | ||
88 | #define MO_AUD_INTMSTAT 0x200068 // Audio interrupt masked status | ||
89 | #define MO_AUD_INTSSTAT 0x20006C // Audio interrupt set status | ||
90 | #define MO_TS_INTMSK 0x200070 // Transport stream interrupt mask | ||
91 | #define MO_TS_INTSTAT 0x200074 // Transport stream interrupt status | ||
92 | #define MO_TS_INTMSTAT 0x200078 // Transport stream interrupt mask status | ||
93 | #define MO_TS_INTSSTAT 0x20007C // Transport stream interrupt set status | ||
94 | #define MO_VIP_INTMSK 0x200080 // VIP interrupt mask | ||
95 | #define MO_VIP_INTSTAT 0x200084 // VIP interrupt status | ||
96 | #define MO_VIP_INTMSTAT 0x200088 // VIP interrupt masked status | ||
97 | #define MO_VIP_INTSSTAT 0x20008C // VIP interrupt set status | ||
98 | #define MO_GPHST_INTMSK 0x200090 // Host interrupt mask | ||
99 | #define MO_GPHST_INTSTAT 0x200094 // Host interrupt status | ||
100 | #define MO_GPHST_INTMSTAT 0x200098 // Host interrupt masked status | ||
101 | #define MO_GPHST_INTSSTAT 0x20009C // Host interrupt set status | ||
102 | |||
103 | // DMA Channels 1-6 belong to SPIPE | ||
104 | #define MO_DMA7_PTR1 0x300018 // {24}RW* DMA Current Ptr : Ch#7 | ||
105 | #define MO_DMA8_PTR1 0x30001C // {24}RW* DMA Current Ptr : Ch#8 | ||
106 | |||
107 | // DMA Channels 9-20 belong to SPIPE | ||
108 | #define MO_DMA21_PTR1 0x300080 // {24}R0* DMA Current Ptr : Ch#21 | ||
109 | #define MO_DMA22_PTR1 0x300084 // {24}R0* DMA Current Ptr : Ch#22 | ||
110 | #define MO_DMA23_PTR1 0x300088 // {24}R0* DMA Current Ptr : Ch#23 | ||
111 | #define MO_DMA24_PTR1 0x30008C // {24}R0* DMA Current Ptr : Ch#24 | ||
112 | #define MO_DMA25_PTR1 0x300090 // {24}R0* DMA Current Ptr : Ch#25 | ||
113 | #define MO_DMA26_PTR1 0x300094 // {24}R0* DMA Current Ptr : Ch#26 | ||
114 | #define MO_DMA27_PTR1 0x300098 // {24}R0* DMA Current Ptr : Ch#27 | ||
115 | #define MO_DMA28_PTR1 0x30009C // {24}R0* DMA Current Ptr : Ch#28 | ||
116 | #define MO_DMA29_PTR1 0x3000A0 // {24}R0* DMA Current Ptr : Ch#29 | ||
117 | #define MO_DMA30_PTR1 0x3000A4 // {24}R0* DMA Current Ptr : Ch#30 | ||
118 | #define MO_DMA31_PTR1 0x3000A8 // {24}R0* DMA Current Ptr : Ch#31 | ||
119 | #define MO_DMA32_PTR1 0x3000AC // {24}R0* DMA Current Ptr : Ch#32 | ||
120 | |||
121 | #define MO_DMA21_PTR2 0x3000C0 // {24}RW* DMA Tab Ptr : Ch#21 | ||
122 | #define MO_DMA22_PTR2 0x3000C4 // {24}RW* DMA Tab Ptr : Ch#22 | ||
123 | #define MO_DMA23_PTR2 0x3000C8 // {24}RW* DMA Tab Ptr : Ch#23 | ||
124 | #define MO_DMA24_PTR2 0x3000CC // {24}RW* DMA Tab Ptr : Ch#24 | ||
125 | #define MO_DMA25_PTR2 0x3000D0 // {24}RW* DMA Tab Ptr : Ch#25 | ||
126 | #define MO_DMA26_PTR2 0x3000D4 // {24}RW* DMA Tab Ptr : Ch#26 | ||
127 | #define MO_DMA27_PTR2 0x3000D8 // {24}RW* DMA Tab Ptr : Ch#27 | ||
128 | #define MO_DMA28_PTR2 0x3000DC // {24}RW* DMA Tab Ptr : Ch#28 | ||
129 | #define MO_DMA29_PTR2 0x3000E0 // {24}RW* DMA Tab Ptr : Ch#29 | ||
130 | #define MO_DMA30_PTR2 0x3000E4 // {24}RW* DMA Tab Ptr : Ch#30 | ||
131 | #define MO_DMA31_PTR2 0x3000E8 // {24}RW* DMA Tab Ptr : Ch#31 | ||
132 | #define MO_DMA32_PTR2 0x3000EC // {24}RW* DMA Tab Ptr : Ch#32 | ||
133 | |||
134 | #define MO_DMA21_CNT1 0x300100 // {11}RW* DMA Buffer Size : Ch#21 | ||
135 | #define MO_DMA22_CNT1 0x300104 // {11}RW* DMA Buffer Size : Ch#22 | ||
136 | #define MO_DMA23_CNT1 0x300108 // {11}RW* DMA Buffer Size : Ch#23 | ||
137 | #define MO_DMA24_CNT1 0x30010C // {11}RW* DMA Buffer Size : Ch#24 | ||
138 | #define MO_DMA25_CNT1 0x300110 // {11}RW* DMA Buffer Size : Ch#25 | ||
139 | #define MO_DMA26_CNT1 0x300114 // {11}RW* DMA Buffer Size : Ch#26 | ||
140 | #define MO_DMA27_CNT1 0x300118 // {11}RW* DMA Buffer Size : Ch#27 | ||
141 | #define MO_DMA28_CNT1 0x30011C // {11}RW* DMA Buffer Size : Ch#28 | ||
142 | #define MO_DMA29_CNT1 0x300120 // {11}RW* DMA Buffer Size : Ch#29 | ||
143 | #define MO_DMA30_CNT1 0x300124 // {11}RW* DMA Buffer Size : Ch#30 | ||
144 | #define MO_DMA31_CNT1 0x300128 // {11}RW* DMA Buffer Size : Ch#31 | ||
145 | #define MO_DMA32_CNT1 0x30012C // {11}RW* DMA Buffer Size : Ch#32 | ||
146 | |||
147 | #define MO_DMA21_CNT2 0x300140 // {11}RW* DMA Table Size : Ch#21 | ||
148 | #define MO_DMA22_CNT2 0x300144 // {11}RW* DMA Table Size : Ch#22 | ||
149 | #define MO_DMA23_CNT2 0x300148 // {11}RW* DMA Table Size : Ch#23 | ||
150 | #define MO_DMA24_CNT2 0x30014C // {11}RW* DMA Table Size : Ch#24 | ||
151 | #define MO_DMA25_CNT2 0x300150 // {11}RW* DMA Table Size : Ch#25 | ||
152 | #define MO_DMA26_CNT2 0x300154 // {11}RW* DMA Table Size : Ch#26 | ||
153 | #define MO_DMA27_CNT2 0x300158 // {11}RW* DMA Table Size : Ch#27 | ||
154 | #define MO_DMA28_CNT2 0x30015C // {11}RW* DMA Table Size : Ch#28 | ||
155 | #define MO_DMA29_CNT2 0x300160 // {11}RW* DMA Table Size : Ch#29 | ||
156 | #define MO_DMA30_CNT2 0x300164 // {11}RW* DMA Table Size : Ch#30 | ||
157 | #define MO_DMA31_CNT2 0x300168 // {11}RW* DMA Table Size : Ch#31 | ||
158 | #define MO_DMA32_CNT2 0x30016C // {11}RW* DMA Table Size : Ch#32 | ||
159 | |||
160 | |||
161 | /* ---------------------------------------------------------------------- */ | ||
162 | /* Video registers */ | ||
163 | |||
164 | #define MO_VIDY_DMA 0x310000 // {64}RWp Video Y | ||
165 | #define MO_VIDU_DMA 0x310008 // {64}RWp Video U | ||
166 | #define MO_VIDV_DMA 0x310010 // {64}RWp Video V | ||
167 | #define MO_VBI_DMA 0x310018 // {64}RWp VBI (Vertical blanking interval) | ||
168 | |||
169 | #define MO_DEVICE_STATUS 0x310100 | ||
170 | #define MO_INPUT_FORMAT 0x310104 | ||
171 | #define MO_AGC_BURST 0x31010c | ||
172 | #define MO_CONTR_BRIGHT 0x310110 | ||
173 | #define MO_UV_SATURATION 0x310114 | ||
174 | #define MO_HUE 0x310118 | ||
175 | #define MO_HTOTAL 0x310120 | ||
176 | #define MO_HDELAY_EVEN 0x310124 | ||
177 | #define MO_HDELAY_ODD 0x310128 | ||
178 | #define MO_VDELAY_ODD 0x31012c | ||
179 | #define MO_VDELAY_EVEN 0x310130 | ||
180 | #define MO_HACTIVE_EVEN 0x31013c | ||
181 | #define MO_HACTIVE_ODD 0x310140 | ||
182 | #define MO_VACTIVE_EVEN 0x310144 | ||
183 | #define MO_VACTIVE_ODD 0x310148 | ||
184 | #define MO_HSCALE_EVEN 0x31014c | ||
185 | #define MO_HSCALE_ODD 0x310150 | ||
186 | #define MO_VSCALE_EVEN 0x310154 | ||
187 | #define MO_FILTER_EVEN 0x31015c | ||
188 | #define MO_VSCALE_ODD 0x310158 | ||
189 | #define MO_FILTER_ODD 0x310160 | ||
190 | #define MO_OUTPUT_FORMAT 0x310164 | ||
191 | |||
192 | #define MO_PLL_REG 0x310168 // PLL register | ||
193 | #define MO_PLL_ADJ_CTRL 0x31016c // PLL adjust control register | ||
194 | #define MO_SCONV_REG 0x310170 // sample rate conversion register | ||
195 | #define MO_SCONV_FIFO 0x310174 // sample rate conversion fifo | ||
196 | #define MO_SUB_STEP 0x310178 // subcarrier step size | ||
197 | #define MO_SUB_STEP_DR 0x31017c // subcarrier step size for DR line | ||
198 | |||
199 | #define MO_CAPTURE_CTRL 0x310180 // capture control | ||
200 | #define MO_COLOR_CTRL 0x310184 | ||
201 | #define MO_VBI_PACKET 0x310188 // vbi packet size / delay | ||
202 | #define MO_FIELD_COUNT 0x310190 // field counter | ||
203 | #define MO_VIP_CONFIG 0x310194 | ||
204 | #define MO_VBOS_CONTROL 0x3101a8 | ||
205 | |||
206 | #define MO_AGC_BACK_VBI 0x310200 | ||
207 | #define MO_AGC_SYNC_TIP1 0x310208 | ||
208 | |||
209 | #define MO_VIDY_GPCNT 0x31C020 // {16}RO Video Y general purpose counter | ||
210 | #define MO_VIDU_GPCNT 0x31C024 // {16}RO Video U general purpose counter | ||
211 | #define MO_VIDV_GPCNT 0x31C028 // {16}RO Video V general purpose counter | ||
212 | #define MO_VBI_GPCNT 0x31C02C // {16}RO VBI general purpose counter | ||
213 | #define MO_VIDY_GPCNTRL 0x31C030 // {2}WO Video Y general purpose control | ||
214 | #define MO_VIDU_GPCNTRL 0x31C034 // {2}WO Video U general purpose control | ||
215 | #define MO_VIDV_GPCNTRL 0x31C038 // {2}WO Video V general purpose control | ||
216 | #define MO_VBI_GPCNTRL 0x31C03C // {2}WO VBI general purpose counter | ||
217 | #define MO_VID_DMACNTRL 0x31C040 // {8}RW Video DMA control | ||
218 | #define MO_VID_XFR_STAT 0x31C044 // {1}RO Video transfer status | ||
219 | |||
220 | |||
221 | /* ---------------------------------------------------------------------- */ | ||
222 | /* audio registers */ | ||
223 | |||
224 | #define MO_AUDD_DMA 0x320000 // {64}RWp Audio downstream | ||
225 | #define MO_AUDU_DMA 0x320008 // {64}RWp Audio upstream | ||
226 | #define MO_AUDR_DMA 0x320010 // {64}RWp Audio RDS (downstream) | ||
227 | #define MO_AUDD_GPCNT 0x32C020 // {16}RO Audio down general purpose counter | ||
228 | #define MO_AUDU_GPCNT 0x32C024 // {16}RO Audio up general purpose counter | ||
229 | #define MO_AUDR_GPCNT 0x32C028 // {16}RO Audio RDS general purpose counter | ||
230 | #define MO_AUDD_GPCNTRL 0x32C030 // {2}WO Audio down general purpose control | ||
231 | #define MO_AUDU_GPCNTRL 0x32C034 // {2}WO Audio up general purpose control | ||
232 | #define MO_AUDR_GPCNTRL 0x32C038 // {2}WO Audio RDS general purpose control | ||
233 | #define MO_AUD_DMACNTRL 0x32C040 // {6}RW Audio DMA control | ||
234 | #define MO_AUD_XFR_STAT 0x32C044 // {1}RO Audio transfer status | ||
235 | #define MO_AUDD_LNGTH 0x32C048 // {12}RW Audio down line length | ||
236 | #define MO_AUDR_LNGTH 0x32C04C // {12}RW Audio RDS line length | ||
237 | |||
238 | #define AUD_INIT 0x320100 | ||
239 | #define AUD_INIT_LD 0x320104 | ||
240 | #define AUD_SOFT_RESET 0x320108 | ||
241 | #define AUD_I2SINPUTCNTL 0x320120 | ||
242 | #define AUD_BAUDRATE 0x320124 | ||
243 | #define AUD_I2SOUTPUTCNTL 0x320128 | ||
244 | #define AAGC_HYST 0x320134 | ||
245 | #define AAGC_GAIN 0x320138 | ||
246 | #define AAGC_DEF 0x32013c | ||
247 | #define AUD_IIR1_0_SEL 0x320150 | ||
248 | #define AUD_IIR1_0_SHIFT 0x320154 | ||
249 | #define AUD_IIR1_1_SEL 0x320158 | ||
250 | #define AUD_IIR1_1_SHIFT 0x32015c | ||
251 | #define AUD_IIR1_2_SEL 0x320160 | ||
252 | #define AUD_IIR1_2_SHIFT 0x320164 | ||
253 | #define AUD_IIR1_3_SEL 0x320168 | ||
254 | #define AUD_IIR1_3_SHIFT 0x32016c | ||
255 | #define AUD_IIR1_4_SEL 0x320170 | ||
256 | #define AUD_IIR1_4_SHIFT 0x32017c | ||
257 | #define AUD_IIR1_5_SEL 0x320180 | ||
258 | #define AUD_IIR1_5_SHIFT 0x320184 | ||
259 | #define AUD_IIR2_0_SEL 0x320190 | ||
260 | #define AUD_IIR2_0_SHIFT 0x320194 | ||
261 | #define AUD_IIR2_1_SEL 0x320198 | ||
262 | #define AUD_IIR2_1_SHIFT 0x32019c | ||
263 | #define AUD_IIR2_2_SEL 0x3201a0 | ||
264 | #define AUD_IIR2_2_SHIFT 0x3201a4 | ||
265 | #define AUD_IIR2_3_SEL 0x3201a8 | ||
266 | #define AUD_IIR2_3_SHIFT 0x3201ac | ||
267 | #define AUD_IIR3_0_SEL 0x3201c0 | ||
268 | #define AUD_IIR3_0_SHIFT 0x3201c4 | ||
269 | #define AUD_IIR3_1_SEL 0x3201c8 | ||
270 | #define AUD_IIR3_1_SHIFT 0x3201cc | ||
271 | #define AUD_IIR3_2_SEL 0x3201d0 | ||
272 | #define AUD_IIR3_2_SHIFT 0x3201d4 | ||
273 | #define AUD_IIR4_0_SEL 0x3201e0 | ||
274 | #define AUD_IIR4_0_SHIFT 0x3201e4 | ||
275 | #define AUD_IIR4_1_SEL 0x3201e8 | ||
276 | #define AUD_IIR4_1_SHIFT 0x3201ec | ||
277 | #define AUD_IIR4_2_SEL 0x3201f0 | ||
278 | #define AUD_IIR4_2_SHIFT 0x3201f4 | ||
279 | #define AUD_IIR4_0_CA0 0x320200 | ||
280 | #define AUD_IIR4_0_CA1 0x320204 | ||
281 | #define AUD_IIR4_0_CA2 0x320208 | ||
282 | #define AUD_IIR4_0_CB0 0x32020c | ||
283 | #define AUD_IIR4_0_CB1 0x320210 | ||
284 | #define AUD_IIR4_1_CA0 0x320214 | ||
285 | #define AUD_IIR4_1_CA1 0x320218 | ||
286 | #define AUD_IIR4_1_CA2 0x32021c | ||
287 | #define AUD_IIR4_1_CB0 0x320220 | ||
288 | #define AUD_IIR4_1_CB1 0x320224 | ||
289 | #define AUD_IIR4_2_CA0 0x320228 | ||
290 | #define AUD_IIR4_2_CA1 0x32022c | ||
291 | #define AUD_IIR4_2_CA2 0x320230 | ||
292 | #define AUD_IIR4_2_CB0 0x320234 | ||
293 | #define AUD_IIR4_2_CB1 0x320238 | ||
294 | #define AUD_HP_MD_IIR4_1 0x320250 | ||
295 | #define AUD_HP_PROG_IIR4_1 0x320254 | ||
296 | #define AUD_FM_MODE_ENABLE 0x320258 | ||
297 | #define AUD_POLY0_DDS_CONSTANT 0x320270 | ||
298 | #define AUD_DN0_FREQ 0x320274 | ||
299 | #define AUD_DN1_FREQ 0x320278 | ||
300 | #define AUD_DN1_FREQ_SHIFT 0x32027c | ||
301 | #define AUD_DN1_AFC 0x320280 | ||
302 | #define AUD_DN1_SRC_SEL 0x320284 | ||
303 | #define AUD_DN1_SHFT 0x320288 | ||
304 | #define AUD_DN2_FREQ 0x32028c | ||
305 | #define AUD_DN2_FREQ_SHIFT 0x320290 | ||
306 | #define AUD_DN2_AFC 0x320294 | ||
307 | #define AUD_DN2_SRC_SEL 0x320298 | ||
308 | #define AUD_DN2_SHFT 0x32029c | ||
309 | #define AUD_CRDC0_SRC_SEL 0x320300 | ||
310 | #define AUD_CRDC0_SHIFT 0x320304 | ||
311 | #define AUD_CORDIC_SHIFT_0 0x320308 | ||
312 | #define AUD_CRDC1_SRC_SEL 0x32030c | ||
313 | #define AUD_CRDC1_SHIFT 0x320310 | ||
314 | #define AUD_CORDIC_SHIFT_1 0x320314 | ||
315 | #define AUD_DCOC_0_SRC 0x320320 | ||
316 | #define AUD_DCOC0_SHIFT 0x320324 | ||
317 | #define AUD_DCOC_0_SHIFT_IN0 0x320328 | ||
318 | #define AUD_DCOC_0_SHIFT_IN1 0x32032c | ||
319 | #define AUD_DCOC_1_SRC 0x320330 | ||
320 | #define AUD_DCOC1_SHIFT 0x320334 | ||
321 | #define AUD_DCOC_1_SHIFT_IN0 0x320338 | ||
322 | #define AUD_DCOC_1_SHIFT_IN1 0x32033c | ||
323 | #define AUD_DCOC_2_SRC 0x320340 | ||
324 | #define AUD_DCOC2_SHIFT 0x320344 | ||
325 | #define AUD_DCOC_2_SHIFT_IN0 0x320348 | ||
326 | #define AUD_DCOC_2_SHIFT_IN1 0x32034c | ||
327 | #define AUD_DCOC_PASS_IN 0x320350 | ||
328 | #define AUD_PDET_SRC 0x320370 | ||
329 | #define AUD_PDET_SHIFT 0x320374 | ||
330 | #define AUD_PILOT_BQD_1_K0 0x320380 | ||
331 | #define AUD_PILOT_BQD_1_K1 0x320384 | ||
332 | #define AUD_PILOT_BQD_1_K2 0x320388 | ||
333 | #define AUD_PILOT_BQD_1_K3 0x32038c | ||
334 | #define AUD_PILOT_BQD_1_K4 0x320390 | ||
335 | #define AUD_PILOT_BQD_2_K0 0x320394 | ||
336 | #define AUD_PILOT_BQD_2_K1 0x320398 | ||
337 | #define AUD_PILOT_BQD_2_K2 0x32039c | ||
338 | #define AUD_PILOT_BQD_2_K3 0x3203a0 | ||
339 | #define AUD_PILOT_BQD_2_K4 0x3203a4 | ||
340 | #define AUD_THR_FR 0x3203c0 | ||
341 | #define AUD_X_PROG 0x3203c4 | ||
342 | #define AUD_Y_PROG 0x3203c8 | ||
343 | #define AUD_HARMONIC_MULT 0x3203cc | ||
344 | #define AUD_C1_UP_THR 0x3203d0 | ||
345 | #define AUD_C1_LO_THR 0x3203d4 | ||
346 | #define AUD_C2_UP_THR 0x3203d8 | ||
347 | #define AUD_C2_LO_THR 0x3203dc | ||
348 | #define AUD_PLL_EN 0x320400 | ||
349 | #define AUD_PLL_SRC 0x320404 | ||
350 | #define AUD_PLL_SHIFT 0x320408 | ||
351 | #define AUD_PLL_IF_SEL 0x32040c | ||
352 | #define AUD_PLL_IF_SHIFT 0x320410 | ||
353 | #define AUD_BIQUAD_PLL_K0 0x320414 | ||
354 | #define AUD_BIQUAD_PLL_K1 0x320418 | ||
355 | #define AUD_BIQUAD_PLL_K2 0x32041c | ||
356 | #define AUD_BIQUAD_PLL_K3 0x320420 | ||
357 | #define AUD_BIQUAD_PLL_K4 0x320424 | ||
358 | #define AUD_DEEMPH0_SRC_SEL 0x320440 | ||
359 | #define AUD_DEEMPH0_SHIFT 0x320444 | ||
360 | #define AUD_DEEMPH0_G0 0x320448 | ||
361 | #define AUD_DEEMPH0_A0 0x32044c | ||
362 | #define AUD_DEEMPH0_B0 0x320450 | ||
363 | #define AUD_DEEMPH0_A1 0x320454 | ||
364 | #define AUD_DEEMPH0_B1 0x320458 | ||
365 | #define AUD_DEEMPH1_SRC_SEL 0x32045c | ||
366 | #define AUD_DEEMPH1_SHIFT 0x320460 | ||
367 | #define AUD_DEEMPH1_G0 0x320464 | ||
368 | #define AUD_DEEMPH1_A0 0x320468 | ||
369 | #define AUD_DEEMPH1_B0 0x32046c | ||
370 | #define AUD_DEEMPH1_A1 0x320470 | ||
371 | #define AUD_DEEMPH1_B1 0x320474 | ||
372 | #define AUD_OUT0_SEL 0x320490 | ||
373 | #define AUD_OUT0_SHIFT 0x320494 | ||
374 | #define AUD_OUT1_SEL 0x320498 | ||
375 | #define AUD_OUT1_SHIFT 0x32049c | ||
376 | #define AUD_RDSI_SEL 0x3204a0 | ||
377 | #define AUD_RDSI_SHIFT 0x3204a4 | ||
378 | #define AUD_RDSQ_SEL 0x3204a8 | ||
379 | #define AUD_RDSQ_SHIFT 0x3204ac | ||
380 | #define AUD_DBX_IN_GAIN 0x320500 | ||
381 | #define AUD_DBX_WBE_GAIN 0x320504 | ||
382 | #define AUD_DBX_SE_GAIN 0x320508 | ||
383 | #define AUD_DBX_RMS_WBE 0x32050c | ||
384 | #define AUD_DBX_RMS_SE 0x320510 | ||
385 | #define AUD_DBX_SE_BYPASS 0x320514 | ||
386 | #define AUD_FAWDETCTL 0x320530 | ||
387 | #define AUD_FAWDETWINCTL 0x320534 | ||
388 | #define AUD_DEEMPHGAIN_R 0x320538 | ||
389 | #define AUD_DEEMPHNUMER1_R 0x32053c | ||
390 | #define AUD_DEEMPHNUMER2_R 0x320540 | ||
391 | #define AUD_DEEMPHDENOM1_R 0x320544 | ||
392 | #define AUD_DEEMPHDENOM2_R 0x320548 | ||
393 | #define AUD_ERRLOGPERIOD_R 0x32054c | ||
394 | #define AUD_ERRINTRPTTHSHLD1_R 0x320550 | ||
395 | #define AUD_ERRINTRPTTHSHLD2_R 0x320554 | ||
396 | #define AUD_ERRINTRPTTHSHLD3_R 0x320558 | ||
397 | #define AUD_NICAM_STATUS1 0x32055c | ||
398 | #define AUD_NICAM_STATUS2 0x320560 | ||
399 | #define AUD_ERRLOG1 0x320564 | ||
400 | #define AUD_ERRLOG2 0x320568 | ||
401 | #define AUD_ERRLOG3 0x32056c | ||
402 | #define AUD_DAC_BYPASS_L 0x320580 | ||
403 | #define AUD_DAC_BYPASS_R 0x320584 | ||
404 | #define AUD_DAC_BYPASS_CTL 0x320588 | ||
405 | #define AUD_CTL 0x32058c | ||
406 | #define AUD_STATUS 0x320590 | ||
407 | #define AUD_VOL_CTL 0x320594 | ||
408 | #define AUD_BAL_CTL 0x320598 | ||
409 | #define AUD_START_TIMER 0x3205b0 | ||
410 | #define AUD_MODE_CHG_TIMER 0x3205b4 | ||
411 | #define AUD_POLYPH80SCALEFAC 0x3205b8 | ||
412 | #define AUD_DMD_RA_DDS 0x3205bc | ||
413 | #define AUD_I2S_RA_DDS 0x3205c0 | ||
414 | #define AUD_RATE_THRES_DMD 0x3205d0 | ||
415 | #define AUD_RATE_THRES_I2S 0x3205d4 | ||
416 | #define AUD_RATE_ADJ1 0x3205d8 | ||
417 | #define AUD_RATE_ADJ2 0x3205dc | ||
418 | #define AUD_RATE_ADJ3 0x3205e0 | ||
419 | #define AUD_RATE_ADJ4 0x3205e4 | ||
420 | #define AUD_RATE_ADJ5 0x3205e8 | ||
421 | #define AUD_APB_IN_RATE_ADJ 0x3205ec | ||
422 | #define AUD_I2SCNTL 0x3205ec | ||
423 | #define AUD_PHASE_FIX_CTL 0x3205f0 | ||
424 | #define AUD_PLL_PRESCALE 0x320600 | ||
425 | #define AUD_PLL_DDS 0x320604 | ||
426 | #define AUD_PLL_INT 0x320608 | ||
427 | #define AUD_PLL_FRAC 0x32060c | ||
428 | #define AUD_PLL_JTAG 0x320620 | ||
429 | #define AUD_PLL_SPMP 0x320624 | ||
430 | #define AUD_AFE_12DB_EN 0x320628 | ||
431 | |||
432 | // Audio QAM Register Addresses | ||
433 | #define AUD_PDF_DDS_CNST_BYTE2 0x320d01 | ||
434 | #define AUD_PDF_DDS_CNST_BYTE1 0x320d02 | ||
435 | #define AUD_PDF_DDS_CNST_BYTE0 0x320d03 | ||
436 | #define AUD_PHACC_FREQ_8MSB 0x320d2a | ||
437 | #define AUD_PHACC_FREQ_8LSB 0x320d2b | ||
438 | #define AUD_QAM_MODE 0x320d04 | ||
439 | |||
440 | |||
441 | /* ---------------------------------------------------------------------- */ | ||
442 | /* transport stream registers */ | ||
443 | |||
444 | #define MO_TS_DMA 0x330000 // {64}RWp Transport stream downstream | ||
445 | #define MO_TS_GPCNT 0x33C020 // {16}RO TS general purpose counter | ||
446 | #define MO_TS_GPCNTRL 0x33C030 // {2}WO TS general purpose control | ||
447 | #define MO_TS_DMACNTRL 0x33C040 // {6}RW TS DMA control | ||
448 | #define MO_TS_XFR_STAT 0x33C044 // {1}RO TS transfer status | ||
449 | #define MO_TS_LNGTH 0x33C048 // {12}RW TS line length | ||
450 | |||
451 | #define TS_HW_SOP_CNTRL 0x33C04C | ||
452 | #define TS_GEN_CNTRL 0x33C050 | ||
453 | #define TS_BD_PKT_STAT 0x33C054 | ||
454 | #define TS_SOP_STAT 0x33C058 | ||
455 | #define TS_FIFO_OVFL_STAT 0x33C05C | ||
456 | #define TS_VALERR_CNTRL 0x33C060 | ||
457 | |||
458 | |||
459 | /* ---------------------------------------------------------------------- */ | ||
460 | /* VIP registers */ | ||
461 | |||
462 | #define MO_VIPD_DMA 0x340000 // {64}RWp VIP downstream | ||
463 | #define MO_VIPU_DMA 0x340008 // {64}RWp VIP upstream | ||
464 | #define MO_VIPD_GPCNT 0x34C020 // {16}RO VIP down general purpose counter | ||
465 | #define MO_VIPU_GPCNT 0x34C024 // {16}RO VIP up general purpose counter | ||
466 | #define MO_VIPD_GPCNTRL 0x34C030 // {2}WO VIP down general purpose control | ||
467 | #define MO_VIPU_GPCNTRL 0x34C034 // {2}WO VIP up general purpose control | ||
468 | #define MO_VIP_DMACNTRL 0x34C040 // {6}RW VIP DMA control | ||
469 | #define MO_VIP_XFR_STAT 0x34C044 // {1}RO VIP transfer status | ||
470 | #define MO_VIP_CFG 0x340048 // VIP configuration | ||
471 | #define MO_VIPU_CNTRL 0x34004C // VIP upstream control #1 | ||
472 | #define MO_VIPD_CNTRL 0x340050 // VIP downstream control #2 | ||
473 | #define MO_VIPD_LNGTH 0x340054 // VIP downstream line length | ||
474 | #define MO_VIP_BRSTLN 0x340058 // VIP burst length | ||
475 | #define MO_VIP_INTCNTRL 0x34C05C // VIP Interrupt Control | ||
476 | #define MO_VIP_XFTERM 0x340060 // VIP transfer terminate | ||
477 | |||
478 | |||
479 | /* ---------------------------------------------------------------------- */ | ||
480 | /* misc registers */ | ||
481 | |||
482 | #define MO_M2M_DMA 0x350000 // {64}RWp Mem2Mem DMA Bfr | ||
483 | #define MO_GP0_IO 0x350010 // {32}RW* GPIOoutput enablesdata I/O | ||
484 | #define MO_GP1_IO 0x350014 // {32}RW* GPIOoutput enablesdata I/O | ||
485 | #define MO_GP2_IO 0x350018 // {32}RW* GPIOoutput enablesdata I/O | ||
486 | #define MO_GP3_IO 0x35001C // {32}RW* GPIO Mode/Ctrloutput enables | ||
487 | #define MO_GPIO 0x350020 // {32}RW* GPIO I2C Ctrldata I/O | ||
488 | #define MO_GPOE 0x350024 // {32}RW GPIO I2C Ctrloutput enables | ||
489 | #define MO_GP_ISM 0x350028 // {16}WO GPIO Intr Sens/Pol | ||
490 | |||
491 | #define MO_PLL_B 0x35C008 // {32}RW* PLL Control for ASB bus clks | ||
492 | #define MO_M2M_CNT 0x35C024 // {32}RW Mem2Mem DMA Cnt | ||
493 | #define MO_M2M_XSUM 0x35C028 // {32}RO M2M XOR-Checksum | ||
494 | #define MO_CRC 0x35C02C // {16}RW CRC16 init/result | ||
495 | #define MO_CRC_D 0x35C030 // {32}WO CRC16 new data in | ||
496 | #define MO_TM_CNT_LDW 0x35C034 // {32}RO Timer : Counter low dword | ||
497 | #define MO_TM_CNT_UW 0x35C038 // {16}RO Timer : Counter high word | ||
498 | #define MO_TM_LMT_LDW 0x35C03C // {32}RW Timer : Limit low dword | ||
499 | #define MO_TM_LMT_UW 0x35C040 // {32}RW Timer : Limit high word | ||
500 | #define MO_PINMUX_IO 0x35C044 // {8}RW Pin Mux Control | ||
501 | #define MO_TSTSEL_IO 0x35C048 // {2}RW Pin Mux Control | ||
502 | #define MO_AFECFG_IO 0x35C04C // AFE configuration reg | ||
503 | #define MO_DDS_IO 0x35C050 // DDS Increment reg | ||
504 | #define MO_DDSCFG_IO 0x35C054 // DDS Configuration reg | ||
505 | #define MO_SAMPLE_IO 0x35C058 // IRIn sample reg | ||
506 | #define MO_SRST_IO 0x35C05C // Output system reset reg | ||
507 | |||
508 | #define MO_INT1_MSK 0x35C060 // DMA RISC interrupt mask | ||
509 | #define MO_INT1_STAT 0x35C064 // DMA RISC interrupt status | ||
510 | #define MO_INT1_MSTAT 0x35C068 // DMA RISC interrupt masked status | ||
511 | |||
512 | |||
513 | /* ---------------------------------------------------------------------- */ | ||
514 | /* i2c bus registers */ | ||
515 | |||
516 | #define MO_I2C 0x368000 // I2C data/control | ||
517 | #define MO_I2C_DIV (0xf<<4) | ||
518 | #define MO_I2C_SYNC (1<<3) | ||
519 | #define MO_I2C_W3B (1<<2) | ||
520 | #define MO_I2C_SCL (1<<1) | ||
521 | #define MO_I2C_SDA (1<<0) | ||
522 | |||
523 | |||
524 | /* ---------------------------------------------------------------------- */ | ||
525 | /* general purpose host registers */ | ||
526 | /* FIXME: tyops? s/0x35/0x38/ ?? */ | ||
527 | |||
528 | #define MO_GPHSTD_DMA 0x350000 // {64}RWp Host downstream | ||
529 | #define MO_GPHSTU_DMA 0x350008 // {64}RWp Host upstream | ||
530 | #define MO_GPHSTU_CNTRL 0x380048 // Host upstream control #1 | ||
531 | #define MO_GPHSTD_CNTRL 0x38004C // Host downstream control #2 | ||
532 | #define MO_GPHSTD_LNGTH 0x380050 // Host downstream line length | ||
533 | #define MO_GPHST_WSC 0x380054 // Host wait state control | ||
534 | #define MO_GPHST_XFR 0x380058 // Host transfer control | ||
535 | #define MO_GPHST_WDTH 0x38005C // Host interface width | ||
536 | #define MO_GPHST_HDSHK 0x380060 // Host peripheral handshake | ||
537 | #define MO_GPHST_MUX16 0x380064 // Host muxed 16-bit transfer parameters | ||
538 | #define MO_GPHST_MODE 0x380068 // Host mode select | ||
539 | |||
540 | #define MO_GPHSTD_GPCNT 0x35C020 // Host down general purpose counter | ||
541 | #define MO_GPHSTU_GPCNT 0x35C024 // Host up general purpose counter | ||
542 | #define MO_GPHSTD_GPCNTRL 0x38C030 // Host down general purpose control | ||
543 | #define MO_GPHSTU_GPCNTRL 0x38C034 // Host up general purpose control | ||
544 | #define MO_GPHST_DMACNTRL 0x38C040 // Host DMA control | ||
545 | #define MO_GPHST_XFR_STAT 0x38C044 // Host transfer status | ||
546 | #define MO_GPHST_SOFT_RST 0x38C06C // Host software reset | ||
547 | |||
548 | |||
549 | /* ---------------------------------------------------------------------- */ | ||
550 | /* RISC instructions */ | ||
551 | |||
552 | #define RISC_SYNC 0x80000000 | ||
553 | #define RISC_SYNC_ODD 0x80000000 | ||
554 | #define RISC_SYNC_EVEN 0x80000200 | ||
555 | #define RISC_RESYNC 0x80008000 | ||
556 | #define RISC_RESYNC_ODD 0x80008000 | ||
557 | #define RISC_RESYNC_EVEN 0x80008200 | ||
558 | #define RISC_WRITE 0x10000000 | ||
559 | #define RISC_WRITEC 0x50000000 | ||
560 | #define RISC_READ 0x90000000 | ||
561 | #define RISC_READC 0xA0000000 | ||
562 | #define RISC_JUMP 0x70000000 | ||
563 | #define RISC_SKIP 0x20000000 | ||
564 | #define RISC_WRITERM 0xB0000000 | ||
565 | #define RISC_WRITECM 0xC0000000 | ||
566 | #define RISC_WRITECR 0xD0000000 | ||
567 | #define RISC_IMM 0x00000001 | ||
568 | |||
569 | #define RISC_SOL 0x08000000 | ||
570 | #define RISC_EOL 0x04000000 | ||
571 | |||
572 | #define RISC_IRQ2 0x02000000 | ||
573 | #define RISC_IRQ1 0x01000000 | ||
574 | |||
575 | #define RISC_CNT_NONE 0x00000000 | ||
576 | #define RISC_CNT_INC 0x00010000 | ||
577 | #define RISC_CNT_RSVR 0x00020000 | ||
578 | #define RISC_CNT_RESET 0x00030000 | ||
579 | #define RISC_JMP_SRP 0x01 | ||
580 | |||
581 | |||
582 | /* ---------------------------------------------------------------------- */ | ||
583 | /* various constants */ | ||
584 | |||
585 | // DMA | ||
586 | /* Interrupt mask/status */ | ||
587 | #define PCI_INT_VIDINT (1 << 0) | ||
588 | #define PCI_INT_AUDINT (1 << 1) | ||
589 | #define PCI_INT_TSINT (1 << 2) | ||
590 | #define PCI_INT_VIPINT (1 << 3) | ||
591 | #define PCI_INT_HSTINT (1 << 4) | ||
592 | #define PCI_INT_TM1INT (1 << 5) | ||
593 | #define PCI_INT_SRCDMAINT (1 << 6) | ||
594 | #define PCI_INT_DSTDMAINT (1 << 7) | ||
595 | #define PCI_INT_RISC_RD_BERRINT (1 << 10) | ||
596 | #define PCI_INT_RISC_WR_BERRINT (1 << 11) | ||
597 | #define PCI_INT_BRDG_BERRINT (1 << 12) | ||
598 | #define PCI_INT_SRC_DMA_BERRINT (1 << 13) | ||
599 | #define PCI_INT_DST_DMA_BERRINT (1 << 14) | ||
600 | #define PCI_INT_IPB_DMA_BERRINT (1 << 15) | ||
601 | #define PCI_INT_I2CDONE (1 << 16) | ||
602 | #define PCI_INT_I2CRACK (1 << 17) | ||
603 | #define PCI_INT_IR_SMPINT (1 << 18) | ||
604 | #define PCI_INT_GPIO_INT0 (1 << 19) | ||
605 | #define PCI_INT_GPIO_INT1 (1 << 20) | ||
606 | |||
607 | #define SEL_BTSC 0x01 | ||
608 | #define SEL_EIAJ 0x02 | ||
609 | #define SEL_A2 0x04 | ||
610 | #define SEL_SAP 0x08 | ||
611 | #define SEL_NICAM 0x10 | ||
612 | #define SEL_FMRADIO 0x20 | ||
613 | |||
614 | // AUD_CTL | ||
615 | #define AUD_INT_DN_RISCI1 (1 << 0) | ||
616 | #define AUD_INT_UP_RISCI1 (1 << 1) | ||
617 | #define AUD_INT_RDS_DN_RISCI1 (1 << 2) | ||
618 | #define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */ | ||
619 | #define AUD_INT_UP_RISCI2 (1 << 5) | ||
620 | #define AUD_INT_RDS_DN_RISCI2 (1 << 6) | ||
621 | #define AUD_INT_DN_SYNC (1 << 12) | ||
622 | #define AUD_INT_UP_SYNC (1 << 13) | ||
623 | #define AUD_INT_RDS_DN_SYNC (1 << 14) | ||
624 | #define AUD_INT_OPC_ERR (1 << 16) | ||
625 | #define AUD_INT_BER_IRQ (1 << 20) | ||
626 | #define AUD_INT_MCHG_IRQ (1 << 21) | ||
627 | |||
628 | #define EN_BTSC_FORCE_MONO 0 | ||
629 | #define EN_BTSC_FORCE_STEREO 1 | ||
630 | #define EN_BTSC_FORCE_SAP 2 | ||
631 | #define EN_BTSC_AUTO_STEREO 3 | ||
632 | #define EN_BTSC_AUTO_SAP 4 | ||
633 | |||
634 | #define EN_A2_FORCE_MONO1 8 | ||
635 | #define EN_A2_FORCE_MONO2 9 | ||
636 | #define EN_A2_FORCE_STEREO 10 | ||
637 | #define EN_A2_AUTO_MONO2 11 | ||
638 | #define EN_A2_AUTO_STEREO 12 | ||
639 | |||
640 | #define EN_EIAJ_FORCE_MONO1 16 | ||
641 | #define EN_EIAJ_FORCE_MONO2 17 | ||
642 | #define EN_EIAJ_FORCE_STEREO 18 | ||
643 | #define EN_EIAJ_AUTO_MONO2 19 | ||
644 | #define EN_EIAJ_AUTO_STEREO 20 | ||
645 | |||
646 | #define EN_NICAM_FORCE_MONO1 32 | ||
647 | #define EN_NICAM_FORCE_MONO2 33 | ||
648 | #define EN_NICAM_FORCE_STEREO 34 | ||
649 | #define EN_NICAM_AUTO_MONO2 35 | ||
650 | #define EN_NICAM_AUTO_STEREO 36 | ||
651 | |||
652 | #define EN_FMRADIO_FORCE_MONO 24 | ||
653 | #define EN_FMRADIO_FORCE_STEREO 25 | ||
654 | #define EN_FMRADIO_AUTO_STEREO 26 | ||
655 | |||
656 | #define EN_NICAM_AUTO_FALLBACK 0x00000040 | ||
657 | #define EN_FMRADIO_EN_RDS 0x00000200 | ||
658 | #define EN_NICAM_TRY_AGAIN_BIT 0x00000400 | ||
659 | #define EN_DAC_ENABLE 0x00001000 | ||
660 | #define EN_I2SOUT_ENABLE 0x00002000 | ||
661 | #define EN_I2SIN_STR2DAC 0x00004000 | ||
662 | #define EN_I2SIN_ENABLE 0x00008000 | ||
663 | |||
664 | #define EN_DMTRX_SUMDIFF (0 << 7) | ||
665 | #define EN_DMTRX_SUMR (1 << 7) | ||
666 | #define EN_DMTRX_LR (2 << 7) | ||
667 | #define EN_DMTRX_MONO (3 << 7) | ||
668 | #define EN_DMTRX_BYPASS (1 << 11) | ||
669 | |||
670 | // Video | ||
671 | #define VID_CAPTURE_CONTROL 0x310180 | ||
672 | |||
673 | #define CX23880_CAP_CTL_CAPTURE_VBI_ODD (1<<3) | ||
674 | #define CX23880_CAP_CTL_CAPTURE_VBI_EVEN (1<<2) | ||
675 | #define CX23880_CAP_CTL_CAPTURE_ODD (1<<1) | ||
676 | #define CX23880_CAP_CTL_CAPTURE_EVEN (1<<0) | ||
677 | |||
678 | #define VideoInputMux0 0x0 | ||
679 | #define VideoInputMux1 0x1 | ||
680 | #define VideoInputMux2 0x2 | ||
681 | #define VideoInputMux3 0x3 | ||
682 | #define VideoInputTuner 0x0 | ||
683 | #define VideoInputComposite 0x1 | ||
684 | #define VideoInputSVideo 0x2 | ||
685 | #define VideoInputOther 0x3 | ||
686 | |||
687 | #define Xtal0 0x1 | ||
688 | #define Xtal1 0x2 | ||
689 | #define XtalAuto 0x3 | ||
690 | |||
691 | #define VideoFormatAuto 0x0 | ||
692 | #define VideoFormatNTSC 0x1 | ||
693 | #define VideoFormatNTSCJapan 0x2 | ||
694 | #define VideoFormatNTSC443 0x3 | ||
695 | #define VideoFormatPAL 0x4 | ||
696 | #define VideoFormatPALB 0x4 | ||
697 | #define VideoFormatPALD 0x4 | ||
698 | #define VideoFormatPALG 0x4 | ||
699 | #define VideoFormatPALH 0x4 | ||
700 | #define VideoFormatPALI 0x4 | ||
701 | #define VideoFormatPALBDGHI 0x4 | ||
702 | #define VideoFormatPALM 0x5 | ||
703 | #define VideoFormatPALN 0x6 | ||
704 | #define VideoFormatPALNC 0x7 | ||
705 | #define VideoFormatPAL60 0x8 | ||
706 | #define VideoFormatSECAM 0x9 | ||
707 | |||
708 | #define VideoFormatAuto27MHz 0x10 | ||
709 | #define VideoFormatNTSC27MHz 0x11 | ||
710 | #define VideoFormatNTSCJapan27MHz 0x12 | ||
711 | #define VideoFormatNTSC44327MHz 0x13 | ||
712 | #define VideoFormatPAL27MHz 0x14 | ||
713 | #define VideoFormatPALB27MHz 0x14 | ||
714 | #define VideoFormatPALD27MHz 0x14 | ||
715 | #define VideoFormatPALG27MHz 0x14 | ||
716 | #define VideoFormatPALH27MHz 0x14 | ||
717 | #define VideoFormatPALI27MHz 0x14 | ||
718 | #define VideoFormatPALBDGHI27MHz 0x14 | ||
719 | #define VideoFormatPALM27MHz 0x15 | ||
720 | #define VideoFormatPALN27MHz 0x16 | ||
721 | #define VideoFormatPALNC27MHz 0x17 | ||
722 | #define VideoFormatPAL6027MHz 0x18 | ||
723 | #define VideoFormatSECAM27MHz 0x19 | ||
724 | |||
725 | #define NominalUSECAM 0x87 | ||
726 | #define NominalVSECAM 0x85 | ||
727 | #define NominalUNTSC 0xFE | ||
728 | #define NominalVNTSC 0xB4 | ||
729 | |||
730 | #define NominalContrast 0xD8 | ||
731 | |||
732 | #define HFilterAutoFormat 0x0 | ||
733 | #define HFilterCIF 0x1 | ||
734 | #define HFilterQCIF 0x2 | ||
735 | #define HFilterICON 0x3 | ||
736 | |||
737 | #define VFilter2TapInterpolate 0 | ||
738 | #define VFilter3TapInterpolate 1 | ||
739 | #define VFilter4TapInterpolate 2 | ||
740 | #define VFilter5TapInterpolate 3 | ||
741 | #define VFilter2TapNoInterpolate 4 | ||
742 | #define VFilter3TapNoInterpolate 5 | ||
743 | #define VFilter4TapNoInterpolate 6 | ||
744 | #define VFilter5TapNoInterpolate 7 | ||
745 | |||
746 | #define ColorFormatRGB32 0x0000 | ||
747 | #define ColorFormatRGB24 0x0011 | ||
748 | #define ColorFormatRGB16 0x0022 | ||
749 | #define ColorFormatRGB15 0x0033 | ||
750 | #define ColorFormatYUY2 0x0044 | ||
751 | #define ColorFormatBTYUV 0x0055 | ||
752 | #define ColorFormatY8 0x0066 | ||
753 | #define ColorFormatRGB8 0x0077 | ||
754 | #define ColorFormatPL422 0x0088 | ||
755 | #define ColorFormatPL411 0x0099 | ||
756 | #define ColorFormatYUV12 0x00AA | ||
757 | #define ColorFormatYUV9 0x00BB | ||
758 | #define ColorFormatRAW 0x00EE | ||
759 | #define ColorFormatBSWAP 0x0300 | ||
760 | #define ColorFormatWSWAP 0x0c00 | ||
761 | #define ColorFormatEvenMask 0x050f | ||
762 | #define ColorFormatOddMask 0x0af0 | ||
763 | #define ColorFormatGamma 0x1000 | ||
764 | |||
765 | #define Interlaced 0x1 | ||
766 | #define NonInterlaced 0x0 | ||
767 | |||
768 | #define FieldEven 0x1 | ||
769 | #define FieldOdd 0x0 | ||
770 | |||
771 | #define TGReadWriteMode 0x0 | ||
772 | #define TGEnableMode 0x1 | ||
773 | |||
774 | #define DV_CbAlign 0x0 | ||
775 | #define DV_Y0Align 0x1 | ||
776 | #define DV_CrAlign 0x2 | ||
777 | #define DV_Y1Align 0x3 | ||
778 | |||
779 | #define DVF_Analog 0x0 | ||
780 | #define DVF_CCIR656 0x1 | ||
781 | #define DVF_ByteStream 0x2 | ||
782 | #define DVF_ExtVSYNC 0x4 | ||
783 | #define DVF_ExtField 0x5 | ||
784 | |||
785 | #define CHANNEL_VID_Y 0x1 | ||
786 | #define CHANNEL_VID_U 0x2 | ||
787 | #define CHANNEL_VID_V 0x3 | ||
788 | #define CHANNEL_VID_VBI 0x4 | ||
789 | #define CHANNEL_AUD_DN 0x5 | ||
790 | #define CHANNEL_AUD_UP 0x6 | ||
791 | #define CHANNEL_AUD_RDS_DN 0x7 | ||
792 | #define CHANNEL_MPEG_DN 0x8 | ||
793 | #define CHANNEL_VIP_DN 0x9 | ||
794 | #define CHANNEL_VIP_UP 0xA | ||
795 | #define CHANNEL_HOST_DN 0xB | ||
796 | #define CHANNEL_HOST_UP 0xC | ||
797 | #define CHANNEL_FIRST 0x1 | ||
798 | #define CHANNEL_LAST 0xC | ||
799 | |||
800 | #define GP_COUNT_CONTROL_NONE 0x0 | ||
801 | #define GP_COUNT_CONTROL_INC 0x1 | ||
802 | #define GP_COUNT_CONTROL_RESERVED 0x2 | ||
803 | #define GP_COUNT_CONTROL_RESET 0x3 | ||
804 | |||
805 | #define PLL_PRESCALE_BY_2 2 | ||
806 | #define PLL_PRESCALE_BY_3 3 | ||
807 | #define PLL_PRESCALE_BY_4 4 | ||
808 | #define PLL_PRESCALE_BY_5 5 | ||
809 | |||
810 | #define HLNotchFilter4xFsc 0 | ||
811 | #define HLNotchFilterSquare 1 | ||
812 | #define HLNotchFilter135NTSC 2 | ||
813 | #define HLNotchFilter135PAL 3 | ||
814 | |||
815 | #define NTSC_8x_SUB_CARRIER 28.63636E6 | ||
816 | #define PAL_8x_SUB_CARRIER 35.46895E6 | ||
817 | |||
818 | // Default analog settings | ||
819 | #define DEFAULT_HUE_NTSC 0x00 | ||
820 | #define DEFAULT_BRIGHTNESS_NTSC 0x00 | ||
821 | #define DEFAULT_CONTRAST_NTSC 0x39 | ||
822 | #define DEFAULT_SAT_U_NTSC 0x7F | ||
823 | #define DEFAULT_SAT_V_NTSC 0x5A | ||
824 | |||
825 | typedef enum | ||
826 | { | ||
827 | SOURCE_TUNER = 0, | ||
828 | SOURCE_COMPOSITE, | ||
829 | SOURCE_SVIDEO, | ||
830 | SOURCE_OTHER1, | ||
831 | SOURCE_OTHER2, | ||
832 | SOURCE_COMPVIASVIDEO, | ||
833 | SOURCE_CCIR656 | ||
834 | } VIDEOSOURCETYPE; | ||
835 | |||
836 | #endif /* _CX88_REG_H_ */ | ||
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c new file mode 100644 index 00000000000..770ec05b5e9 --- /dev/null +++ b/drivers/media/video/cx88/cx88-tvaudio.c | |||
@@ -0,0 +1,1059 @@ | |||
1 | /* | ||
2 | |||
3 | cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver | ||
4 | |||
5 | (c) 2001 Michael Eskin, Tom Zakrajsek [Windows version] | ||
6 | (c) 2002 Yurij Sysoev <yurij@naturesoft.net> | ||
7 | (c) 2003 Gerd Knorr <kraxel@bytesex.org> | ||
8 | |||
9 | ----------------------------------------------------------------------- | ||
10 | |||
11 | Lot of voodoo here. Even the data sheet doesn't help to | ||
12 | understand what is going on here, the documentation for the audio | ||
13 | part of the cx2388x chip is *very* bad. | ||
14 | |||
15 | Some of this comes from party done linux driver sources I got from | ||
16 | [undocumented]. | ||
17 | |||
18 | Some comes from the dscaler sources, one of the dscaler driver guy works | ||
19 | for Conexant ... | ||
20 | |||
21 | ----------------------------------------------------------------------- | ||
22 | |||
23 | This program is free software; you can redistribute it and/or modify | ||
24 | it under the terms of the GNU General Public License as published by | ||
25 | the Free Software Foundation; either version 2 of the License, or | ||
26 | (at your option) any later version. | ||
27 | |||
28 | This program is distributed in the hope that it will be useful, | ||
29 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
30 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
31 | GNU General Public License for more details. | ||
32 | |||
33 | You should have received a copy of the GNU General Public License | ||
34 | along with this program; if not, write to the Free Software | ||
35 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
36 | */ | ||
37 | |||
38 | #include <linux/module.h> | ||
39 | #include <linux/errno.h> | ||
40 | #include <linux/freezer.h> | ||
41 | #include <linux/kernel.h> | ||
42 | #include <linux/mm.h> | ||
43 | #include <linux/poll.h> | ||
44 | #include <linux/signal.h> | ||
45 | #include <linux/ioport.h> | ||
46 | #include <linux/types.h> | ||
47 | #include <linux/interrupt.h> | ||
48 | #include <linux/vmalloc.h> | ||
49 | #include <linux/init.h> | ||
50 | #include <linux/delay.h> | ||
51 | #include <linux/kthread.h> | ||
52 | |||
53 | #include "cx88.h" | ||
54 | |||
55 | static unsigned int audio_debug; | ||
56 | module_param(audio_debug, int, 0644); | ||
57 | MODULE_PARM_DESC(audio_debug, "enable debug messages [audio]"); | ||
58 | |||
59 | static unsigned int always_analog; | ||
60 | module_param(always_analog,int,0644); | ||
61 | MODULE_PARM_DESC(always_analog,"force analog audio out"); | ||
62 | |||
63 | static unsigned int radio_deemphasis; | ||
64 | module_param(radio_deemphasis,int,0644); | ||
65 | MODULE_PARM_DESC(radio_deemphasis, "Radio deemphasis time constant, " | ||
66 | "0=None, 1=50us (elsewhere), 2=75us (USA)"); | ||
67 | |||
68 | #define dprintk(fmt, arg...) if (audio_debug) \ | ||
69 | printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg) | ||
70 | |||
71 | /* ----------------------------------------------------------- */ | ||
72 | |||
73 | static const char * const aud_ctl_names[64] = { | ||
74 | [EN_BTSC_FORCE_MONO] = "BTSC_FORCE_MONO", | ||
75 | [EN_BTSC_FORCE_STEREO] = "BTSC_FORCE_STEREO", | ||
76 | [EN_BTSC_FORCE_SAP] = "BTSC_FORCE_SAP", | ||
77 | [EN_BTSC_AUTO_STEREO] = "BTSC_AUTO_STEREO", | ||
78 | [EN_BTSC_AUTO_SAP] = "BTSC_AUTO_SAP", | ||
79 | [EN_A2_FORCE_MONO1] = "A2_FORCE_MONO1", | ||
80 | [EN_A2_FORCE_MONO2] = "A2_FORCE_MONO2", | ||
81 | [EN_A2_FORCE_STEREO] = "A2_FORCE_STEREO", | ||
82 | [EN_A2_AUTO_MONO2] = "A2_AUTO_MONO2", | ||
83 | [EN_A2_AUTO_STEREO] = "A2_AUTO_STEREO", | ||
84 | [EN_EIAJ_FORCE_MONO1] = "EIAJ_FORCE_MONO1", | ||
85 | [EN_EIAJ_FORCE_MONO2] = "EIAJ_FORCE_MONO2", | ||
86 | [EN_EIAJ_FORCE_STEREO] = "EIAJ_FORCE_STEREO", | ||
87 | [EN_EIAJ_AUTO_MONO2] = "EIAJ_AUTO_MONO2", | ||
88 | [EN_EIAJ_AUTO_STEREO] = "EIAJ_AUTO_STEREO", | ||
89 | [EN_NICAM_FORCE_MONO1] = "NICAM_FORCE_MONO1", | ||
90 | [EN_NICAM_FORCE_MONO2] = "NICAM_FORCE_MONO2", | ||
91 | [EN_NICAM_FORCE_STEREO] = "NICAM_FORCE_STEREO", | ||
92 | [EN_NICAM_AUTO_MONO2] = "NICAM_AUTO_MONO2", | ||
93 | [EN_NICAM_AUTO_STEREO] = "NICAM_AUTO_STEREO", | ||
94 | [EN_FMRADIO_FORCE_MONO] = "FMRADIO_FORCE_MONO", | ||
95 | [EN_FMRADIO_FORCE_STEREO] = "FMRADIO_FORCE_STEREO", | ||
96 | [EN_FMRADIO_AUTO_STEREO] = "FMRADIO_AUTO_STEREO", | ||
97 | }; | ||
98 | |||
99 | struct rlist { | ||
100 | u32 reg; | ||
101 | u32 val; | ||
102 | }; | ||
103 | |||
104 | static void set_audio_registers(struct cx88_core *core, const struct rlist *l) | ||
105 | { | ||
106 | int i; | ||
107 | |||
108 | for (i = 0; l[i].reg; i++) { | ||
109 | switch (l[i].reg) { | ||
110 | case AUD_PDF_DDS_CNST_BYTE2: | ||
111 | case AUD_PDF_DDS_CNST_BYTE1: | ||
112 | case AUD_PDF_DDS_CNST_BYTE0: | ||
113 | case AUD_QAM_MODE: | ||
114 | case AUD_PHACC_FREQ_8MSB: | ||
115 | case AUD_PHACC_FREQ_8LSB: | ||
116 | cx_writeb(l[i].reg, l[i].val); | ||
117 | break; | ||
118 | default: | ||
119 | cx_write(l[i].reg, l[i].val); | ||
120 | break; | ||
121 | } | ||
122 | } | ||
123 | } | ||
124 | |||
125 | static void set_audio_start(struct cx88_core *core, u32 mode) | ||
126 | { | ||
127 | /* mute */ | ||
128 | cx_write(AUD_VOL_CTL, (1 << 6)); | ||
129 | |||
130 | /* start programming */ | ||
131 | cx_write(AUD_INIT, mode); | ||
132 | cx_write(AUD_INIT_LD, 0x0001); | ||
133 | cx_write(AUD_SOFT_RESET, 0x0001); | ||
134 | } | ||
135 | |||
136 | static void set_audio_finish(struct cx88_core *core, u32 ctl) | ||
137 | { | ||
138 | u32 volume; | ||
139 | |||
140 | /* restart dma; This avoids buzz in NICAM and is good in others */ | ||
141 | cx88_stop_audio_dma(core); | ||
142 | cx_write(AUD_RATE_THRES_DMD, 0x000000C0); | ||
143 | cx88_start_audio_dma(core); | ||
144 | |||
145 | if (core->board.mpeg & CX88_MPEG_BLACKBIRD) { | ||
146 | cx_write(AUD_I2SINPUTCNTL, 4); | ||
147 | cx_write(AUD_BAUDRATE, 1); | ||
148 | /* 'pass-thru mode': this enables the i2s output to the mpeg encoder */ | ||
149 | cx_set(AUD_CTL, EN_I2SOUT_ENABLE); | ||
150 | cx_write(AUD_I2SOUTPUTCNTL, 1); | ||
151 | cx_write(AUD_I2SCNTL, 0); | ||
152 | /* cx_write(AUD_APB_IN_RATE_ADJ, 0); */ | ||
153 | } | ||
154 | if ((always_analog) || (!(core->board.mpeg & CX88_MPEG_BLACKBIRD))) { | ||
155 | ctl |= EN_DAC_ENABLE; | ||
156 | cx_write(AUD_CTL, ctl); | ||
157 | } | ||
158 | |||
159 | /* finish programming */ | ||
160 | cx_write(AUD_SOFT_RESET, 0x0000); | ||
161 | |||
162 | /* unmute */ | ||
163 | volume = cx_sread(SHADOW_AUD_VOL_CTL); | ||
164 | cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume); | ||
165 | |||
166 | core->last_change = jiffies; | ||
167 | } | ||
168 | |||
169 | /* ----------------------------------------------------------- */ | ||
170 | |||
171 | static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap, | ||
172 | u32 mode) | ||
173 | { | ||
174 | static const struct rlist btsc[] = { | ||
175 | {AUD_AFE_12DB_EN, 0x00000001}, | ||
176 | {AUD_OUT1_SEL, 0x00000013}, | ||
177 | {AUD_OUT1_SHIFT, 0x00000000}, | ||
178 | {AUD_POLY0_DDS_CONSTANT, 0x0012010c}, | ||
179 | {AUD_DMD_RA_DDS, 0x00c3e7aa}, | ||
180 | {AUD_DBX_IN_GAIN, 0x00004734}, | ||
181 | {AUD_DBX_WBE_GAIN, 0x00004640}, | ||
182 | {AUD_DBX_SE_GAIN, 0x00008d31}, | ||
183 | {AUD_DCOC_0_SRC, 0x0000001a}, | ||
184 | {AUD_IIR1_4_SEL, 0x00000021}, | ||
185 | {AUD_DCOC_PASS_IN, 0x00000003}, | ||
186 | {AUD_DCOC_0_SHIFT_IN0, 0x0000000a}, | ||
187 | {AUD_DCOC_0_SHIFT_IN1, 0x00000008}, | ||
188 | {AUD_DCOC_1_SHIFT_IN0, 0x0000000a}, | ||
189 | {AUD_DCOC_1_SHIFT_IN1, 0x00000008}, | ||
190 | {AUD_DN0_FREQ, 0x0000283b}, | ||
191 | {AUD_DN2_SRC_SEL, 0x00000008}, | ||
192 | {AUD_DN2_FREQ, 0x00003000}, | ||
193 | {AUD_DN2_AFC, 0x00000002}, | ||
194 | {AUD_DN2_SHFT, 0x00000000}, | ||
195 | {AUD_IIR2_2_SEL, 0x00000020}, | ||
196 | {AUD_IIR2_2_SHIFT, 0x00000000}, | ||
197 | {AUD_IIR2_3_SEL, 0x0000001f}, | ||
198 | {AUD_IIR2_3_SHIFT, 0x00000000}, | ||
199 | {AUD_CRDC1_SRC_SEL, 0x000003ce}, | ||
200 | {AUD_CRDC1_SHIFT, 0x00000000}, | ||
201 | {AUD_CORDIC_SHIFT_1, 0x00000007}, | ||
202 | {AUD_DCOC_1_SRC, 0x0000001b}, | ||
203 | {AUD_DCOC1_SHIFT, 0x00000000}, | ||
204 | {AUD_RDSI_SEL, 0x00000008}, | ||
205 | {AUD_RDSQ_SEL, 0x00000008}, | ||
206 | {AUD_RDSI_SHIFT, 0x00000000}, | ||
207 | {AUD_RDSQ_SHIFT, 0x00000000}, | ||
208 | {AUD_POLYPH80SCALEFAC, 0x00000003}, | ||
209 | { /* end of list */ }, | ||
210 | }; | ||
211 | static const struct rlist btsc_sap[] = { | ||
212 | {AUD_AFE_12DB_EN, 0x00000001}, | ||
213 | {AUD_DBX_IN_GAIN, 0x00007200}, | ||
214 | {AUD_DBX_WBE_GAIN, 0x00006200}, | ||
215 | {AUD_DBX_SE_GAIN, 0x00006200}, | ||
216 | {AUD_IIR1_1_SEL, 0x00000000}, | ||
217 | {AUD_IIR1_3_SEL, 0x00000001}, | ||
218 | {AUD_DN1_SRC_SEL, 0x00000007}, | ||
219 | {AUD_IIR1_4_SHIFT, 0x00000006}, | ||
220 | {AUD_IIR2_1_SHIFT, 0x00000000}, | ||
221 | {AUD_IIR2_2_SHIFT, 0x00000000}, | ||
222 | {AUD_IIR3_0_SHIFT, 0x00000000}, | ||
223 | {AUD_IIR3_1_SHIFT, 0x00000000}, | ||
224 | {AUD_IIR3_0_SEL, 0x0000000d}, | ||
225 | {AUD_IIR3_1_SEL, 0x0000000e}, | ||
226 | {AUD_DEEMPH1_SRC_SEL, 0x00000014}, | ||
227 | {AUD_DEEMPH1_SHIFT, 0x00000000}, | ||
228 | {AUD_DEEMPH1_G0, 0x00004000}, | ||
229 | {AUD_DEEMPH1_A0, 0x00000000}, | ||
230 | {AUD_DEEMPH1_B0, 0x00000000}, | ||
231 | {AUD_DEEMPH1_A1, 0x00000000}, | ||
232 | {AUD_DEEMPH1_B1, 0x00000000}, | ||
233 | {AUD_OUT0_SEL, 0x0000003f}, | ||
234 | {AUD_OUT1_SEL, 0x0000003f}, | ||
235 | {AUD_DN1_AFC, 0x00000002}, | ||
236 | {AUD_DCOC_0_SHIFT_IN0, 0x0000000a}, | ||
237 | {AUD_DCOC_0_SHIFT_IN1, 0x00000008}, | ||
238 | {AUD_DCOC_1_SHIFT_IN0, 0x0000000a}, | ||
239 | {AUD_DCOC_1_SHIFT_IN1, 0x00000008}, | ||
240 | {AUD_IIR1_0_SEL, 0x0000001d}, | ||
241 | {AUD_IIR1_2_SEL, 0x0000001e}, | ||
242 | {AUD_IIR2_1_SEL, 0x00000002}, | ||
243 | {AUD_IIR2_2_SEL, 0x00000004}, | ||
244 | {AUD_IIR3_2_SEL, 0x0000000f}, | ||
245 | {AUD_DCOC2_SHIFT, 0x00000001}, | ||
246 | {AUD_IIR3_2_SHIFT, 0x00000001}, | ||
247 | {AUD_DEEMPH0_SRC_SEL, 0x00000014}, | ||
248 | {AUD_CORDIC_SHIFT_1, 0x00000006}, | ||
249 | {AUD_POLY0_DDS_CONSTANT, 0x000e4db2}, | ||
250 | {AUD_DMD_RA_DDS, 0x00f696e6}, | ||
251 | {AUD_IIR2_3_SEL, 0x00000025}, | ||
252 | {AUD_IIR1_4_SEL, 0x00000021}, | ||
253 | {AUD_DN1_FREQ, 0x0000c965}, | ||
254 | {AUD_DCOC_PASS_IN, 0x00000003}, | ||
255 | {AUD_DCOC_0_SRC, 0x0000001a}, | ||
256 | {AUD_DCOC_1_SRC, 0x0000001b}, | ||
257 | {AUD_DCOC1_SHIFT, 0x00000000}, | ||
258 | {AUD_RDSI_SEL, 0x00000009}, | ||
259 | {AUD_RDSQ_SEL, 0x00000009}, | ||
260 | {AUD_RDSI_SHIFT, 0x00000000}, | ||
261 | {AUD_RDSQ_SHIFT, 0x00000000}, | ||
262 | {AUD_POLYPH80SCALEFAC, 0x00000003}, | ||
263 | { /* end of list */ }, | ||
264 | }; | ||
265 | |||
266 | mode |= EN_FMRADIO_EN_RDS; | ||
267 | |||
268 | if (sap) { | ||
269 | dprintk("%s SAP (status: unknown)\n", __func__); | ||
270 | set_audio_start(core, SEL_SAP); | ||
271 | set_audio_registers(core, btsc_sap); | ||
272 | set_audio_finish(core, mode); | ||
273 | } else { | ||
274 | dprintk("%s (status: known-good)\n", __func__); | ||
275 | set_audio_start(core, SEL_BTSC); | ||
276 | set_audio_registers(core, btsc); | ||
277 | set_audio_finish(core, mode); | ||
278 | } | ||
279 | } | ||
280 | |||
281 | static void set_audio_standard_NICAM(struct cx88_core *core, u32 mode) | ||
282 | { | ||
283 | static const struct rlist nicam_l[] = { | ||
284 | {AUD_AFE_12DB_EN, 0x00000001}, | ||
285 | {AUD_RATE_ADJ1, 0x00000060}, | ||
286 | {AUD_RATE_ADJ2, 0x000000F9}, | ||
287 | {AUD_RATE_ADJ3, 0x000001CC}, | ||
288 | {AUD_RATE_ADJ4, 0x000002B3}, | ||
289 | {AUD_RATE_ADJ5, 0x00000726}, | ||
290 | {AUD_DEEMPHDENOM1_R, 0x0000F3D0}, | ||
291 | {AUD_DEEMPHDENOM2_R, 0x00000000}, | ||
292 | {AUD_ERRLOGPERIOD_R, 0x00000064}, | ||
293 | {AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF}, | ||
294 | {AUD_ERRINTRPTTHSHLD2_R, 0x0000001F}, | ||
295 | {AUD_ERRINTRPTTHSHLD3_R, 0x0000000F}, | ||
296 | {AUD_POLYPH80SCALEFAC, 0x00000003}, | ||
297 | {AUD_DMD_RA_DDS, 0x00C00000}, | ||
298 | {AUD_PLL_INT, 0x0000001E}, | ||
299 | {AUD_PLL_DDS, 0x00000000}, | ||
300 | {AUD_PLL_FRAC, 0x0000E542}, | ||
301 | {AUD_START_TIMER, 0x00000000}, | ||
302 | {AUD_DEEMPHNUMER1_R, 0x000353DE}, | ||
303 | {AUD_DEEMPHNUMER2_R, 0x000001B1}, | ||
304 | {AUD_PDF_DDS_CNST_BYTE2, 0x06}, | ||
305 | {AUD_PDF_DDS_CNST_BYTE1, 0x82}, | ||
306 | {AUD_PDF_DDS_CNST_BYTE0, 0x12}, | ||
307 | {AUD_QAM_MODE, 0x05}, | ||
308 | {AUD_PHACC_FREQ_8MSB, 0x34}, | ||
309 | {AUD_PHACC_FREQ_8LSB, 0x4C}, | ||
310 | {AUD_DEEMPHGAIN_R, 0x00006680}, | ||
311 | {AUD_RATE_THRES_DMD, 0x000000C0}, | ||
312 | { /* end of list */ }, | ||
313 | }; | ||
314 | |||
315 | static const struct rlist nicam_bgdki_common[] = { | ||
316 | {AUD_AFE_12DB_EN, 0x00000001}, | ||
317 | {AUD_RATE_ADJ1, 0x00000010}, | ||
318 | {AUD_RATE_ADJ2, 0x00000040}, | ||
319 | {AUD_RATE_ADJ3, 0x00000100}, | ||
320 | {AUD_RATE_ADJ4, 0x00000400}, | ||
321 | {AUD_RATE_ADJ5, 0x00001000}, | ||
322 | {AUD_ERRLOGPERIOD_R, 0x00000fff}, | ||
323 | {AUD_ERRINTRPTTHSHLD1_R, 0x000003ff}, | ||
324 | {AUD_ERRINTRPTTHSHLD2_R, 0x000000ff}, | ||
325 | {AUD_ERRINTRPTTHSHLD3_R, 0x0000003f}, | ||
326 | {AUD_POLYPH80SCALEFAC, 0x00000003}, | ||
327 | {AUD_DEEMPHGAIN_R, 0x000023c2}, | ||
328 | {AUD_DEEMPHNUMER1_R, 0x0002a7bc}, | ||
329 | {AUD_DEEMPHNUMER2_R, 0x0003023e}, | ||
330 | {AUD_DEEMPHDENOM1_R, 0x0000f3d0}, | ||
331 | {AUD_DEEMPHDENOM2_R, 0x00000000}, | ||
332 | {AUD_PDF_DDS_CNST_BYTE2, 0x06}, | ||
333 | {AUD_PDF_DDS_CNST_BYTE1, 0x82}, | ||
334 | {AUD_QAM_MODE, 0x05}, | ||
335 | { /* end of list */ }, | ||
336 | }; | ||
337 | |||
338 | static const struct rlist nicam_i[] = { | ||
339 | {AUD_PDF_DDS_CNST_BYTE0, 0x12}, | ||
340 | {AUD_PHACC_FREQ_8MSB, 0x3a}, | ||
341 | {AUD_PHACC_FREQ_8LSB, 0x93}, | ||
342 | { /* end of list */ }, | ||
343 | }; | ||
344 | |||
345 | static const struct rlist nicam_default[] = { | ||
346 | {AUD_PDF_DDS_CNST_BYTE0, 0x16}, | ||
347 | {AUD_PHACC_FREQ_8MSB, 0x34}, | ||
348 | {AUD_PHACC_FREQ_8LSB, 0x4c}, | ||
349 | { /* end of list */ }, | ||
350 | }; | ||
351 | |||
352 | set_audio_start(core,SEL_NICAM); | ||
353 | switch (core->tvaudio) { | ||
354 | case WW_L: | ||
355 | dprintk("%s SECAM-L NICAM (status: devel)\n", __func__); | ||
356 | set_audio_registers(core, nicam_l); | ||
357 | break; | ||
358 | case WW_I: | ||
359 | dprintk("%s PAL-I NICAM (status: known-good)\n", __func__); | ||
360 | set_audio_registers(core, nicam_bgdki_common); | ||
361 | set_audio_registers(core, nicam_i); | ||
362 | break; | ||
363 | case WW_NONE: | ||
364 | case WW_BTSC: | ||
365 | case WW_BG: | ||
366 | case WW_DK: | ||
367 | case WW_EIAJ: | ||
368 | case WW_I2SPT: | ||
369 | case WW_FM: | ||
370 | case WW_I2SADC: | ||
371 | case WW_M: | ||
372 | dprintk("%s PAL-BGDK NICAM (status: known-good)\n", __func__); | ||
373 | set_audio_registers(core, nicam_bgdki_common); | ||
374 | set_audio_registers(core, nicam_default); | ||
375 | break; | ||
376 | }; | ||
377 | |||
378 | mode |= EN_DMTRX_LR | EN_DMTRX_BYPASS; | ||
379 | set_audio_finish(core, mode); | ||
380 | } | ||
381 | |||
382 | static void set_audio_standard_A2(struct cx88_core *core, u32 mode) | ||
383 | { | ||
384 | static const struct rlist a2_bgdk_common[] = { | ||
385 | {AUD_ERRLOGPERIOD_R, 0x00000064}, | ||
386 | {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff}, | ||
387 | {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f}, | ||
388 | {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f}, | ||
389 | {AUD_PDF_DDS_CNST_BYTE2, 0x06}, | ||
390 | {AUD_PDF_DDS_CNST_BYTE1, 0x82}, | ||
391 | {AUD_PDF_DDS_CNST_BYTE0, 0x12}, | ||
392 | {AUD_QAM_MODE, 0x05}, | ||
393 | {AUD_PHACC_FREQ_8MSB, 0x34}, | ||
394 | {AUD_PHACC_FREQ_8LSB, 0x4c}, | ||
395 | {AUD_RATE_ADJ1, 0x00000100}, | ||
396 | {AUD_RATE_ADJ2, 0x00000200}, | ||
397 | {AUD_RATE_ADJ3, 0x00000300}, | ||
398 | {AUD_RATE_ADJ4, 0x00000400}, | ||
399 | {AUD_RATE_ADJ5, 0x00000500}, | ||
400 | {AUD_THR_FR, 0x00000000}, | ||
401 | {AAGC_HYST, 0x0000001a}, | ||
402 | {AUD_PILOT_BQD_1_K0, 0x0000755b}, | ||
403 | {AUD_PILOT_BQD_1_K1, 0x00551340}, | ||
404 | {AUD_PILOT_BQD_1_K2, 0x006d30be}, | ||
405 | {AUD_PILOT_BQD_1_K3, 0xffd394af}, | ||
406 | {AUD_PILOT_BQD_1_K4, 0x00400000}, | ||
407 | {AUD_PILOT_BQD_2_K0, 0x00040000}, | ||
408 | {AUD_PILOT_BQD_2_K1, 0x002a4841}, | ||
409 | {AUD_PILOT_BQD_2_K2, 0x00400000}, | ||
410 | {AUD_PILOT_BQD_2_K3, 0x00000000}, | ||
411 | {AUD_PILOT_BQD_2_K4, 0x00000000}, | ||
412 | {AUD_MODE_CHG_TIMER, 0x00000040}, | ||
413 | {AUD_AFE_12DB_EN, 0x00000001}, | ||
414 | {AUD_CORDIC_SHIFT_0, 0x00000007}, | ||
415 | {AUD_CORDIC_SHIFT_1, 0x00000007}, | ||
416 | {AUD_DEEMPH0_G0, 0x00000380}, | ||
417 | {AUD_DEEMPH1_G0, 0x00000380}, | ||
418 | {AUD_DCOC_0_SRC, 0x0000001a}, | ||
419 | {AUD_DCOC0_SHIFT, 0x00000000}, | ||
420 | {AUD_DCOC_0_SHIFT_IN0, 0x0000000a}, | ||
421 | {AUD_DCOC_0_SHIFT_IN1, 0x00000008}, | ||
422 | {AUD_DCOC_PASS_IN, 0x00000003}, | ||
423 | {AUD_IIR3_0_SEL, 0x00000021}, | ||
424 | {AUD_DN2_AFC, 0x00000002}, | ||
425 | {AUD_DCOC_1_SRC, 0x0000001b}, | ||
426 | {AUD_DCOC1_SHIFT, 0x00000000}, | ||
427 | {AUD_DCOC_1_SHIFT_IN0, 0x0000000a}, | ||
428 | {AUD_DCOC_1_SHIFT_IN1, 0x00000008}, | ||
429 | {AUD_IIR3_1_SEL, 0x00000023}, | ||
430 | {AUD_RDSI_SEL, 0x00000017}, | ||
431 | {AUD_RDSI_SHIFT, 0x00000000}, | ||
432 | {AUD_RDSQ_SEL, 0x00000017}, | ||
433 | {AUD_RDSQ_SHIFT, 0x00000000}, | ||
434 | {AUD_PLL_INT, 0x0000001e}, | ||
435 | {AUD_PLL_DDS, 0x00000000}, | ||
436 | {AUD_PLL_FRAC, 0x0000e542}, | ||
437 | {AUD_POLYPH80SCALEFAC, 0x00000001}, | ||
438 | {AUD_START_TIMER, 0x00000000}, | ||
439 | { /* end of list */ }, | ||
440 | }; | ||
441 | |||
442 | static const struct rlist a2_bg[] = { | ||
443 | {AUD_DMD_RA_DDS, 0x002a4f2f}, | ||
444 | {AUD_C1_UP_THR, 0x00007000}, | ||
445 | {AUD_C1_LO_THR, 0x00005400}, | ||
446 | {AUD_C2_UP_THR, 0x00005400}, | ||
447 | {AUD_C2_LO_THR, 0x00003000}, | ||
448 | { /* end of list */ }, | ||
449 | }; | ||
450 | |||
451 | static const struct rlist a2_dk[] = { | ||
452 | {AUD_DMD_RA_DDS, 0x002a4f2f}, | ||
453 | {AUD_C1_UP_THR, 0x00007000}, | ||
454 | {AUD_C1_LO_THR, 0x00005400}, | ||
455 | {AUD_C2_UP_THR, 0x00005400}, | ||
456 | {AUD_C2_LO_THR, 0x00003000}, | ||
457 | {AUD_DN0_FREQ, 0x00003a1c}, | ||
458 | {AUD_DN2_FREQ, 0x0000d2e0}, | ||
459 | { /* end of list */ }, | ||
460 | }; | ||
461 | |||
462 | static const struct rlist a1_i[] = { | ||
463 | {AUD_ERRLOGPERIOD_R, 0x00000064}, | ||
464 | {AUD_ERRINTRPTTHSHLD1_R, 0x00000fff}, | ||
465 | {AUD_ERRINTRPTTHSHLD2_R, 0x0000001f}, | ||
466 | {AUD_ERRINTRPTTHSHLD3_R, 0x0000000f}, | ||
467 | {AUD_PDF_DDS_CNST_BYTE2, 0x06}, | ||
468 | {AUD_PDF_DDS_CNST_BYTE1, 0x82}, | ||
469 | {AUD_PDF_DDS_CNST_BYTE0, 0x12}, | ||
470 | {AUD_QAM_MODE, 0x05}, | ||
471 | {AUD_PHACC_FREQ_8MSB, 0x3a}, | ||
472 | {AUD_PHACC_FREQ_8LSB, 0x93}, | ||
473 | {AUD_DMD_RA_DDS, 0x002a4f2f}, | ||
474 | {AUD_PLL_INT, 0x0000001e}, | ||
475 | {AUD_PLL_DDS, 0x00000004}, | ||
476 | {AUD_PLL_FRAC, 0x0000e542}, | ||
477 | {AUD_RATE_ADJ1, 0x00000100}, | ||
478 | {AUD_RATE_ADJ2, 0x00000200}, | ||
479 | {AUD_RATE_ADJ3, 0x00000300}, | ||
480 | {AUD_RATE_ADJ4, 0x00000400}, | ||
481 | {AUD_RATE_ADJ5, 0x00000500}, | ||
482 | {AUD_THR_FR, 0x00000000}, | ||
483 | {AUD_PILOT_BQD_1_K0, 0x0000755b}, | ||
484 | {AUD_PILOT_BQD_1_K1, 0x00551340}, | ||
485 | {AUD_PILOT_BQD_1_K2, 0x006d30be}, | ||
486 | {AUD_PILOT_BQD_1_K3, 0xffd394af}, | ||
487 | {AUD_PILOT_BQD_1_K4, 0x00400000}, | ||
488 | {AUD_PILOT_BQD_2_K0, 0x00040000}, | ||
489 | {AUD_PILOT_BQD_2_K1, 0x002a4841}, | ||
490 | {AUD_PILOT_BQD_2_K2, 0x00400000}, | ||
491 | {AUD_PILOT_BQD_2_K3, 0x00000000}, | ||
492 | {AUD_PILOT_BQD_2_K4, 0x00000000}, | ||
493 | {AUD_MODE_CHG_TIMER, 0x00000060}, | ||
494 | {AUD_AFE_12DB_EN, 0x00000001}, | ||
495 | {AAGC_HYST, 0x0000000a}, | ||
496 | {AUD_CORDIC_SHIFT_0, 0x00000007}, | ||
497 | {AUD_CORDIC_SHIFT_1, 0x00000007}, | ||
498 | {AUD_C1_UP_THR, 0x00007000}, | ||
499 | {AUD_C1_LO_THR, 0x00005400}, | ||
500 | {AUD_C2_UP_THR, 0x00005400}, | ||
501 | {AUD_C2_LO_THR, 0x00003000}, | ||
502 | {AUD_DCOC_0_SRC, 0x0000001a}, | ||
503 | {AUD_DCOC0_SHIFT, 0x00000000}, | ||
504 | {AUD_DCOC_0_SHIFT_IN0, 0x0000000a}, | ||
505 | {AUD_DCOC_0_SHIFT_IN1, 0x00000008}, | ||
506 | {AUD_DCOC_PASS_IN, 0x00000003}, | ||
507 | {AUD_IIR3_0_SEL, 0x00000021}, | ||
508 | {AUD_DN2_AFC, 0x00000002}, | ||
509 | {AUD_DCOC_1_SRC, 0x0000001b}, | ||
510 | {AUD_DCOC1_SHIFT, 0x00000000}, | ||
511 | {AUD_DCOC_1_SHIFT_IN0, 0x0000000a}, | ||
512 | {AUD_DCOC_1_SHIFT_IN1, 0x00000008}, | ||
513 | {AUD_IIR3_1_SEL, 0x00000023}, | ||
514 | {AUD_DN0_FREQ, 0x000035a3}, | ||
515 | {AUD_DN2_FREQ, 0x000029c7}, | ||
516 | {AUD_CRDC0_SRC_SEL, 0x00000511}, | ||
517 | {AUD_IIR1_0_SEL, 0x00000001}, | ||
518 | {AUD_IIR1_1_SEL, 0x00000000}, | ||
519 | {AUD_IIR3_2_SEL, 0x00000003}, | ||
520 | {AUD_IIR3_2_SHIFT, 0x00000000}, | ||
521 | {AUD_IIR3_0_SEL, 0x00000002}, | ||
522 | {AUD_IIR2_0_SEL, 0x00000021}, | ||
523 | {AUD_IIR2_0_SHIFT, 0x00000002}, | ||
524 | {AUD_DEEMPH0_SRC_SEL, 0x0000000b}, | ||
525 | {AUD_DEEMPH1_SRC_SEL, 0x0000000b}, | ||
526 | {AUD_POLYPH80SCALEFAC, 0x00000001}, | ||
527 | {AUD_START_TIMER, 0x00000000}, | ||
528 | { /* end of list */ }, | ||
529 | }; | ||
530 | |||
531 | static const struct rlist am_l[] = { | ||
532 | {AUD_ERRLOGPERIOD_R, 0x00000064}, | ||
533 | {AUD_ERRINTRPTTHSHLD1_R, 0x00000FFF}, | ||
534 | {AUD_ERRINTRPTTHSHLD2_R, 0x0000001F}, | ||
535 | {AUD_ERRINTRPTTHSHLD3_R, 0x0000000F}, | ||
536 | {AUD_PDF_DDS_CNST_BYTE2, 0x48}, | ||
537 | {AUD_PDF_DDS_CNST_BYTE1, 0x3D}, | ||
538 | {AUD_QAM_MODE, 0x00}, | ||
539 | {AUD_PDF_DDS_CNST_BYTE0, 0xf5}, | ||
540 | {AUD_PHACC_FREQ_8MSB, 0x3a}, | ||
541 | {AUD_PHACC_FREQ_8LSB, 0x4a}, | ||
542 | {AUD_DEEMPHGAIN_R, 0x00006680}, | ||
543 | {AUD_DEEMPHNUMER1_R, 0x000353DE}, | ||
544 | {AUD_DEEMPHNUMER2_R, 0x000001B1}, | ||
545 | {AUD_DEEMPHDENOM1_R, 0x0000F3D0}, | ||
546 | {AUD_DEEMPHDENOM2_R, 0x00000000}, | ||
547 | {AUD_FM_MODE_ENABLE, 0x00000007}, | ||
548 | {AUD_POLYPH80SCALEFAC, 0x00000003}, | ||
549 | {AUD_AFE_12DB_EN, 0x00000001}, | ||
550 | {AAGC_GAIN, 0x00000000}, | ||
551 | {AAGC_HYST, 0x00000018}, | ||
552 | {AAGC_DEF, 0x00000020}, | ||
553 | {AUD_DN0_FREQ, 0x00000000}, | ||
554 | {AUD_POLY0_DDS_CONSTANT, 0x000E4DB2}, | ||
555 | {AUD_DCOC_0_SRC, 0x00000021}, | ||
556 | {AUD_IIR1_0_SEL, 0x00000000}, | ||
557 | {AUD_IIR1_0_SHIFT, 0x00000007}, | ||
558 | {AUD_IIR1_1_SEL, 0x00000002}, | ||
559 | {AUD_IIR1_1_SHIFT, 0x00000000}, | ||
560 | {AUD_DCOC_1_SRC, 0x00000003}, | ||
561 | {AUD_DCOC1_SHIFT, 0x00000000}, | ||
562 | {AUD_DCOC_PASS_IN, 0x00000000}, | ||
563 | {AUD_IIR1_2_SEL, 0x00000023}, | ||
564 | {AUD_IIR1_2_SHIFT, 0x00000000}, | ||
565 | {AUD_IIR1_3_SEL, 0x00000004}, | ||
566 | {AUD_IIR1_3_SHIFT, 0x00000007}, | ||
567 | {AUD_IIR1_4_SEL, 0x00000005}, | ||
568 | {AUD_IIR1_4_SHIFT, 0x00000007}, | ||
569 | {AUD_IIR3_0_SEL, 0x00000007}, | ||
570 | {AUD_IIR3_0_SHIFT, 0x00000000}, | ||
571 | {AUD_DEEMPH0_SRC_SEL, 0x00000011}, | ||
572 | {AUD_DEEMPH0_SHIFT, 0x00000000}, | ||
573 | {AUD_DEEMPH0_G0, 0x00007000}, | ||
574 | {AUD_DEEMPH0_A0, 0x00000000}, | ||
575 | {AUD_DEEMPH0_B0, 0x00000000}, | ||
576 | {AUD_DEEMPH0_A1, 0x00000000}, | ||
577 | {AUD_DEEMPH0_B1, 0x00000000}, | ||
578 | {AUD_DEEMPH1_SRC_SEL, 0x00000011}, | ||
579 | {AUD_DEEMPH1_SHIFT, 0x00000000}, | ||
580 | {AUD_DEEMPH1_G0, 0x00007000}, | ||
581 | {AUD_DEEMPH1_A0, 0x00000000}, | ||
582 | {AUD_DEEMPH1_B0, 0x00000000}, | ||
583 | {AUD_DEEMPH1_A1, 0x00000000}, | ||
584 | {AUD_DEEMPH1_B1, 0x00000000}, | ||
585 | {AUD_OUT0_SEL, 0x0000003F}, | ||
586 | {AUD_OUT1_SEL, 0x0000003F}, | ||
587 | {AUD_DMD_RA_DDS, 0x00F5C285}, | ||
588 | {AUD_PLL_INT, 0x0000001E}, | ||
589 | {AUD_PLL_DDS, 0x00000000}, | ||
590 | {AUD_PLL_FRAC, 0x0000E542}, | ||
591 | {AUD_RATE_ADJ1, 0x00000100}, | ||
592 | {AUD_RATE_ADJ2, 0x00000200}, | ||
593 | {AUD_RATE_ADJ3, 0x00000300}, | ||
594 | {AUD_RATE_ADJ4, 0x00000400}, | ||
595 | {AUD_RATE_ADJ5, 0x00000500}, | ||
596 | {AUD_RATE_THRES_DMD, 0x000000C0}, | ||
597 | { /* end of list */ }, | ||
598 | }; | ||
599 | |||
600 | static const struct rlist a2_deemph50[] = { | ||
601 | {AUD_DEEMPH0_G0, 0x00000380}, | ||
602 | {AUD_DEEMPH1_G0, 0x00000380}, | ||
603 | {AUD_DEEMPHGAIN_R, 0x000011e1}, | ||
604 | {AUD_DEEMPHNUMER1_R, 0x0002a7bc}, | ||
605 | {AUD_DEEMPHNUMER2_R, 0x0003023c}, | ||
606 | { /* end of list */ }, | ||
607 | }; | ||
608 | |||
609 | set_audio_start(core, SEL_A2); | ||
610 | switch (core->tvaudio) { | ||
611 | case WW_BG: | ||
612 | dprintk("%s PAL-BG A1/2 (status: known-good)\n", __func__); | ||
613 | set_audio_registers(core, a2_bgdk_common); | ||
614 | set_audio_registers(core, a2_bg); | ||
615 | set_audio_registers(core, a2_deemph50); | ||
616 | break; | ||
617 | case WW_DK: | ||
618 | dprintk("%s PAL-DK A1/2 (status: known-good)\n", __func__); | ||
619 | set_audio_registers(core, a2_bgdk_common); | ||
620 | set_audio_registers(core, a2_dk); | ||
621 | set_audio_registers(core, a2_deemph50); | ||
622 | break; | ||
623 | case WW_I: | ||
624 | dprintk("%s PAL-I A1 (status: known-good)\n", __func__); | ||
625 | set_audio_registers(core, a1_i); | ||
626 | set_audio_registers(core, a2_deemph50); | ||
627 | break; | ||
628 | case WW_L: | ||
629 | dprintk("%s AM-L (status: devel)\n", __func__); | ||
630 | set_audio_registers(core, am_l); | ||
631 | break; | ||
632 | case WW_NONE: | ||
633 | case WW_BTSC: | ||
634 | case WW_EIAJ: | ||
635 | case WW_I2SPT: | ||
636 | case WW_FM: | ||
637 | case WW_I2SADC: | ||
638 | case WW_M: | ||
639 | dprintk("%s Warning: wrong value\n", __func__); | ||
640 | return; | ||
641 | break; | ||
642 | }; | ||
643 | |||
644 | mode |= EN_FMRADIO_EN_RDS | EN_DMTRX_SUMDIFF; | ||
645 | set_audio_finish(core, mode); | ||
646 | } | ||
647 | |||
648 | static void set_audio_standard_EIAJ(struct cx88_core *core) | ||
649 | { | ||
650 | static const struct rlist eiaj[] = { | ||
651 | /* TODO: eiaj register settings are not there yet ... */ | ||
652 | |||
653 | { /* end of list */ }, | ||
654 | }; | ||
655 | dprintk("%s (status: unknown)\n", __func__); | ||
656 | |||
657 | set_audio_start(core, SEL_EIAJ); | ||
658 | set_audio_registers(core, eiaj); | ||
659 | set_audio_finish(core, EN_EIAJ_AUTO_STEREO); | ||
660 | } | ||
661 | |||
662 | static void set_audio_standard_FM(struct cx88_core *core, | ||
663 | enum cx88_deemph_type deemph) | ||
664 | { | ||
665 | static const struct rlist fm_deemph_50[] = { | ||
666 | {AUD_DEEMPH0_G0, 0x0C45}, | ||
667 | {AUD_DEEMPH0_A0, 0x6262}, | ||
668 | {AUD_DEEMPH0_B0, 0x1C29}, | ||
669 | {AUD_DEEMPH0_A1, 0x3FC66}, | ||
670 | {AUD_DEEMPH0_B1, 0x399A}, | ||
671 | |||
672 | {AUD_DEEMPH1_G0, 0x0D80}, | ||
673 | {AUD_DEEMPH1_A0, 0x6262}, | ||
674 | {AUD_DEEMPH1_B0, 0x1C29}, | ||
675 | {AUD_DEEMPH1_A1, 0x3FC66}, | ||
676 | {AUD_DEEMPH1_B1, 0x399A}, | ||
677 | |||
678 | {AUD_POLYPH80SCALEFAC, 0x0003}, | ||
679 | { /* end of list */ }, | ||
680 | }; | ||
681 | static const struct rlist fm_deemph_75[] = { | ||
682 | {AUD_DEEMPH0_G0, 0x091B}, | ||
683 | {AUD_DEEMPH0_A0, 0x6B68}, | ||
684 | {AUD_DEEMPH0_B0, 0x11EC}, | ||
685 | {AUD_DEEMPH0_A1, 0x3FC66}, | ||
686 | {AUD_DEEMPH0_B1, 0x399A}, | ||
687 | |||
688 | {AUD_DEEMPH1_G0, 0x0AA0}, | ||
689 | {AUD_DEEMPH1_A0, 0x6B68}, | ||
690 | {AUD_DEEMPH1_B0, 0x11EC}, | ||
691 | {AUD_DEEMPH1_A1, 0x3FC66}, | ||
692 | {AUD_DEEMPH1_B1, 0x399A}, | ||
693 | |||
694 | {AUD_POLYPH80SCALEFAC, 0x0003}, | ||
695 | { /* end of list */ }, | ||
696 | }; | ||
697 | |||
698 | /* It is enough to leave default values? */ | ||
699 | /* No, it's not! The deemphasis registers are reset to the 75us | ||
700 | * values by default. Analyzing the spectrum of the decoded audio | ||
701 | * reveals that "no deemphasis" is the same as 75 us, while the 50 us | ||
702 | * setting results in less deemphasis. */ | ||
703 | static const struct rlist fm_no_deemph[] = { | ||
704 | |||
705 | {AUD_POLYPH80SCALEFAC, 0x0003}, | ||
706 | { /* end of list */ }, | ||
707 | }; | ||
708 | |||
709 | dprintk("%s (status: unknown)\n", __func__); | ||
710 | set_audio_start(core, SEL_FMRADIO); | ||
711 | |||
712 | switch (deemph) { | ||
713 | default: | ||
714 | case FM_NO_DEEMPH: | ||
715 | set_audio_registers(core, fm_no_deemph); | ||
716 | break; | ||
717 | |||
718 | case FM_DEEMPH_50: | ||
719 | set_audio_registers(core, fm_deemph_50); | ||
720 | break; | ||
721 | |||
722 | case FM_DEEMPH_75: | ||
723 | set_audio_registers(core, fm_deemph_75); | ||
724 | break; | ||
725 | } | ||
726 | |||
727 | set_audio_finish(core, EN_FMRADIO_AUTO_STEREO); | ||
728 | } | ||
729 | |||
730 | /* ----------------------------------------------------------- */ | ||
731 | |||
732 | static int cx88_detect_nicam(struct cx88_core *core) | ||
733 | { | ||
734 | int i, j = 0; | ||
735 | |||
736 | dprintk("start nicam autodetect.\n"); | ||
737 | |||
738 | for (i = 0; i < 6; i++) { | ||
739 | /* if bit1=1 then nicam is detected */ | ||
740 | j += ((cx_read(AUD_NICAM_STATUS2) & 0x02) >> 1); | ||
741 | |||
742 | if (j == 1) { | ||
743 | dprintk("nicam is detected.\n"); | ||
744 | return 1; | ||
745 | } | ||
746 | |||
747 | /* wait a little bit for next reading status */ | ||
748 | msleep(10); | ||
749 | } | ||
750 | |||
751 | dprintk("nicam is not detected.\n"); | ||
752 | return 0; | ||
753 | } | ||
754 | |||
755 | void cx88_set_tvaudio(struct cx88_core *core) | ||
756 | { | ||
757 | switch (core->tvaudio) { | ||
758 | case WW_BTSC: | ||
759 | set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO); | ||
760 | break; | ||
761 | case WW_BG: | ||
762 | case WW_DK: | ||
763 | case WW_M: | ||
764 | case WW_I: | ||
765 | case WW_L: | ||
766 | /* prepare all dsp registers */ | ||
767 | set_audio_standard_A2(core, EN_A2_FORCE_MONO1); | ||
768 | |||
769 | /* set nicam mode - otherwise | ||
770 | AUD_NICAM_STATUS2 contains wrong values */ | ||
771 | set_audio_standard_NICAM(core, EN_NICAM_AUTO_STEREO); | ||
772 | if (0 == cx88_detect_nicam(core)) { | ||
773 | /* fall back to fm / am mono */ | ||
774 | set_audio_standard_A2(core, EN_A2_FORCE_MONO1); | ||
775 | core->audiomode_current = V4L2_TUNER_MODE_MONO; | ||
776 | core->use_nicam = 0; | ||
777 | } else { | ||
778 | core->use_nicam = 1; | ||
779 | } | ||
780 | break; | ||
781 | case WW_EIAJ: | ||
782 | set_audio_standard_EIAJ(core); | ||
783 | break; | ||
784 | case WW_FM: | ||
785 | set_audio_standard_FM(core, radio_deemphasis); | ||
786 | break; | ||
787 | case WW_I2SADC: | ||
788 | set_audio_start(core, 0x01); | ||
789 | /* | ||
790 | * Slave/Philips/Autobaud | ||
791 | * NB on Nova-S bit1 NPhilipsSony appears to be inverted: | ||
792 | * 0= Sony, 1=Philips | ||
793 | */ | ||
794 | cx_write(AUD_I2SINPUTCNTL, core->board.i2sinputcntl); | ||
795 | /* Switch to "I2S ADC mode" */ | ||
796 | cx_write(AUD_I2SCNTL, 0x1); | ||
797 | set_audio_finish(core, EN_I2SIN_ENABLE); | ||
798 | break; | ||
799 | case WW_NONE: | ||
800 | case WW_I2SPT: | ||
801 | printk("%s/0: unknown tv audio mode [%d]\n", | ||
802 | core->name, core->tvaudio); | ||
803 | break; | ||
804 | } | ||
805 | return; | ||
806 | } | ||
807 | |||
808 | void cx88_newstation(struct cx88_core *core) | ||
809 | { | ||
810 | core->audiomode_manual = UNSET; | ||
811 | core->last_change = jiffies; | ||
812 | } | ||
813 | |||
814 | void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t) | ||
815 | { | ||
816 | static const char * const m[] = { "stereo", "dual mono", "mono", "sap" }; | ||
817 | static const char * const p[] = { "no pilot", "pilot c1", "pilot c2", "?" }; | ||
818 | u32 reg, mode, pilot; | ||
819 | |||
820 | reg = cx_read(AUD_STATUS); | ||
821 | mode = reg & 0x03; | ||
822 | pilot = (reg >> 2) & 0x03; | ||
823 | |||
824 | if (core->astat != reg) | ||
825 | dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n", | ||
826 | reg, m[mode], p[pilot], | ||
827 | aud_ctl_names[cx_read(AUD_CTL) & 63]); | ||
828 | core->astat = reg; | ||
829 | |||
830 | t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP | | ||
831 | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; | ||
832 | t->rxsubchans = UNSET; | ||
833 | t->audmode = V4L2_TUNER_MODE_MONO; | ||
834 | |||
835 | switch (mode) { | ||
836 | case 0: | ||
837 | t->audmode = V4L2_TUNER_MODE_STEREO; | ||
838 | break; | ||
839 | case 1: | ||
840 | t->audmode = V4L2_TUNER_MODE_LANG2; | ||
841 | break; | ||
842 | case 2: | ||
843 | t->audmode = V4L2_TUNER_MODE_MONO; | ||
844 | break; | ||
845 | case 3: | ||
846 | t->audmode = V4L2_TUNER_MODE_SAP; | ||
847 | break; | ||
848 | } | ||
849 | |||
850 | switch (core->tvaudio) { | ||
851 | case WW_BTSC: | ||
852 | case WW_BG: | ||
853 | case WW_DK: | ||
854 | case WW_M: | ||
855 | case WW_EIAJ: | ||
856 | if (!core->use_nicam) { | ||
857 | t->rxsubchans = cx88_dsp_detect_stereo_sap(core); | ||
858 | break; | ||
859 | } | ||
860 | break; | ||
861 | case WW_NONE: | ||
862 | case WW_I: | ||
863 | case WW_L: | ||
864 | case WW_I2SPT: | ||
865 | case WW_FM: | ||
866 | case WW_I2SADC: | ||
867 | /* nothing */ | ||
868 | break; | ||
869 | } | ||
870 | |||
871 | /* If software stereo detection is not supported... */ | ||
872 | if (UNSET == t->rxsubchans) { | ||
873 | t->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
874 | /* If the hardware itself detected stereo, also return | ||
875 | stereo as an available subchannel */ | ||
876 | if (V4L2_TUNER_MODE_STEREO == t->audmode) | ||
877 | t->rxsubchans |= V4L2_TUNER_SUB_STEREO; | ||
878 | } | ||
879 | return; | ||
880 | } | ||
881 | |||
882 | void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual) | ||
883 | { | ||
884 | u32 ctl = UNSET; | ||
885 | u32 mask = UNSET; | ||
886 | |||
887 | if (manual) { | ||
888 | core->audiomode_manual = mode; | ||
889 | } else { | ||
890 | if (UNSET != core->audiomode_manual) | ||
891 | return; | ||
892 | } | ||
893 | core->audiomode_current = mode; | ||
894 | |||
895 | switch (core->tvaudio) { | ||
896 | case WW_BTSC: | ||
897 | switch (mode) { | ||
898 | case V4L2_TUNER_MODE_MONO: | ||
899 | set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_MONO); | ||
900 | break; | ||
901 | case V4L2_TUNER_MODE_LANG1: | ||
902 | set_audio_standard_BTSC(core, 0, EN_BTSC_AUTO_STEREO); | ||
903 | break; | ||
904 | case V4L2_TUNER_MODE_LANG2: | ||
905 | set_audio_standard_BTSC(core, 1, EN_BTSC_FORCE_SAP); | ||
906 | break; | ||
907 | case V4L2_TUNER_MODE_STEREO: | ||
908 | case V4L2_TUNER_MODE_LANG1_LANG2: | ||
909 | set_audio_standard_BTSC(core, 0, EN_BTSC_FORCE_STEREO); | ||
910 | break; | ||
911 | } | ||
912 | break; | ||
913 | case WW_BG: | ||
914 | case WW_DK: | ||
915 | case WW_M: | ||
916 | case WW_I: | ||
917 | case WW_L: | ||
918 | if (1 == core->use_nicam) { | ||
919 | switch (mode) { | ||
920 | case V4L2_TUNER_MODE_MONO: | ||
921 | case V4L2_TUNER_MODE_LANG1: | ||
922 | set_audio_standard_NICAM(core, | ||
923 | EN_NICAM_FORCE_MONO1); | ||
924 | break; | ||
925 | case V4L2_TUNER_MODE_LANG2: | ||
926 | set_audio_standard_NICAM(core, | ||
927 | EN_NICAM_FORCE_MONO2); | ||
928 | break; | ||
929 | case V4L2_TUNER_MODE_STEREO: | ||
930 | case V4L2_TUNER_MODE_LANG1_LANG2: | ||
931 | set_audio_standard_NICAM(core, | ||
932 | EN_NICAM_FORCE_STEREO); | ||
933 | break; | ||
934 | } | ||
935 | } else { | ||
936 | if ((core->tvaudio == WW_I) || (core->tvaudio == WW_L)) { | ||
937 | /* fall back to fm / am mono */ | ||
938 | set_audio_standard_A2(core, EN_A2_FORCE_MONO1); | ||
939 | } else { | ||
940 | /* TODO: Add A2 autodection */ | ||
941 | mask = 0x3f; | ||
942 | switch (mode) { | ||
943 | case V4L2_TUNER_MODE_MONO: | ||
944 | case V4L2_TUNER_MODE_LANG1: | ||
945 | ctl = EN_A2_FORCE_MONO1; | ||
946 | break; | ||
947 | case V4L2_TUNER_MODE_LANG2: | ||
948 | ctl = EN_A2_FORCE_MONO2; | ||
949 | break; | ||
950 | case V4L2_TUNER_MODE_STEREO: | ||
951 | case V4L2_TUNER_MODE_LANG1_LANG2: | ||
952 | ctl = EN_A2_FORCE_STEREO; | ||
953 | break; | ||
954 | } | ||
955 | } | ||
956 | } | ||
957 | break; | ||
958 | case WW_FM: | ||
959 | switch (mode) { | ||
960 | case V4L2_TUNER_MODE_MONO: | ||
961 | ctl = EN_FMRADIO_FORCE_MONO; | ||
962 | mask = 0x3f; | ||
963 | break; | ||
964 | case V4L2_TUNER_MODE_STEREO: | ||
965 | ctl = EN_FMRADIO_AUTO_STEREO; | ||
966 | mask = 0x3f; | ||
967 | break; | ||
968 | } | ||
969 | break; | ||
970 | case WW_I2SADC: | ||
971 | case WW_NONE: | ||
972 | case WW_EIAJ: | ||
973 | case WW_I2SPT: | ||
974 | /* DO NOTHING */ | ||
975 | break; | ||
976 | } | ||
977 | |||
978 | if (UNSET != ctl) { | ||
979 | dprintk("cx88_set_stereo: mask 0x%x, ctl 0x%x " | ||
980 | "[status=0x%x,ctl=0x%x,vol=0x%x]\n", | ||
981 | mask, ctl, cx_read(AUD_STATUS), | ||
982 | cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL)); | ||
983 | cx_andor(AUD_CTL, mask, ctl); | ||
984 | } | ||
985 | return; | ||
986 | } | ||
987 | |||
988 | int cx88_audio_thread(void *data) | ||
989 | { | ||
990 | struct cx88_core *core = data; | ||
991 | struct v4l2_tuner t; | ||
992 | u32 mode = 0; | ||
993 | |||
994 | dprintk("cx88: tvaudio thread started\n"); | ||
995 | set_freezable(); | ||
996 | for (;;) { | ||
997 | msleep_interruptible(1000); | ||
998 | if (kthread_should_stop()) | ||
999 | break; | ||
1000 | try_to_freeze(); | ||
1001 | |||
1002 | switch (core->tvaudio) { | ||
1003 | case WW_BG: | ||
1004 | case WW_DK: | ||
1005 | case WW_M: | ||
1006 | case WW_I: | ||
1007 | case WW_L: | ||
1008 | if (core->use_nicam) | ||
1009 | goto hw_autodetect; | ||
1010 | |||
1011 | /* just monitor the audio status for now ... */ | ||
1012 | memset(&t, 0, sizeof(t)); | ||
1013 | cx88_get_stereo(core, &t); | ||
1014 | |||
1015 | if (UNSET != core->audiomode_manual) | ||
1016 | /* manually set, don't do anything. */ | ||
1017 | continue; | ||
1018 | |||
1019 | /* monitor signal and set stereo if available */ | ||
1020 | if (t.rxsubchans & V4L2_TUNER_SUB_STEREO) | ||
1021 | mode = V4L2_TUNER_MODE_STEREO; | ||
1022 | else | ||
1023 | mode = V4L2_TUNER_MODE_MONO; | ||
1024 | if (mode == core->audiomode_current) | ||
1025 | continue; | ||
1026 | /* automatically switch to best available mode */ | ||
1027 | cx88_set_stereo(core, mode, 0); | ||
1028 | break; | ||
1029 | case WW_NONE: | ||
1030 | case WW_BTSC: | ||
1031 | case WW_EIAJ: | ||
1032 | case WW_I2SPT: | ||
1033 | case WW_FM: | ||
1034 | case WW_I2SADC: | ||
1035 | hw_autodetect: | ||
1036 | /* stereo autodetection is supported by hardware so | ||
1037 | we don't need to do it manually. Do nothing. */ | ||
1038 | break; | ||
1039 | } | ||
1040 | } | ||
1041 | |||
1042 | dprintk("cx88: tvaudio thread exiting\n"); | ||
1043 | return 0; | ||
1044 | } | ||
1045 | |||
1046 | /* ----------------------------------------------------------- */ | ||
1047 | |||
1048 | EXPORT_SYMBOL(cx88_set_tvaudio); | ||
1049 | EXPORT_SYMBOL(cx88_newstation); | ||
1050 | EXPORT_SYMBOL(cx88_set_stereo); | ||
1051 | EXPORT_SYMBOL(cx88_get_stereo); | ||
1052 | EXPORT_SYMBOL(cx88_audio_thread); | ||
1053 | |||
1054 | /* | ||
1055 | * Local variables: | ||
1056 | * c-basic-offset: 8 | ||
1057 | * End: | ||
1058 | * 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 | ||
1059 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-vbi.c b/drivers/media/video/cx88/cx88-vbi.c new file mode 100644 index 00000000000..f8f8389c036 --- /dev/null +++ b/drivers/media/video/cx88/cx88-vbi.c | |||
@@ -0,0 +1,245 @@ | |||
1 | /* | ||
2 | */ | ||
3 | #include <linux/kernel.h> | ||
4 | #include <linux/module.h> | ||
5 | #include <linux/init.h> | ||
6 | |||
7 | #include "cx88.h" | ||
8 | |||
9 | static unsigned int vbibufs = 4; | ||
10 | module_param(vbibufs,int,0644); | ||
11 | MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32"); | ||
12 | |||
13 | static unsigned int vbi_debug; | ||
14 | module_param(vbi_debug,int,0644); | ||
15 | MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]"); | ||
16 | |||
17 | #define dprintk(level,fmt, arg...) if (vbi_debug >= level) \ | ||
18 | printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) | ||
19 | |||
20 | /* ------------------------------------------------------------------ */ | ||
21 | |||
22 | int cx8800_vbi_fmt (struct file *file, void *priv, | ||
23 | struct v4l2_format *f) | ||
24 | { | ||
25 | struct cx8800_fh *fh = priv; | ||
26 | struct cx8800_dev *dev = fh->dev; | ||
27 | |||
28 | f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; | ||
29 | f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; | ||
30 | f->fmt.vbi.offset = 244; | ||
31 | f->fmt.vbi.count[0] = VBI_LINE_COUNT; | ||
32 | f->fmt.vbi.count[1] = VBI_LINE_COUNT; | ||
33 | |||
34 | if (dev->core->tvnorm & V4L2_STD_525_60) { | ||
35 | /* ntsc */ | ||
36 | f->fmt.vbi.sampling_rate = 28636363; | ||
37 | f->fmt.vbi.start[0] = 10; | ||
38 | f->fmt.vbi.start[1] = 273; | ||
39 | |||
40 | } else if (dev->core->tvnorm & V4L2_STD_625_50) { | ||
41 | /* pal */ | ||
42 | f->fmt.vbi.sampling_rate = 35468950; | ||
43 | f->fmt.vbi.start[0] = 7 -1; | ||
44 | f->fmt.vbi.start[1] = 319 -1; | ||
45 | } | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static int cx8800_start_vbi_dma(struct cx8800_dev *dev, | ||
50 | struct cx88_dmaqueue *q, | ||
51 | struct cx88_buffer *buf) | ||
52 | { | ||
53 | struct cx88_core *core = dev->core; | ||
54 | |||
55 | /* setup fifo + format */ | ||
56 | cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24], | ||
57 | buf->vb.width, buf->risc.dma); | ||
58 | |||
59 | cx_write(MO_VBOS_CONTROL, ( (1 << 18) | // comb filter delay fixup | ||
60 | (1 << 15) | // enable vbi capture | ||
61 | (1 << 11) )); | ||
62 | |||
63 | /* reset counter */ | ||
64 | cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET); | ||
65 | q->count = 1; | ||
66 | |||
67 | /* enable irqs */ | ||
68 | cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT); | ||
69 | cx_set(MO_VID_INTMSK, 0x0f0088); | ||
70 | |||
71 | /* enable capture */ | ||
72 | cx_set(VID_CAPTURE_CONTROL,0x18); | ||
73 | |||
74 | /* start dma */ | ||
75 | cx_set(MO_DEV_CNTRL2, (1<<5)); | ||
76 | cx_set(MO_VID_DMACNTRL, 0x88); | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | int cx8800_stop_vbi_dma(struct cx8800_dev *dev) | ||
82 | { | ||
83 | struct cx88_core *core = dev->core; | ||
84 | |||
85 | /* stop dma */ | ||
86 | cx_clear(MO_VID_DMACNTRL, 0x88); | ||
87 | |||
88 | /* disable capture */ | ||
89 | cx_clear(VID_CAPTURE_CONTROL,0x18); | ||
90 | |||
91 | /* disable irqs */ | ||
92 | cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT); | ||
93 | cx_clear(MO_VID_INTMSK, 0x0f0088); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | int cx8800_restart_vbi_queue(struct cx8800_dev *dev, | ||
98 | struct cx88_dmaqueue *q) | ||
99 | { | ||
100 | struct cx88_buffer *buf; | ||
101 | |||
102 | if (list_empty(&q->active)) | ||
103 | return 0; | ||
104 | |||
105 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
106 | dprintk(2,"restart_queue [%p/%d]: restart dma\n", | ||
107 | buf, buf->vb.i); | ||
108 | cx8800_start_vbi_dma(dev, q, buf); | ||
109 | list_for_each_entry(buf, &q->active, vb.queue) | ||
110 | buf->count = q->count++; | ||
111 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | void cx8800_vbi_timeout(unsigned long data) | ||
116 | { | ||
117 | struct cx8800_dev *dev = (struct cx8800_dev*)data; | ||
118 | struct cx88_core *core = dev->core; | ||
119 | struct cx88_dmaqueue *q = &dev->vbiq; | ||
120 | struct cx88_buffer *buf; | ||
121 | unsigned long flags; | ||
122 | |||
123 | cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH24]); | ||
124 | |||
125 | cx_clear(MO_VID_DMACNTRL, 0x88); | ||
126 | cx_clear(VID_CAPTURE_CONTROL, 0x18); | ||
127 | |||
128 | spin_lock_irqsave(&dev->slock,flags); | ||
129 | while (!list_empty(&q->active)) { | ||
130 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
131 | list_del(&buf->vb.queue); | ||
132 | buf->vb.state = VIDEOBUF_ERROR; | ||
133 | wake_up(&buf->vb.done); | ||
134 | printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name, | ||
135 | buf, buf->vb.i, (unsigned long)buf->risc.dma); | ||
136 | } | ||
137 | cx8800_restart_vbi_queue(dev,q); | ||
138 | spin_unlock_irqrestore(&dev->slock,flags); | ||
139 | } | ||
140 | |||
141 | /* ------------------------------------------------------------------ */ | ||
142 | |||
143 | static int | ||
144 | vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) | ||
145 | { | ||
146 | *size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; | ||
147 | if (0 == *count) | ||
148 | *count = vbibufs; | ||
149 | if (*count < 2) | ||
150 | *count = 2; | ||
151 | if (*count > 32) | ||
152 | *count = 32; | ||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static int | ||
157 | vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, | ||
158 | enum v4l2_field field) | ||
159 | { | ||
160 | struct cx8800_fh *fh = q->priv_data; | ||
161 | struct cx8800_dev *dev = fh->dev; | ||
162 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
163 | unsigned int size; | ||
164 | int rc; | ||
165 | |||
166 | size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2; | ||
167 | if (0 != buf->vb.baddr && buf->vb.bsize < size) | ||
168 | return -EINVAL; | ||
169 | |||
170 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { | ||
171 | struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); | ||
172 | buf->vb.width = VBI_LINE_LENGTH; | ||
173 | buf->vb.height = VBI_LINE_COUNT; | ||
174 | buf->vb.size = size; | ||
175 | buf->vb.field = V4L2_FIELD_SEQ_TB; | ||
176 | |||
177 | if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) | ||
178 | goto fail; | ||
179 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
180 | dma->sglist, | ||
181 | 0, buf->vb.width * buf->vb.height, | ||
182 | buf->vb.width, 0, | ||
183 | buf->vb.height); | ||
184 | } | ||
185 | buf->vb.state = VIDEOBUF_PREPARED; | ||
186 | return 0; | ||
187 | |||
188 | fail: | ||
189 | cx88_free_buffer(q,buf); | ||
190 | return rc; | ||
191 | } | ||
192 | |||
193 | static void | ||
194 | vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) | ||
195 | { | ||
196 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
197 | struct cx88_buffer *prev; | ||
198 | struct cx8800_fh *fh = vq->priv_data; | ||
199 | struct cx8800_dev *dev = fh->dev; | ||
200 | struct cx88_dmaqueue *q = &dev->vbiq; | ||
201 | |||
202 | /* add jump to stopper */ | ||
203 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); | ||
204 | buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); | ||
205 | |||
206 | if (list_empty(&q->active)) { | ||
207 | list_add_tail(&buf->vb.queue,&q->active); | ||
208 | cx8800_start_vbi_dma(dev, q, buf); | ||
209 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
210 | buf->count = q->count++; | ||
211 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
212 | dprintk(2,"[%p/%d] vbi_queue - first active\n", | ||
213 | buf, buf->vb.i); | ||
214 | |||
215 | } else { | ||
216 | prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); | ||
217 | list_add_tail(&buf->vb.queue,&q->active); | ||
218 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
219 | buf->count = q->count++; | ||
220 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
221 | dprintk(2,"[%p/%d] buffer_queue - append to active\n", | ||
222 | buf, buf->vb.i); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
227 | { | ||
228 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
229 | |||
230 | cx88_free_buffer(q,buf); | ||
231 | } | ||
232 | |||
233 | const struct videobuf_queue_ops cx8800_vbi_qops = { | ||
234 | .buf_setup = vbi_setup, | ||
235 | .buf_prepare = vbi_prepare, | ||
236 | .buf_queue = vbi_queue, | ||
237 | .buf_release = vbi_release, | ||
238 | }; | ||
239 | |||
240 | /* ------------------------------------------------------------------ */ | ||
241 | /* | ||
242 | * Local variables: | ||
243 | * c-basic-offset: 8 | ||
244 | * End: | ||
245 | */ | ||
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c new file mode 100644 index 00000000000..60d28fdd779 --- /dev/null +++ b/drivers/media/video/cx88/cx88-video.c | |||
@@ -0,0 +1,2194 @@ | |||
1 | /* | ||
2 | * | ||
3 | * device driver for Conexant 2388x based TV cards | ||
4 | * video4linux video interface | ||
5 | * | ||
6 | * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] | ||
7 | * | ||
8 | * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> | ||
9 | * - Multituner support | ||
10 | * - video_ioctl2 conversion | ||
11 | * - PAL/M fixes | ||
12 | * | ||
13 | * 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> | ||
31 | #include <linux/kmod.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/interrupt.h> | ||
35 | #include <linux/dma-mapping.h> | ||
36 | #include <linux/delay.h> | ||
37 | #include <linux/kthread.h> | ||
38 | #include <asm/div64.h> | ||
39 | |||
40 | #include "cx88.h" | ||
41 | #include <media/v4l2-common.h> | ||
42 | #include <media/v4l2-ioctl.h> | ||
43 | #include <media/wm8775.h> | ||
44 | |||
45 | MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); | ||
46 | MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); | ||
47 | MODULE_LICENSE("GPL"); | ||
48 | MODULE_VERSION(CX88_VERSION); | ||
49 | |||
50 | /* ------------------------------------------------------------------ */ | ||
51 | |||
52 | static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
53 | static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
54 | static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; | ||
55 | |||
56 | module_param_array(video_nr, int, NULL, 0444); | ||
57 | module_param_array(vbi_nr, int, NULL, 0444); | ||
58 | module_param_array(radio_nr, int, NULL, 0444); | ||
59 | |||
60 | MODULE_PARM_DESC(video_nr,"video device numbers"); | ||
61 | MODULE_PARM_DESC(vbi_nr,"vbi device numbers"); | ||
62 | MODULE_PARM_DESC(radio_nr,"radio device numbers"); | ||
63 | |||
64 | static unsigned int video_debug; | ||
65 | module_param(video_debug,int,0644); | ||
66 | MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); | ||
67 | |||
68 | static unsigned int irq_debug; | ||
69 | module_param(irq_debug,int,0644); | ||
70 | MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]"); | ||
71 | |||
72 | static unsigned int vid_limit = 16; | ||
73 | module_param(vid_limit,int,0644); | ||
74 | MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes"); | ||
75 | |||
76 | #define dprintk(level,fmt, arg...) if (video_debug >= level) \ | ||
77 | printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg) | ||
78 | |||
79 | /* ------------------------------------------------------------------- */ | ||
80 | /* static data */ | ||
81 | |||
82 | static const struct cx8800_fmt formats[] = { | ||
83 | { | ||
84 | .name = "8 bpp, gray", | ||
85 | .fourcc = V4L2_PIX_FMT_GREY, | ||
86 | .cxformat = ColorFormatY8, | ||
87 | .depth = 8, | ||
88 | .flags = FORMAT_FLAGS_PACKED, | ||
89 | },{ | ||
90 | .name = "15 bpp RGB, le", | ||
91 | .fourcc = V4L2_PIX_FMT_RGB555, | ||
92 | .cxformat = ColorFormatRGB15, | ||
93 | .depth = 16, | ||
94 | .flags = FORMAT_FLAGS_PACKED, | ||
95 | },{ | ||
96 | .name = "15 bpp RGB, be", | ||
97 | .fourcc = V4L2_PIX_FMT_RGB555X, | ||
98 | .cxformat = ColorFormatRGB15 | ColorFormatBSWAP, | ||
99 | .depth = 16, | ||
100 | .flags = FORMAT_FLAGS_PACKED, | ||
101 | },{ | ||
102 | .name = "16 bpp RGB, le", | ||
103 | .fourcc = V4L2_PIX_FMT_RGB565, | ||
104 | .cxformat = ColorFormatRGB16, | ||
105 | .depth = 16, | ||
106 | .flags = FORMAT_FLAGS_PACKED, | ||
107 | },{ | ||
108 | .name = "16 bpp RGB, be", | ||
109 | .fourcc = V4L2_PIX_FMT_RGB565X, | ||
110 | .cxformat = ColorFormatRGB16 | ColorFormatBSWAP, | ||
111 | .depth = 16, | ||
112 | .flags = FORMAT_FLAGS_PACKED, | ||
113 | },{ | ||
114 | .name = "24 bpp RGB, le", | ||
115 | .fourcc = V4L2_PIX_FMT_BGR24, | ||
116 | .cxformat = ColorFormatRGB24, | ||
117 | .depth = 24, | ||
118 | .flags = FORMAT_FLAGS_PACKED, | ||
119 | },{ | ||
120 | .name = "32 bpp RGB, le", | ||
121 | .fourcc = V4L2_PIX_FMT_BGR32, | ||
122 | .cxformat = ColorFormatRGB32, | ||
123 | .depth = 32, | ||
124 | .flags = FORMAT_FLAGS_PACKED, | ||
125 | },{ | ||
126 | .name = "32 bpp RGB, be", | ||
127 | .fourcc = V4L2_PIX_FMT_RGB32, | ||
128 | .cxformat = ColorFormatRGB32 | ColorFormatBSWAP | ColorFormatWSWAP, | ||
129 | .depth = 32, | ||
130 | .flags = FORMAT_FLAGS_PACKED, | ||
131 | },{ | ||
132 | .name = "4:2:2, packed, YUYV", | ||
133 | .fourcc = V4L2_PIX_FMT_YUYV, | ||
134 | .cxformat = ColorFormatYUY2, | ||
135 | .depth = 16, | ||
136 | .flags = FORMAT_FLAGS_PACKED, | ||
137 | },{ | ||
138 | .name = "4:2:2, packed, UYVY", | ||
139 | .fourcc = V4L2_PIX_FMT_UYVY, | ||
140 | .cxformat = ColorFormatYUY2 | ColorFormatBSWAP, | ||
141 | .depth = 16, | ||
142 | .flags = FORMAT_FLAGS_PACKED, | ||
143 | }, | ||
144 | }; | ||
145 | |||
146 | static const struct cx8800_fmt* format_by_fourcc(unsigned int fourcc) | ||
147 | { | ||
148 | unsigned int i; | ||
149 | |||
150 | for (i = 0; i < ARRAY_SIZE(formats); i++) | ||
151 | if (formats[i].fourcc == fourcc) | ||
152 | return formats+i; | ||
153 | return NULL; | ||
154 | } | ||
155 | |||
156 | /* ------------------------------------------------------------------- */ | ||
157 | |||
158 | static const struct v4l2_queryctrl no_ctl = { | ||
159 | .name = "42", | ||
160 | .flags = V4L2_CTRL_FLAG_DISABLED, | ||
161 | }; | ||
162 | |||
163 | static const struct cx88_ctrl cx8800_ctls[] = { | ||
164 | /* --- video --- */ | ||
165 | { | ||
166 | .v = { | ||
167 | .id = V4L2_CID_BRIGHTNESS, | ||
168 | .name = "Brightness", | ||
169 | .minimum = 0x00, | ||
170 | .maximum = 0xff, | ||
171 | .step = 1, | ||
172 | .default_value = 0x7f, | ||
173 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
174 | }, | ||
175 | .off = 128, | ||
176 | .reg = MO_CONTR_BRIGHT, | ||
177 | .mask = 0x00ff, | ||
178 | .shift = 0, | ||
179 | },{ | ||
180 | .v = { | ||
181 | .id = V4L2_CID_CONTRAST, | ||
182 | .name = "Contrast", | ||
183 | .minimum = 0, | ||
184 | .maximum = 0xff, | ||
185 | .step = 1, | ||
186 | .default_value = 0x3f, | ||
187 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
188 | }, | ||
189 | .off = 0, | ||
190 | .reg = MO_CONTR_BRIGHT, | ||
191 | .mask = 0xff00, | ||
192 | .shift = 8, | ||
193 | },{ | ||
194 | .v = { | ||
195 | .id = V4L2_CID_HUE, | ||
196 | .name = "Hue", | ||
197 | .minimum = 0, | ||
198 | .maximum = 0xff, | ||
199 | .step = 1, | ||
200 | .default_value = 0x7f, | ||
201 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
202 | }, | ||
203 | .off = 128, | ||
204 | .reg = MO_HUE, | ||
205 | .mask = 0x00ff, | ||
206 | .shift = 0, | ||
207 | },{ | ||
208 | /* strictly, this only describes only U saturation. | ||
209 | * V saturation is handled specially through code. | ||
210 | */ | ||
211 | .v = { | ||
212 | .id = V4L2_CID_SATURATION, | ||
213 | .name = "Saturation", | ||
214 | .minimum = 0, | ||
215 | .maximum = 0xff, | ||
216 | .step = 1, | ||
217 | .default_value = 0x7f, | ||
218 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
219 | }, | ||
220 | .off = 0, | ||
221 | .reg = MO_UV_SATURATION, | ||
222 | .mask = 0x00ff, | ||
223 | .shift = 0, | ||
224 | }, { | ||
225 | .v = { | ||
226 | .id = V4L2_CID_SHARPNESS, | ||
227 | .name = "Sharpness", | ||
228 | .minimum = 0, | ||
229 | .maximum = 4, | ||
230 | .step = 1, | ||
231 | .default_value = 0x0, | ||
232 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
233 | }, | ||
234 | .off = 0, | ||
235 | /* NOTE: the value is converted and written to both even | ||
236 | and odd registers in the code */ | ||
237 | .reg = MO_FILTER_ODD, | ||
238 | .mask = 7 << 7, | ||
239 | .shift = 7, | ||
240 | }, { | ||
241 | .v = { | ||
242 | .id = V4L2_CID_CHROMA_AGC, | ||
243 | .name = "Chroma AGC", | ||
244 | .minimum = 0, | ||
245 | .maximum = 1, | ||
246 | .default_value = 0x1, | ||
247 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
248 | }, | ||
249 | .reg = MO_INPUT_FORMAT, | ||
250 | .mask = 1 << 10, | ||
251 | .shift = 10, | ||
252 | }, { | ||
253 | .v = { | ||
254 | .id = V4L2_CID_COLOR_KILLER, | ||
255 | .name = "Color killer", | ||
256 | .minimum = 0, | ||
257 | .maximum = 1, | ||
258 | .default_value = 0x1, | ||
259 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
260 | }, | ||
261 | .reg = MO_INPUT_FORMAT, | ||
262 | .mask = 1 << 9, | ||
263 | .shift = 9, | ||
264 | }, { | ||
265 | .v = { | ||
266 | .id = V4L2_CID_BAND_STOP_FILTER, | ||
267 | .name = "Notch filter", | ||
268 | .minimum = 0, | ||
269 | .maximum = 3, | ||
270 | .step = 1, | ||
271 | .default_value = 0x0, | ||
272 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
273 | }, | ||
274 | .off = 0, | ||
275 | .reg = MO_HTOTAL, | ||
276 | .mask = 3 << 11, | ||
277 | .shift = 11, | ||
278 | }, { | ||
279 | /* --- audio --- */ | ||
280 | .v = { | ||
281 | .id = V4L2_CID_AUDIO_MUTE, | ||
282 | .name = "Mute", | ||
283 | .minimum = 0, | ||
284 | .maximum = 1, | ||
285 | .default_value = 1, | ||
286 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
287 | }, | ||
288 | .reg = AUD_VOL_CTL, | ||
289 | .sreg = SHADOW_AUD_VOL_CTL, | ||
290 | .mask = (1 << 6), | ||
291 | .shift = 6, | ||
292 | },{ | ||
293 | .v = { | ||
294 | .id = V4L2_CID_AUDIO_VOLUME, | ||
295 | .name = "Volume", | ||
296 | .minimum = 0, | ||
297 | .maximum = 0x3f, | ||
298 | .step = 1, | ||
299 | .default_value = 0x3f, | ||
300 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
301 | }, | ||
302 | .reg = AUD_VOL_CTL, | ||
303 | .sreg = SHADOW_AUD_VOL_CTL, | ||
304 | .mask = 0x3f, | ||
305 | .shift = 0, | ||
306 | },{ | ||
307 | .v = { | ||
308 | .id = V4L2_CID_AUDIO_BALANCE, | ||
309 | .name = "Balance", | ||
310 | .minimum = 0, | ||
311 | .maximum = 0x7f, | ||
312 | .step = 1, | ||
313 | .default_value = 0x40, | ||
314 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
315 | }, | ||
316 | .reg = AUD_BAL_CTL, | ||
317 | .sreg = SHADOW_AUD_BAL_CTL, | ||
318 | .mask = 0x7f, | ||
319 | .shift = 0, | ||
320 | } | ||
321 | }; | ||
322 | enum { CX8800_CTLS = ARRAY_SIZE(cx8800_ctls) }; | ||
323 | |||
324 | /* Must be sorted from low to high control ID! */ | ||
325 | const u32 cx88_user_ctrls[] = { | ||
326 | V4L2_CID_USER_CLASS, | ||
327 | V4L2_CID_BRIGHTNESS, | ||
328 | V4L2_CID_CONTRAST, | ||
329 | V4L2_CID_SATURATION, | ||
330 | V4L2_CID_HUE, | ||
331 | V4L2_CID_AUDIO_VOLUME, | ||
332 | V4L2_CID_AUDIO_BALANCE, | ||
333 | V4L2_CID_AUDIO_MUTE, | ||
334 | V4L2_CID_SHARPNESS, | ||
335 | V4L2_CID_CHROMA_AGC, | ||
336 | V4L2_CID_COLOR_KILLER, | ||
337 | V4L2_CID_BAND_STOP_FILTER, | ||
338 | 0 | ||
339 | }; | ||
340 | EXPORT_SYMBOL(cx88_user_ctrls); | ||
341 | |||
342 | static const u32 * const ctrl_classes[] = { | ||
343 | cx88_user_ctrls, | ||
344 | NULL | ||
345 | }; | ||
346 | |||
347 | int cx8800_ctrl_query(struct cx88_core *core, struct v4l2_queryctrl *qctrl) | ||
348 | { | ||
349 | int i; | ||
350 | |||
351 | if (qctrl->id < V4L2_CID_BASE || | ||
352 | qctrl->id >= V4L2_CID_LASTP1) | ||
353 | return -EINVAL; | ||
354 | for (i = 0; i < CX8800_CTLS; i++) | ||
355 | if (cx8800_ctls[i].v.id == qctrl->id) | ||
356 | break; | ||
357 | if (i == CX8800_CTLS) { | ||
358 | *qctrl = no_ctl; | ||
359 | return 0; | ||
360 | } | ||
361 | *qctrl = cx8800_ctls[i].v; | ||
362 | /* Report chroma AGC as inactive when SECAM is selected */ | ||
363 | if (cx8800_ctls[i].v.id == V4L2_CID_CHROMA_AGC && | ||
364 | core->tvnorm & V4L2_STD_SECAM) | ||
365 | qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | EXPORT_SYMBOL(cx8800_ctrl_query); | ||
370 | |||
371 | /* ------------------------------------------------------------------- */ | ||
372 | /* resource management */ | ||
373 | |||
374 | static int res_get(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bit) | ||
375 | { | ||
376 | struct cx88_core *core = dev->core; | ||
377 | if (fh->resources & bit) | ||
378 | /* have it already allocated */ | ||
379 | return 1; | ||
380 | |||
381 | /* is it free? */ | ||
382 | mutex_lock(&core->lock); | ||
383 | if (dev->resources & bit) { | ||
384 | /* no, someone else uses it */ | ||
385 | mutex_unlock(&core->lock); | ||
386 | return 0; | ||
387 | } | ||
388 | /* it's free, grab it */ | ||
389 | fh->resources |= bit; | ||
390 | dev->resources |= bit; | ||
391 | dprintk(1,"res: get %d\n",bit); | ||
392 | mutex_unlock(&core->lock); | ||
393 | return 1; | ||
394 | } | ||
395 | |||
396 | static | ||
397 | int res_check(struct cx8800_fh *fh, unsigned int bit) | ||
398 | { | ||
399 | return (fh->resources & bit); | ||
400 | } | ||
401 | |||
402 | static | ||
403 | int res_locked(struct cx8800_dev *dev, unsigned int bit) | ||
404 | { | ||
405 | return (dev->resources & bit); | ||
406 | } | ||
407 | |||
408 | static | ||
409 | void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits) | ||
410 | { | ||
411 | struct cx88_core *core = dev->core; | ||
412 | BUG_ON((fh->resources & bits) != bits); | ||
413 | |||
414 | mutex_lock(&core->lock); | ||
415 | fh->resources &= ~bits; | ||
416 | dev->resources &= ~bits; | ||
417 | dprintk(1,"res: put %d\n",bits); | ||
418 | mutex_unlock(&core->lock); | ||
419 | } | ||
420 | |||
421 | /* ------------------------------------------------------------------ */ | ||
422 | |||
423 | int cx88_video_mux(struct cx88_core *core, unsigned int input) | ||
424 | { | ||
425 | /* struct cx88_core *core = dev->core; */ | ||
426 | |||
427 | dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n", | ||
428 | input, INPUT(input).vmux, | ||
429 | INPUT(input).gpio0,INPUT(input).gpio1, | ||
430 | INPUT(input).gpio2,INPUT(input).gpio3); | ||
431 | core->input = input; | ||
432 | cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input).vmux << 14); | ||
433 | cx_write(MO_GP3_IO, INPUT(input).gpio3); | ||
434 | cx_write(MO_GP0_IO, INPUT(input).gpio0); | ||
435 | cx_write(MO_GP1_IO, INPUT(input).gpio1); | ||
436 | cx_write(MO_GP2_IO, INPUT(input).gpio2); | ||
437 | |||
438 | switch (INPUT(input).type) { | ||
439 | case CX88_VMUX_SVIDEO: | ||
440 | cx_set(MO_AFECFG_IO, 0x00000001); | ||
441 | cx_set(MO_INPUT_FORMAT, 0x00010010); | ||
442 | cx_set(MO_FILTER_EVEN, 0x00002020); | ||
443 | cx_set(MO_FILTER_ODD, 0x00002020); | ||
444 | break; | ||
445 | default: | ||
446 | cx_clear(MO_AFECFG_IO, 0x00000001); | ||
447 | cx_clear(MO_INPUT_FORMAT, 0x00010010); | ||
448 | cx_clear(MO_FILTER_EVEN, 0x00002020); | ||
449 | cx_clear(MO_FILTER_ODD, 0x00002020); | ||
450 | break; | ||
451 | } | ||
452 | |||
453 | /* if there are audioroutes defined, we have an external | ||
454 | ADC to deal with audio */ | ||
455 | if (INPUT(input).audioroute) { | ||
456 | /* The wm8775 module has the "2" route hardwired into | ||
457 | the initialization. Some boards may use different | ||
458 | routes for different inputs. HVR-1300 surely does */ | ||
459 | if (core->board.audio_chip && | ||
460 | core->board.audio_chip == V4L2_IDENT_WM8775) { | ||
461 | call_all(core, audio, s_routing, | ||
462 | INPUT(input).audioroute, 0, 0); | ||
463 | } | ||
464 | /* cx2388's C-ADC is connected to the tuner only. | ||
465 | When used with S-Video, that ADC is busy dealing with | ||
466 | chroma, so an external must be used for baseband audio */ | ||
467 | if (INPUT(input).type != CX88_VMUX_TELEVISION && | ||
468 | INPUT(input).type != CX88_VMUX_CABLE) { | ||
469 | /* "I2S ADC mode" */ | ||
470 | core->tvaudio = WW_I2SADC; | ||
471 | cx88_set_tvaudio(core); | ||
472 | } else { | ||
473 | /* Normal mode */ | ||
474 | cx_write(AUD_I2SCNTL, 0x0); | ||
475 | cx_clear(AUD_CTL, EN_I2SIN_ENABLE); | ||
476 | } | ||
477 | } | ||
478 | |||
479 | return 0; | ||
480 | } | ||
481 | EXPORT_SYMBOL(cx88_video_mux); | ||
482 | |||
483 | /* ------------------------------------------------------------------ */ | ||
484 | |||
485 | static int start_video_dma(struct cx8800_dev *dev, | ||
486 | struct cx88_dmaqueue *q, | ||
487 | struct cx88_buffer *buf) | ||
488 | { | ||
489 | struct cx88_core *core = dev->core; | ||
490 | |||
491 | /* setup fifo + format */ | ||
492 | cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], | ||
493 | buf->bpl, buf->risc.dma); | ||
494 | cx88_set_scale(core, buf->vb.width, buf->vb.height, buf->vb.field); | ||
495 | cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma); | ||
496 | |||
497 | /* reset counter */ | ||
498 | cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET); | ||
499 | q->count = 1; | ||
500 | |||
501 | /* enable irqs */ | ||
502 | cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT); | ||
503 | |||
504 | /* Enables corresponding bits at PCI_INT_STAT: | ||
505 | bits 0 to 4: video, audio, transport stream, VIP, Host | ||
506 | bit 7: timer | ||
507 | bits 8 and 9: DMA complete for: SRC, DST | ||
508 | bits 10 and 11: BERR signal asserted for RISC: RD, WR | ||
509 | bits 12 to 15: BERR signal asserted for: BRDG, SRC, DST, IPB | ||
510 | */ | ||
511 | cx_set(MO_VID_INTMSK, 0x0f0011); | ||
512 | |||
513 | /* enable capture */ | ||
514 | cx_set(VID_CAPTURE_CONTROL,0x06); | ||
515 | |||
516 | /* start dma */ | ||
517 | cx_set(MO_DEV_CNTRL2, (1<<5)); | ||
518 | cx_set(MO_VID_DMACNTRL, 0x11); /* Planar Y and packed FIFO and RISC enable */ | ||
519 | |||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | #ifdef CONFIG_PM | ||
524 | static int stop_video_dma(struct cx8800_dev *dev) | ||
525 | { | ||
526 | struct cx88_core *core = dev->core; | ||
527 | |||
528 | /* stop dma */ | ||
529 | cx_clear(MO_VID_DMACNTRL, 0x11); | ||
530 | |||
531 | /* disable capture */ | ||
532 | cx_clear(VID_CAPTURE_CONTROL,0x06); | ||
533 | |||
534 | /* disable irqs */ | ||
535 | cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT); | ||
536 | cx_clear(MO_VID_INTMSK, 0x0f0011); | ||
537 | return 0; | ||
538 | } | ||
539 | #endif | ||
540 | |||
541 | static int restart_video_queue(struct cx8800_dev *dev, | ||
542 | struct cx88_dmaqueue *q) | ||
543 | { | ||
544 | struct cx88_core *core = dev->core; | ||
545 | struct cx88_buffer *buf, *prev; | ||
546 | |||
547 | if (!list_empty(&q->active)) { | ||
548 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
549 | dprintk(2,"restart_queue [%p/%d]: restart dma\n", | ||
550 | buf, buf->vb.i); | ||
551 | start_video_dma(dev, q, buf); | ||
552 | list_for_each_entry(buf, &q->active, vb.queue) | ||
553 | buf->count = q->count++; | ||
554 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
555 | return 0; | ||
556 | } | ||
557 | |||
558 | prev = NULL; | ||
559 | for (;;) { | ||
560 | if (list_empty(&q->queued)) | ||
561 | return 0; | ||
562 | buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue); | ||
563 | if (NULL == prev) { | ||
564 | list_move_tail(&buf->vb.queue, &q->active); | ||
565 | start_video_dma(dev, q, buf); | ||
566 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
567 | buf->count = q->count++; | ||
568 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
569 | dprintk(2,"[%p/%d] restart_queue - first active\n", | ||
570 | buf,buf->vb.i); | ||
571 | |||
572 | } else if (prev->vb.width == buf->vb.width && | ||
573 | prev->vb.height == buf->vb.height && | ||
574 | prev->fmt == buf->fmt) { | ||
575 | list_move_tail(&buf->vb.queue, &q->active); | ||
576 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
577 | buf->count = q->count++; | ||
578 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
579 | dprintk(2,"[%p/%d] restart_queue - move to active\n", | ||
580 | buf,buf->vb.i); | ||
581 | } else { | ||
582 | return 0; | ||
583 | } | ||
584 | prev = buf; | ||
585 | } | ||
586 | } | ||
587 | |||
588 | /* ------------------------------------------------------------------ */ | ||
589 | |||
590 | static int | ||
591 | buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) | ||
592 | { | ||
593 | struct cx8800_fh *fh = q->priv_data; | ||
594 | |||
595 | *size = fh->fmt->depth*fh->width*fh->height >> 3; | ||
596 | if (0 == *count) | ||
597 | *count = 32; | ||
598 | if (*size * *count > vid_limit * 1024 * 1024) | ||
599 | *count = (vid_limit * 1024 * 1024) / *size; | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | static int | ||
604 | buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, | ||
605 | enum v4l2_field field) | ||
606 | { | ||
607 | struct cx8800_fh *fh = q->priv_data; | ||
608 | struct cx8800_dev *dev = fh->dev; | ||
609 | struct cx88_core *core = dev->core; | ||
610 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
611 | struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); | ||
612 | int rc, init_buffer = 0; | ||
613 | |||
614 | BUG_ON(NULL == fh->fmt); | ||
615 | if (fh->width < 48 || fh->width > norm_maxw(core->tvnorm) || | ||
616 | fh->height < 32 || fh->height > norm_maxh(core->tvnorm)) | ||
617 | return -EINVAL; | ||
618 | buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; | ||
619 | if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) | ||
620 | return -EINVAL; | ||
621 | |||
622 | if (buf->fmt != fh->fmt || | ||
623 | buf->vb.width != fh->width || | ||
624 | buf->vb.height != fh->height || | ||
625 | buf->vb.field != field) { | ||
626 | buf->fmt = fh->fmt; | ||
627 | buf->vb.width = fh->width; | ||
628 | buf->vb.height = fh->height; | ||
629 | buf->vb.field = field; | ||
630 | init_buffer = 1; | ||
631 | } | ||
632 | |||
633 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { | ||
634 | init_buffer = 1; | ||
635 | if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL))) | ||
636 | goto fail; | ||
637 | } | ||
638 | |||
639 | if (init_buffer) { | ||
640 | buf->bpl = buf->vb.width * buf->fmt->depth >> 3; | ||
641 | switch (buf->vb.field) { | ||
642 | case V4L2_FIELD_TOP: | ||
643 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
644 | dma->sglist, 0, UNSET, | ||
645 | buf->bpl, 0, buf->vb.height); | ||
646 | break; | ||
647 | case V4L2_FIELD_BOTTOM: | ||
648 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
649 | dma->sglist, UNSET, 0, | ||
650 | buf->bpl, 0, buf->vb.height); | ||
651 | break; | ||
652 | case V4L2_FIELD_INTERLACED: | ||
653 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
654 | dma->sglist, 0, buf->bpl, | ||
655 | buf->bpl, buf->bpl, | ||
656 | buf->vb.height >> 1); | ||
657 | break; | ||
658 | case V4L2_FIELD_SEQ_TB: | ||
659 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
660 | dma->sglist, | ||
661 | 0, buf->bpl * (buf->vb.height >> 1), | ||
662 | buf->bpl, 0, | ||
663 | buf->vb.height >> 1); | ||
664 | break; | ||
665 | case V4L2_FIELD_SEQ_BT: | ||
666 | cx88_risc_buffer(dev->pci, &buf->risc, | ||
667 | dma->sglist, | ||
668 | buf->bpl * (buf->vb.height >> 1), 0, | ||
669 | buf->bpl, 0, | ||
670 | buf->vb.height >> 1); | ||
671 | break; | ||
672 | default: | ||
673 | BUG(); | ||
674 | } | ||
675 | } | ||
676 | dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", | ||
677 | buf, buf->vb.i, | ||
678 | fh->width, fh->height, fh->fmt->depth, fh->fmt->name, | ||
679 | (unsigned long)buf->risc.dma); | ||
680 | |||
681 | buf->vb.state = VIDEOBUF_PREPARED; | ||
682 | return 0; | ||
683 | |||
684 | fail: | ||
685 | cx88_free_buffer(q,buf); | ||
686 | return rc; | ||
687 | } | ||
688 | |||
689 | static void | ||
690 | buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) | ||
691 | { | ||
692 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
693 | struct cx88_buffer *prev; | ||
694 | struct cx8800_fh *fh = vq->priv_data; | ||
695 | struct cx8800_dev *dev = fh->dev; | ||
696 | struct cx88_core *core = dev->core; | ||
697 | struct cx88_dmaqueue *q = &dev->vidq; | ||
698 | |||
699 | /* add jump to stopper */ | ||
700 | buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); | ||
701 | buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); | ||
702 | |||
703 | if (!list_empty(&q->queued)) { | ||
704 | list_add_tail(&buf->vb.queue,&q->queued); | ||
705 | buf->vb.state = VIDEOBUF_QUEUED; | ||
706 | dprintk(2,"[%p/%d] buffer_queue - append to queued\n", | ||
707 | buf, buf->vb.i); | ||
708 | |||
709 | } else if (list_empty(&q->active)) { | ||
710 | list_add_tail(&buf->vb.queue,&q->active); | ||
711 | start_video_dma(dev, q, buf); | ||
712 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
713 | buf->count = q->count++; | ||
714 | mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); | ||
715 | dprintk(2,"[%p/%d] buffer_queue - first active\n", | ||
716 | buf, buf->vb.i); | ||
717 | |||
718 | } else { | ||
719 | prev = list_entry(q->active.prev, struct cx88_buffer, vb.queue); | ||
720 | if (prev->vb.width == buf->vb.width && | ||
721 | prev->vb.height == buf->vb.height && | ||
722 | prev->fmt == buf->fmt) { | ||
723 | list_add_tail(&buf->vb.queue,&q->active); | ||
724 | buf->vb.state = VIDEOBUF_ACTIVE; | ||
725 | buf->count = q->count++; | ||
726 | prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); | ||
727 | dprintk(2,"[%p/%d] buffer_queue - append to active\n", | ||
728 | buf, buf->vb.i); | ||
729 | |||
730 | } else { | ||
731 | list_add_tail(&buf->vb.queue,&q->queued); | ||
732 | buf->vb.state = VIDEOBUF_QUEUED; | ||
733 | dprintk(2,"[%p/%d] buffer_queue - first queued\n", | ||
734 | buf, buf->vb.i); | ||
735 | } | ||
736 | } | ||
737 | } | ||
738 | |||
739 | static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
740 | { | ||
741 | struct cx88_buffer *buf = container_of(vb,struct cx88_buffer,vb); | ||
742 | |||
743 | cx88_free_buffer(q,buf); | ||
744 | } | ||
745 | |||
746 | static const struct videobuf_queue_ops cx8800_video_qops = { | ||
747 | .buf_setup = buffer_setup, | ||
748 | .buf_prepare = buffer_prepare, | ||
749 | .buf_queue = buffer_queue, | ||
750 | .buf_release = buffer_release, | ||
751 | }; | ||
752 | |||
753 | /* ------------------------------------------------------------------ */ | ||
754 | |||
755 | |||
756 | /* ------------------------------------------------------------------ */ | ||
757 | |||
758 | static struct videobuf_queue* get_queue(struct cx8800_fh *fh) | ||
759 | { | ||
760 | switch (fh->type) { | ||
761 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
762 | return &fh->vidq; | ||
763 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
764 | return &fh->vbiq; | ||
765 | default: | ||
766 | BUG(); | ||
767 | return NULL; | ||
768 | } | ||
769 | } | ||
770 | |||
771 | static int get_ressource(struct cx8800_fh *fh) | ||
772 | { | ||
773 | switch (fh->type) { | ||
774 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
775 | return RESOURCE_VIDEO; | ||
776 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
777 | return RESOURCE_VBI; | ||
778 | default: | ||
779 | BUG(); | ||
780 | return 0; | ||
781 | } | ||
782 | } | ||
783 | |||
784 | static int video_open(struct file *file) | ||
785 | { | ||
786 | struct video_device *vdev = video_devdata(file); | ||
787 | struct cx8800_dev *dev = video_drvdata(file); | ||
788 | struct cx88_core *core = dev->core; | ||
789 | struct cx8800_fh *fh; | ||
790 | enum v4l2_buf_type type = 0; | ||
791 | int radio = 0; | ||
792 | |||
793 | switch (vdev->vfl_type) { | ||
794 | case VFL_TYPE_GRABBER: | ||
795 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
796 | break; | ||
797 | case VFL_TYPE_VBI: | ||
798 | type = V4L2_BUF_TYPE_VBI_CAPTURE; | ||
799 | break; | ||
800 | case VFL_TYPE_RADIO: | ||
801 | radio = 1; | ||
802 | break; | ||
803 | } | ||
804 | |||
805 | dprintk(1, "open dev=%s radio=%d type=%s\n", | ||
806 | video_device_node_name(vdev), radio, v4l2_type_names[type]); | ||
807 | |||
808 | /* allocate + initialize per filehandle data */ | ||
809 | fh = kzalloc(sizeof(*fh),GFP_KERNEL); | ||
810 | if (unlikely(!fh)) | ||
811 | return -ENOMEM; | ||
812 | |||
813 | file->private_data = fh; | ||
814 | fh->dev = dev; | ||
815 | fh->radio = radio; | ||
816 | fh->type = type; | ||
817 | fh->width = 320; | ||
818 | fh->height = 240; | ||
819 | fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); | ||
820 | |||
821 | mutex_lock(&core->lock); | ||
822 | |||
823 | videobuf_queue_sg_init(&fh->vidq, &cx8800_video_qops, | ||
824 | &dev->pci->dev, &dev->slock, | ||
825 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
826 | V4L2_FIELD_INTERLACED, | ||
827 | sizeof(struct cx88_buffer), | ||
828 | fh, NULL); | ||
829 | videobuf_queue_sg_init(&fh->vbiq, &cx8800_vbi_qops, | ||
830 | &dev->pci->dev, &dev->slock, | ||
831 | V4L2_BUF_TYPE_VBI_CAPTURE, | ||
832 | V4L2_FIELD_SEQ_TB, | ||
833 | sizeof(struct cx88_buffer), | ||
834 | fh, NULL); | ||
835 | |||
836 | if (fh->radio) { | ||
837 | dprintk(1,"video_open: setting radio device\n"); | ||
838 | cx_write(MO_GP3_IO, core->board.radio.gpio3); | ||
839 | cx_write(MO_GP0_IO, core->board.radio.gpio0); | ||
840 | cx_write(MO_GP1_IO, core->board.radio.gpio1); | ||
841 | cx_write(MO_GP2_IO, core->board.radio.gpio2); | ||
842 | if (core->board.radio.audioroute) { | ||
843 | if(core->board.audio_chip && | ||
844 | core->board.audio_chip == V4L2_IDENT_WM8775) { | ||
845 | call_all(core, audio, s_routing, | ||
846 | core->board.radio.audioroute, 0, 0); | ||
847 | } | ||
848 | /* "I2S ADC mode" */ | ||
849 | core->tvaudio = WW_I2SADC; | ||
850 | cx88_set_tvaudio(core); | ||
851 | } else { | ||
852 | /* FM Mode */ | ||
853 | core->tvaudio = WW_FM; | ||
854 | cx88_set_tvaudio(core); | ||
855 | cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1); | ||
856 | } | ||
857 | call_all(core, tuner, s_radio); | ||
858 | } | ||
859 | |||
860 | core->users++; | ||
861 | mutex_unlock(&core->lock); | ||
862 | |||
863 | return 0; | ||
864 | } | ||
865 | |||
866 | static ssize_t | ||
867 | video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) | ||
868 | { | ||
869 | struct cx8800_fh *fh = file->private_data; | ||
870 | |||
871 | switch (fh->type) { | ||
872 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
873 | if (res_locked(fh->dev,RESOURCE_VIDEO)) | ||
874 | return -EBUSY; | ||
875 | return videobuf_read_one(&fh->vidq, data, count, ppos, | ||
876 | file->f_flags & O_NONBLOCK); | ||
877 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
878 | if (!res_get(fh->dev,fh,RESOURCE_VBI)) | ||
879 | return -EBUSY; | ||
880 | return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1, | ||
881 | file->f_flags & O_NONBLOCK); | ||
882 | default: | ||
883 | BUG(); | ||
884 | return 0; | ||
885 | } | ||
886 | } | ||
887 | |||
888 | static unsigned int | ||
889 | video_poll(struct file *file, struct poll_table_struct *wait) | ||
890 | { | ||
891 | struct cx8800_fh *fh = file->private_data; | ||
892 | struct cx88_buffer *buf; | ||
893 | unsigned int rc = POLLERR; | ||
894 | |||
895 | if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { | ||
896 | if (!res_get(fh->dev,fh,RESOURCE_VBI)) | ||
897 | return POLLERR; | ||
898 | return videobuf_poll_stream(file, &fh->vbiq, wait); | ||
899 | } | ||
900 | |||
901 | mutex_lock(&fh->vidq.vb_lock); | ||
902 | if (res_check(fh,RESOURCE_VIDEO)) { | ||
903 | /* streaming capture */ | ||
904 | if (list_empty(&fh->vidq.stream)) | ||
905 | goto done; | ||
906 | buf = list_entry(fh->vidq.stream.next,struct cx88_buffer,vb.stream); | ||
907 | } else { | ||
908 | /* read() capture */ | ||
909 | buf = (struct cx88_buffer*)fh->vidq.read_buf; | ||
910 | if (NULL == buf) | ||
911 | goto done; | ||
912 | } | ||
913 | poll_wait(file, &buf->vb.done, wait); | ||
914 | if (buf->vb.state == VIDEOBUF_DONE || | ||
915 | buf->vb.state == VIDEOBUF_ERROR) | ||
916 | rc = POLLIN|POLLRDNORM; | ||
917 | else | ||
918 | rc = 0; | ||
919 | done: | ||
920 | mutex_unlock(&fh->vidq.vb_lock); | ||
921 | return rc; | ||
922 | } | ||
923 | |||
924 | static int video_release(struct file *file) | ||
925 | { | ||
926 | struct cx8800_fh *fh = file->private_data; | ||
927 | struct cx8800_dev *dev = fh->dev; | ||
928 | |||
929 | /* turn off overlay */ | ||
930 | if (res_check(fh, RESOURCE_OVERLAY)) { | ||
931 | /* FIXME */ | ||
932 | res_free(dev,fh,RESOURCE_OVERLAY); | ||
933 | } | ||
934 | |||
935 | /* stop video capture */ | ||
936 | if (res_check(fh, RESOURCE_VIDEO)) { | ||
937 | videobuf_queue_cancel(&fh->vidq); | ||
938 | res_free(dev,fh,RESOURCE_VIDEO); | ||
939 | } | ||
940 | if (fh->vidq.read_buf) { | ||
941 | buffer_release(&fh->vidq,fh->vidq.read_buf); | ||
942 | kfree(fh->vidq.read_buf); | ||
943 | } | ||
944 | |||
945 | /* stop vbi capture */ | ||
946 | if (res_check(fh, RESOURCE_VBI)) { | ||
947 | videobuf_stop(&fh->vbiq); | ||
948 | res_free(dev,fh,RESOURCE_VBI); | ||
949 | } | ||
950 | |||
951 | videobuf_mmap_free(&fh->vidq); | ||
952 | videobuf_mmap_free(&fh->vbiq); | ||
953 | |||
954 | mutex_lock(&dev->core->lock); | ||
955 | file->private_data = NULL; | ||
956 | kfree(fh); | ||
957 | |||
958 | dev->core->users--; | ||
959 | if (!dev->core->users) | ||
960 | call_all(dev->core, core, s_power, 0); | ||
961 | mutex_unlock(&dev->core->lock); | ||
962 | |||
963 | return 0; | ||
964 | } | ||
965 | |||
966 | static int | ||
967 | video_mmap(struct file *file, struct vm_area_struct * vma) | ||
968 | { | ||
969 | struct cx8800_fh *fh = file->private_data; | ||
970 | |||
971 | return videobuf_mmap_mapper(get_queue(fh), vma); | ||
972 | } | ||
973 | |||
974 | /* ------------------------------------------------------------------ */ | ||
975 | /* VIDEO CTRL IOCTLS */ | ||
976 | |||
977 | int cx88_get_control (struct cx88_core *core, struct v4l2_control *ctl) | ||
978 | { | ||
979 | const struct cx88_ctrl *c = NULL; | ||
980 | u32 value; | ||
981 | int i; | ||
982 | |||
983 | for (i = 0; i < CX8800_CTLS; i++) | ||
984 | if (cx8800_ctls[i].v.id == ctl->id) | ||
985 | c = &cx8800_ctls[i]; | ||
986 | if (unlikely(NULL == c)) | ||
987 | return -EINVAL; | ||
988 | |||
989 | value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg); | ||
990 | switch (ctl->id) { | ||
991 | case V4L2_CID_AUDIO_BALANCE: | ||
992 | ctl->value = ((value & 0x7f) < 0x40) ? ((value & 0x7f) + 0x40) | ||
993 | : (0x7f - (value & 0x7f)); | ||
994 | break; | ||
995 | case V4L2_CID_AUDIO_VOLUME: | ||
996 | ctl->value = 0x3f - (value & 0x3f); | ||
997 | break; | ||
998 | case V4L2_CID_SHARPNESS: | ||
999 | ctl->value = ((value & 0x0200) ? (((value & 0x0180) >> 7) + 1) | ||
1000 | : 0); | ||
1001 | break; | ||
1002 | default: | ||
1003 | ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift; | ||
1004 | break; | ||
1005 | } | ||
1006 | dprintk(1,"get_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n", | ||
1007 | ctl->id, c->v.name, ctl->value, c->reg, | ||
1008 | value,c->mask, c->sreg ? " [shadowed]" : ""); | ||
1009 | return 0; | ||
1010 | } | ||
1011 | EXPORT_SYMBOL(cx88_get_control); | ||
1012 | |||
1013 | int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl) | ||
1014 | { | ||
1015 | const struct cx88_ctrl *c = NULL; | ||
1016 | u32 value,mask; | ||
1017 | int i; | ||
1018 | |||
1019 | for (i = 0; i < CX8800_CTLS; i++) { | ||
1020 | if (cx8800_ctls[i].v.id == ctl->id) { | ||
1021 | c = &cx8800_ctls[i]; | ||
1022 | } | ||
1023 | } | ||
1024 | if (unlikely(NULL == c)) | ||
1025 | return -EINVAL; | ||
1026 | |||
1027 | if (ctl->value < c->v.minimum) | ||
1028 | ctl->value = c->v.minimum; | ||
1029 | if (ctl->value > c->v.maximum) | ||
1030 | ctl->value = c->v.maximum; | ||
1031 | |||
1032 | /* Pass changes onto any WM8775 */ | ||
1033 | if (core->board.audio_chip == V4L2_IDENT_WM8775) { | ||
1034 | struct v4l2_control client_ctl; | ||
1035 | memset(&client_ctl, 0, sizeof(client_ctl)); | ||
1036 | client_ctl.id = ctl->id; | ||
1037 | |||
1038 | switch (ctl->id) { | ||
1039 | case V4L2_CID_AUDIO_MUTE: | ||
1040 | client_ctl.value = ctl->value; | ||
1041 | break; | ||
1042 | case V4L2_CID_AUDIO_VOLUME: | ||
1043 | client_ctl.value = (ctl->value) ? | ||
1044 | (0x90 + ctl->value) << 8 : 0; | ||
1045 | break; | ||
1046 | case V4L2_CID_AUDIO_BALANCE: | ||
1047 | client_ctl.value = ctl->value << 9; | ||
1048 | break; | ||
1049 | default: | ||
1050 | client_ctl.id = 0; | ||
1051 | break; | ||
1052 | } | ||
1053 | if (client_ctl.id) | ||
1054 | call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl); | ||
1055 | } | ||
1056 | |||
1057 | mask=c->mask; | ||
1058 | switch (ctl->id) { | ||
1059 | case V4L2_CID_AUDIO_BALANCE: | ||
1060 | value = (ctl->value < 0x40) ? (0x7f - ctl->value) : (ctl->value - 0x40); | ||
1061 | break; | ||
1062 | case V4L2_CID_AUDIO_VOLUME: | ||
1063 | value = 0x3f - (ctl->value & 0x3f); | ||
1064 | break; | ||
1065 | case V4L2_CID_SATURATION: | ||
1066 | /* special v_sat handling */ | ||
1067 | |||
1068 | value = ((ctl->value - c->off) << c->shift) & c->mask; | ||
1069 | |||
1070 | if (core->tvnorm & V4L2_STD_SECAM) { | ||
1071 | /* For SECAM, both U and V sat should be equal */ | ||
1072 | value=value<<8|value; | ||
1073 | } else { | ||
1074 | /* Keeps U Saturation proportional to V Sat */ | ||
1075 | value=(value*0x5a)/0x7f<<8|value; | ||
1076 | } | ||
1077 | mask=0xffff; | ||
1078 | break; | ||
1079 | case V4L2_CID_SHARPNESS: | ||
1080 | /* 0b000, 0b100, 0b101, 0b110, or 0b111 */ | ||
1081 | value = (ctl->value < 1 ? 0 : ((ctl->value + 3) << 7)); | ||
1082 | /* needs to be set for both fields */ | ||
1083 | cx_andor(MO_FILTER_EVEN, mask, value); | ||
1084 | break; | ||
1085 | case V4L2_CID_CHROMA_AGC: | ||
1086 | /* Do not allow chroma AGC to be enabled for SECAM */ | ||
1087 | value = ((ctl->value - c->off) << c->shift) & c->mask; | ||
1088 | if (core->tvnorm & V4L2_STD_SECAM && value) | ||
1089 | return -EINVAL; | ||
1090 | break; | ||
1091 | default: | ||
1092 | value = ((ctl->value - c->off) << c->shift) & c->mask; | ||
1093 | break; | ||
1094 | } | ||
1095 | dprintk(1,"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n", | ||
1096 | ctl->id, c->v.name, ctl->value, c->reg, value, | ||
1097 | mask, c->sreg ? " [shadowed]" : ""); | ||
1098 | if (c->sreg) { | ||
1099 | cx_sandor(c->sreg, c->reg, mask, value); | ||
1100 | } else { | ||
1101 | cx_andor(c->reg, mask, value); | ||
1102 | } | ||
1103 | return 0; | ||
1104 | } | ||
1105 | EXPORT_SYMBOL(cx88_set_control); | ||
1106 | |||
1107 | static void init_controls(struct cx88_core *core) | ||
1108 | { | ||
1109 | struct v4l2_control ctrl; | ||
1110 | int i; | ||
1111 | |||
1112 | for (i = 0; i < CX8800_CTLS; i++) { | ||
1113 | ctrl.id=cx8800_ctls[i].v.id; | ||
1114 | ctrl.value=cx8800_ctls[i].v.default_value; | ||
1115 | |||
1116 | cx88_set_control(core, &ctrl); | ||
1117 | } | ||
1118 | } | ||
1119 | |||
1120 | /* ------------------------------------------------------------------ */ | ||
1121 | /* VIDEO IOCTLS */ | ||
1122 | |||
1123 | static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, | ||
1124 | struct v4l2_format *f) | ||
1125 | { | ||
1126 | struct cx8800_fh *fh = priv; | ||
1127 | |||
1128 | f->fmt.pix.width = fh->width; | ||
1129 | f->fmt.pix.height = fh->height; | ||
1130 | f->fmt.pix.field = fh->vidq.field; | ||
1131 | f->fmt.pix.pixelformat = fh->fmt->fourcc; | ||
1132 | f->fmt.pix.bytesperline = | ||
1133 | (f->fmt.pix.width * fh->fmt->depth) >> 3; | ||
1134 | f->fmt.pix.sizeimage = | ||
1135 | f->fmt.pix.height * f->fmt.pix.bytesperline; | ||
1136 | return 0; | ||
1137 | } | ||
1138 | |||
1139 | static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, | ||
1140 | struct v4l2_format *f) | ||
1141 | { | ||
1142 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1143 | const struct cx8800_fmt *fmt; | ||
1144 | enum v4l2_field field; | ||
1145 | unsigned int maxw, maxh; | ||
1146 | |||
1147 | fmt = format_by_fourcc(f->fmt.pix.pixelformat); | ||
1148 | if (NULL == fmt) | ||
1149 | return -EINVAL; | ||
1150 | |||
1151 | field = f->fmt.pix.field; | ||
1152 | maxw = norm_maxw(core->tvnorm); | ||
1153 | maxh = norm_maxh(core->tvnorm); | ||
1154 | |||
1155 | if (V4L2_FIELD_ANY == field) { | ||
1156 | field = (f->fmt.pix.height > maxh/2) | ||
1157 | ? V4L2_FIELD_INTERLACED | ||
1158 | : V4L2_FIELD_BOTTOM; | ||
1159 | } | ||
1160 | |||
1161 | switch (field) { | ||
1162 | case V4L2_FIELD_TOP: | ||
1163 | case V4L2_FIELD_BOTTOM: | ||
1164 | maxh = maxh / 2; | ||
1165 | break; | ||
1166 | case V4L2_FIELD_INTERLACED: | ||
1167 | break; | ||
1168 | default: | ||
1169 | return -EINVAL; | ||
1170 | } | ||
1171 | |||
1172 | f->fmt.pix.field = field; | ||
1173 | v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, | ||
1174 | &f->fmt.pix.height, 32, maxh, 0, 0); | ||
1175 | f->fmt.pix.bytesperline = | ||
1176 | (f->fmt.pix.width * fmt->depth) >> 3; | ||
1177 | f->fmt.pix.sizeimage = | ||
1178 | f->fmt.pix.height * f->fmt.pix.bytesperline; | ||
1179 | |||
1180 | return 0; | ||
1181 | } | ||
1182 | |||
1183 | static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, | ||
1184 | struct v4l2_format *f) | ||
1185 | { | ||
1186 | struct cx8800_fh *fh = priv; | ||
1187 | int err = vidioc_try_fmt_vid_cap (file,priv,f); | ||
1188 | |||
1189 | if (0 != err) | ||
1190 | return err; | ||
1191 | fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); | ||
1192 | fh->width = f->fmt.pix.width; | ||
1193 | fh->height = f->fmt.pix.height; | ||
1194 | fh->vidq.field = f->fmt.pix.field; | ||
1195 | return 0; | ||
1196 | } | ||
1197 | |||
1198 | static int vidioc_querycap (struct file *file, void *priv, | ||
1199 | struct v4l2_capability *cap) | ||
1200 | { | ||
1201 | struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev; | ||
1202 | struct cx88_core *core = dev->core; | ||
1203 | |||
1204 | strcpy(cap->driver, "cx8800"); | ||
1205 | strlcpy(cap->card, core->board.name, sizeof(cap->card)); | ||
1206 | sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); | ||
1207 | cap->capabilities = | ||
1208 | V4L2_CAP_VIDEO_CAPTURE | | ||
1209 | V4L2_CAP_READWRITE | | ||
1210 | V4L2_CAP_STREAMING | | ||
1211 | V4L2_CAP_VBI_CAPTURE; | ||
1212 | if (UNSET != core->board.tuner_type) | ||
1213 | cap->capabilities |= V4L2_CAP_TUNER; | ||
1214 | return 0; | ||
1215 | } | ||
1216 | |||
1217 | static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, | ||
1218 | struct v4l2_fmtdesc *f) | ||
1219 | { | ||
1220 | if (unlikely(f->index >= ARRAY_SIZE(formats))) | ||
1221 | return -EINVAL; | ||
1222 | |||
1223 | strlcpy(f->description,formats[f->index].name,sizeof(f->description)); | ||
1224 | f->pixelformat = formats[f->index].fourcc; | ||
1225 | |||
1226 | return 0; | ||
1227 | } | ||
1228 | |||
1229 | static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p) | ||
1230 | { | ||
1231 | struct cx8800_fh *fh = priv; | ||
1232 | return (videobuf_reqbufs(get_queue(fh), p)); | ||
1233 | } | ||
1234 | |||
1235 | static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p) | ||
1236 | { | ||
1237 | struct cx8800_fh *fh = priv; | ||
1238 | return (videobuf_querybuf(get_queue(fh), p)); | ||
1239 | } | ||
1240 | |||
1241 | static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) | ||
1242 | { | ||
1243 | struct cx8800_fh *fh = priv; | ||
1244 | return (videobuf_qbuf(get_queue(fh), p)); | ||
1245 | } | ||
1246 | |||
1247 | static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) | ||
1248 | { | ||
1249 | struct cx8800_fh *fh = priv; | ||
1250 | return (videobuf_dqbuf(get_queue(fh), p, | ||
1251 | file->f_flags & O_NONBLOCK)); | ||
1252 | } | ||
1253 | |||
1254 | static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) | ||
1255 | { | ||
1256 | struct cx8800_fh *fh = priv; | ||
1257 | struct cx8800_dev *dev = fh->dev; | ||
1258 | |||
1259 | /* We should remember that this driver also supports teletext, */ | ||
1260 | /* so we have to test if the v4l2_buf_type is VBI capture data. */ | ||
1261 | if (unlikely((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
1262 | (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))) | ||
1263 | return -EINVAL; | ||
1264 | |||
1265 | if (unlikely(i != fh->type)) | ||
1266 | return -EINVAL; | ||
1267 | |||
1268 | if (unlikely(!res_get(dev,fh,get_ressource(fh)))) | ||
1269 | return -EBUSY; | ||
1270 | return videobuf_streamon(get_queue(fh)); | ||
1271 | } | ||
1272 | |||
1273 | static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) | ||
1274 | { | ||
1275 | struct cx8800_fh *fh = priv; | ||
1276 | struct cx8800_dev *dev = fh->dev; | ||
1277 | int err, res; | ||
1278 | |||
1279 | if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
1280 | (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
1281 | return -EINVAL; | ||
1282 | |||
1283 | if (i != fh->type) | ||
1284 | return -EINVAL; | ||
1285 | |||
1286 | res = get_ressource(fh); | ||
1287 | err = videobuf_streamoff(get_queue(fh)); | ||
1288 | if (err < 0) | ||
1289 | return err; | ||
1290 | res_free(dev,fh,res); | ||
1291 | return 0; | ||
1292 | } | ||
1293 | |||
1294 | static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms) | ||
1295 | { | ||
1296 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1297 | |||
1298 | mutex_lock(&core->lock); | ||
1299 | cx88_set_tvnorm(core,*tvnorms); | ||
1300 | mutex_unlock(&core->lock); | ||
1301 | |||
1302 | return 0; | ||
1303 | } | ||
1304 | |||
1305 | /* only one input in this sample driver */ | ||
1306 | int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i) | ||
1307 | { | ||
1308 | static const char * const iname[] = { | ||
1309 | [ CX88_VMUX_COMPOSITE1 ] = "Composite1", | ||
1310 | [ CX88_VMUX_COMPOSITE2 ] = "Composite2", | ||
1311 | [ CX88_VMUX_COMPOSITE3 ] = "Composite3", | ||
1312 | [ CX88_VMUX_COMPOSITE4 ] = "Composite4", | ||
1313 | [ CX88_VMUX_SVIDEO ] = "S-Video", | ||
1314 | [ CX88_VMUX_TELEVISION ] = "Television", | ||
1315 | [ CX88_VMUX_CABLE ] = "Cable TV", | ||
1316 | [ CX88_VMUX_DVB ] = "DVB", | ||
1317 | [ CX88_VMUX_DEBUG ] = "for debug only", | ||
1318 | }; | ||
1319 | unsigned int n = i->index; | ||
1320 | |||
1321 | if (n >= 4) | ||
1322 | return -EINVAL; | ||
1323 | if (0 == INPUT(n).type) | ||
1324 | return -EINVAL; | ||
1325 | i->type = V4L2_INPUT_TYPE_CAMERA; | ||
1326 | strcpy(i->name,iname[INPUT(n).type]); | ||
1327 | if ((CX88_VMUX_TELEVISION == INPUT(n).type) || | ||
1328 | (CX88_VMUX_CABLE == INPUT(n).type)) { | ||
1329 | i->type = V4L2_INPUT_TYPE_TUNER; | ||
1330 | i->std = CX88_NORMS; | ||
1331 | } | ||
1332 | return 0; | ||
1333 | } | ||
1334 | EXPORT_SYMBOL(cx88_enum_input); | ||
1335 | |||
1336 | static int vidioc_enum_input (struct file *file, void *priv, | ||
1337 | struct v4l2_input *i) | ||
1338 | { | ||
1339 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1340 | return cx88_enum_input (core,i); | ||
1341 | } | ||
1342 | |||
1343 | static int vidioc_g_input (struct file *file, void *priv, unsigned int *i) | ||
1344 | { | ||
1345 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1346 | |||
1347 | *i = core->input; | ||
1348 | return 0; | ||
1349 | } | ||
1350 | |||
1351 | static int vidioc_s_input (struct file *file, void *priv, unsigned int i) | ||
1352 | { | ||
1353 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1354 | |||
1355 | if (i >= 4) | ||
1356 | return -EINVAL; | ||
1357 | |||
1358 | mutex_lock(&core->lock); | ||
1359 | cx88_newstation(core); | ||
1360 | cx88_video_mux(core,i); | ||
1361 | mutex_unlock(&core->lock); | ||
1362 | return 0; | ||
1363 | } | ||
1364 | |||
1365 | |||
1366 | |||
1367 | static int vidioc_queryctrl (struct file *file, void *priv, | ||
1368 | struct v4l2_queryctrl *qctrl) | ||
1369 | { | ||
1370 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1371 | |||
1372 | qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); | ||
1373 | if (unlikely(qctrl->id == 0)) | ||
1374 | return -EINVAL; | ||
1375 | return cx8800_ctrl_query(core, qctrl); | ||
1376 | } | ||
1377 | |||
1378 | static int vidioc_g_ctrl (struct file *file, void *priv, | ||
1379 | struct v4l2_control *ctl) | ||
1380 | { | ||
1381 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1382 | return | ||
1383 | cx88_get_control(core,ctl); | ||
1384 | } | ||
1385 | |||
1386 | static int vidioc_s_ctrl (struct file *file, void *priv, | ||
1387 | struct v4l2_control *ctl) | ||
1388 | { | ||
1389 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1390 | return | ||
1391 | cx88_set_control(core,ctl); | ||
1392 | } | ||
1393 | |||
1394 | static int vidioc_g_tuner (struct file *file, void *priv, | ||
1395 | struct v4l2_tuner *t) | ||
1396 | { | ||
1397 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1398 | u32 reg; | ||
1399 | |||
1400 | if (unlikely(UNSET == core->board.tuner_type)) | ||
1401 | return -EINVAL; | ||
1402 | if (0 != t->index) | ||
1403 | return -EINVAL; | ||
1404 | |||
1405 | strcpy(t->name, "Television"); | ||
1406 | t->type = V4L2_TUNER_ANALOG_TV; | ||
1407 | t->capability = V4L2_TUNER_CAP_NORM; | ||
1408 | t->rangehigh = 0xffffffffUL; | ||
1409 | |||
1410 | cx88_get_stereo(core ,t); | ||
1411 | reg = cx_read(MO_DEVICE_STATUS); | ||
1412 | t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; | ||
1413 | return 0; | ||
1414 | } | ||
1415 | |||
1416 | static int vidioc_s_tuner (struct file *file, void *priv, | ||
1417 | struct v4l2_tuner *t) | ||
1418 | { | ||
1419 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1420 | |||
1421 | if (UNSET == core->board.tuner_type) | ||
1422 | return -EINVAL; | ||
1423 | if (0 != t->index) | ||
1424 | return -EINVAL; | ||
1425 | |||
1426 | cx88_set_stereo(core, t->audmode, 1); | ||
1427 | return 0; | ||
1428 | } | ||
1429 | |||
1430 | static int vidioc_g_frequency (struct file *file, void *priv, | ||
1431 | struct v4l2_frequency *f) | ||
1432 | { | ||
1433 | struct cx8800_fh *fh = priv; | ||
1434 | struct cx88_core *core = fh->dev->core; | ||
1435 | |||
1436 | if (unlikely(UNSET == core->board.tuner_type)) | ||
1437 | return -EINVAL; | ||
1438 | |||
1439 | /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */ | ||
1440 | f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; | ||
1441 | f->frequency = core->freq; | ||
1442 | |||
1443 | call_all(core, tuner, g_frequency, f); | ||
1444 | |||
1445 | return 0; | ||
1446 | } | ||
1447 | |||
1448 | int cx88_set_freq (struct cx88_core *core, | ||
1449 | struct v4l2_frequency *f) | ||
1450 | { | ||
1451 | if (unlikely(UNSET == core->board.tuner_type)) | ||
1452 | return -EINVAL; | ||
1453 | if (unlikely(f->tuner != 0)) | ||
1454 | return -EINVAL; | ||
1455 | |||
1456 | mutex_lock(&core->lock); | ||
1457 | core->freq = f->frequency; | ||
1458 | cx88_newstation(core); | ||
1459 | call_all(core, tuner, s_frequency, f); | ||
1460 | |||
1461 | /* When changing channels it is required to reset TVAUDIO */ | ||
1462 | msleep (10); | ||
1463 | cx88_set_tvaudio(core); | ||
1464 | |||
1465 | mutex_unlock(&core->lock); | ||
1466 | |||
1467 | return 0; | ||
1468 | } | ||
1469 | EXPORT_SYMBOL(cx88_set_freq); | ||
1470 | |||
1471 | static int vidioc_s_frequency (struct file *file, void *priv, | ||
1472 | struct v4l2_frequency *f) | ||
1473 | { | ||
1474 | struct cx8800_fh *fh = priv; | ||
1475 | struct cx88_core *core = fh->dev->core; | ||
1476 | |||
1477 | if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) | ||
1478 | return -EINVAL; | ||
1479 | if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) | ||
1480 | return -EINVAL; | ||
1481 | |||
1482 | return | ||
1483 | cx88_set_freq (core,f); | ||
1484 | } | ||
1485 | |||
1486 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1487 | static int vidioc_g_register (struct file *file, void *fh, | ||
1488 | struct v4l2_dbg_register *reg) | ||
1489 | { | ||
1490 | struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; | ||
1491 | |||
1492 | if (!v4l2_chip_match_host(®->match)) | ||
1493 | return -EINVAL; | ||
1494 | /* cx2388x has a 24-bit register space */ | ||
1495 | reg->val = cx_read(reg->reg & 0xffffff); | ||
1496 | reg->size = 4; | ||
1497 | return 0; | ||
1498 | } | ||
1499 | |||
1500 | static int vidioc_s_register (struct file *file, void *fh, | ||
1501 | struct v4l2_dbg_register *reg) | ||
1502 | { | ||
1503 | struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; | ||
1504 | |||
1505 | if (!v4l2_chip_match_host(®->match)) | ||
1506 | return -EINVAL; | ||
1507 | cx_write(reg->reg & 0xffffff, reg->val); | ||
1508 | return 0; | ||
1509 | } | ||
1510 | #endif | ||
1511 | |||
1512 | /* ----------------------------------------------------------- */ | ||
1513 | /* RADIO ESPECIFIC IOCTLS */ | ||
1514 | /* ----------------------------------------------------------- */ | ||
1515 | |||
1516 | static int radio_querycap (struct file *file, void *priv, | ||
1517 | struct v4l2_capability *cap) | ||
1518 | { | ||
1519 | struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev; | ||
1520 | struct cx88_core *core = dev->core; | ||
1521 | |||
1522 | strcpy(cap->driver, "cx8800"); | ||
1523 | strlcpy(cap->card, core->board.name, sizeof(cap->card)); | ||
1524 | sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); | ||
1525 | cap->capabilities = V4L2_CAP_TUNER; | ||
1526 | return 0; | ||
1527 | } | ||
1528 | |||
1529 | static int radio_g_tuner (struct file *file, void *priv, | ||
1530 | struct v4l2_tuner *t) | ||
1531 | { | ||
1532 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1533 | |||
1534 | if (unlikely(t->index > 0)) | ||
1535 | return -EINVAL; | ||
1536 | |||
1537 | strcpy(t->name, "Radio"); | ||
1538 | t->type = V4L2_TUNER_RADIO; | ||
1539 | |||
1540 | call_all(core, tuner, g_tuner, t); | ||
1541 | return 0; | ||
1542 | } | ||
1543 | |||
1544 | static int radio_enum_input (struct file *file, void *priv, | ||
1545 | struct v4l2_input *i) | ||
1546 | { | ||
1547 | if (i->index != 0) | ||
1548 | return -EINVAL; | ||
1549 | strcpy(i->name,"Radio"); | ||
1550 | i->type = V4L2_INPUT_TYPE_TUNER; | ||
1551 | |||
1552 | return 0; | ||
1553 | } | ||
1554 | |||
1555 | static int radio_g_audio (struct file *file, void *priv, struct v4l2_audio *a) | ||
1556 | { | ||
1557 | if (unlikely(a->index)) | ||
1558 | return -EINVAL; | ||
1559 | |||
1560 | strcpy(a->name,"Radio"); | ||
1561 | return 0; | ||
1562 | } | ||
1563 | |||
1564 | /* FIXME: Should add a standard for radio */ | ||
1565 | |||
1566 | static int radio_s_tuner (struct file *file, void *priv, | ||
1567 | struct v4l2_tuner *t) | ||
1568 | { | ||
1569 | struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; | ||
1570 | |||
1571 | if (0 != t->index) | ||
1572 | return -EINVAL; | ||
1573 | |||
1574 | call_all(core, tuner, s_tuner, t); | ||
1575 | |||
1576 | return 0; | ||
1577 | } | ||
1578 | |||
1579 | static int radio_s_audio (struct file *file, void *fh, | ||
1580 | struct v4l2_audio *a) | ||
1581 | { | ||
1582 | return 0; | ||
1583 | } | ||
1584 | |||
1585 | static int radio_s_input (struct file *file, void *fh, unsigned int i) | ||
1586 | { | ||
1587 | return 0; | ||
1588 | } | ||
1589 | |||
1590 | static int radio_queryctrl (struct file *file, void *priv, | ||
1591 | struct v4l2_queryctrl *c) | ||
1592 | { | ||
1593 | int i; | ||
1594 | |||
1595 | if (c->id < V4L2_CID_BASE || | ||
1596 | c->id >= V4L2_CID_LASTP1) | ||
1597 | return -EINVAL; | ||
1598 | if (c->id == V4L2_CID_AUDIO_MUTE || | ||
1599 | c->id == V4L2_CID_AUDIO_VOLUME || | ||
1600 | c->id == V4L2_CID_AUDIO_BALANCE) { | ||
1601 | for (i = 0; i < CX8800_CTLS; i++) { | ||
1602 | if (cx8800_ctls[i].v.id == c->id) | ||
1603 | break; | ||
1604 | } | ||
1605 | if (i == CX8800_CTLS) | ||
1606 | return -EINVAL; | ||
1607 | *c = cx8800_ctls[i].v; | ||
1608 | } else | ||
1609 | *c = no_ctl; | ||
1610 | return 0; | ||
1611 | } | ||
1612 | |||
1613 | /* ----------------------------------------------------------- */ | ||
1614 | |||
1615 | static void cx8800_vid_timeout(unsigned long data) | ||
1616 | { | ||
1617 | struct cx8800_dev *dev = (struct cx8800_dev*)data; | ||
1618 | struct cx88_core *core = dev->core; | ||
1619 | struct cx88_dmaqueue *q = &dev->vidq; | ||
1620 | struct cx88_buffer *buf; | ||
1621 | unsigned long flags; | ||
1622 | |||
1623 | cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]); | ||
1624 | |||
1625 | cx_clear(MO_VID_DMACNTRL, 0x11); | ||
1626 | cx_clear(VID_CAPTURE_CONTROL, 0x06); | ||
1627 | |||
1628 | spin_lock_irqsave(&dev->slock,flags); | ||
1629 | while (!list_empty(&q->active)) { | ||
1630 | buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); | ||
1631 | list_del(&buf->vb.queue); | ||
1632 | buf->vb.state = VIDEOBUF_ERROR; | ||
1633 | wake_up(&buf->vb.done); | ||
1634 | printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name, | ||
1635 | buf, buf->vb.i, (unsigned long)buf->risc.dma); | ||
1636 | } | ||
1637 | restart_video_queue(dev,q); | ||
1638 | spin_unlock_irqrestore(&dev->slock,flags); | ||
1639 | } | ||
1640 | |||
1641 | static const char *cx88_vid_irqs[32] = { | ||
1642 | "y_risci1", "u_risci1", "v_risci1", "vbi_risc1", | ||
1643 | "y_risci2", "u_risci2", "v_risci2", "vbi_risc2", | ||
1644 | "y_oflow", "u_oflow", "v_oflow", "vbi_oflow", | ||
1645 | "y_sync", "u_sync", "v_sync", "vbi_sync", | ||
1646 | "opc_err", "par_err", "rip_err", "pci_abort", | ||
1647 | }; | ||
1648 | |||
1649 | static void cx8800_vid_irq(struct cx8800_dev *dev) | ||
1650 | { | ||
1651 | struct cx88_core *core = dev->core; | ||
1652 | u32 status, mask, count; | ||
1653 | |||
1654 | status = cx_read(MO_VID_INTSTAT); | ||
1655 | mask = cx_read(MO_VID_INTMSK); | ||
1656 | if (0 == (status & mask)) | ||
1657 | return; | ||
1658 | cx_write(MO_VID_INTSTAT, status); | ||
1659 | if (irq_debug || (status & mask & ~0xff)) | ||
1660 | cx88_print_irqbits(core->name, "irq vid", | ||
1661 | cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs), | ||
1662 | status, mask); | ||
1663 | |||
1664 | /* risc op code error */ | ||
1665 | if (status & (1 << 16)) { | ||
1666 | printk(KERN_WARNING "%s/0: video risc op code error\n",core->name); | ||
1667 | cx_clear(MO_VID_DMACNTRL, 0x11); | ||
1668 | cx_clear(VID_CAPTURE_CONTROL, 0x06); | ||
1669 | cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]); | ||
1670 | } | ||
1671 | |||
1672 | /* risc1 y */ | ||
1673 | if (status & 0x01) { | ||
1674 | spin_lock(&dev->slock); | ||
1675 | count = cx_read(MO_VIDY_GPCNT); | ||
1676 | cx88_wakeup(core, &dev->vidq, count); | ||
1677 | spin_unlock(&dev->slock); | ||
1678 | } | ||
1679 | |||
1680 | /* risc1 vbi */ | ||
1681 | if (status & 0x08) { | ||
1682 | spin_lock(&dev->slock); | ||
1683 | count = cx_read(MO_VBI_GPCNT); | ||
1684 | cx88_wakeup(core, &dev->vbiq, count); | ||
1685 | spin_unlock(&dev->slock); | ||
1686 | } | ||
1687 | |||
1688 | /* risc2 y */ | ||
1689 | if (status & 0x10) { | ||
1690 | dprintk(2,"stopper video\n"); | ||
1691 | spin_lock(&dev->slock); | ||
1692 | restart_video_queue(dev,&dev->vidq); | ||
1693 | spin_unlock(&dev->slock); | ||
1694 | } | ||
1695 | |||
1696 | /* risc2 vbi */ | ||
1697 | if (status & 0x80) { | ||
1698 | dprintk(2,"stopper vbi\n"); | ||
1699 | spin_lock(&dev->slock); | ||
1700 | cx8800_restart_vbi_queue(dev,&dev->vbiq); | ||
1701 | spin_unlock(&dev->slock); | ||
1702 | } | ||
1703 | } | ||
1704 | |||
1705 | static irqreturn_t cx8800_irq(int irq, void *dev_id) | ||
1706 | { | ||
1707 | struct cx8800_dev *dev = dev_id; | ||
1708 | struct cx88_core *core = dev->core; | ||
1709 | u32 status; | ||
1710 | int loop, handled = 0; | ||
1711 | |||
1712 | for (loop = 0; loop < 10; loop++) { | ||
1713 | status = cx_read(MO_PCI_INTSTAT) & | ||
1714 | (core->pci_irqmask | PCI_INT_VIDINT); | ||
1715 | if (0 == status) | ||
1716 | goto out; | ||
1717 | cx_write(MO_PCI_INTSTAT, status); | ||
1718 | handled = 1; | ||
1719 | |||
1720 | if (status & core->pci_irqmask) | ||
1721 | cx88_core_irq(core,status); | ||
1722 | if (status & PCI_INT_VIDINT) | ||
1723 | cx8800_vid_irq(dev); | ||
1724 | }; | ||
1725 | if (10 == loop) { | ||
1726 | printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", | ||
1727 | core->name); | ||
1728 | cx_write(MO_PCI_INTMSK,0); | ||
1729 | } | ||
1730 | |||
1731 | out: | ||
1732 | return IRQ_RETVAL(handled); | ||
1733 | } | ||
1734 | |||
1735 | /* ----------------------------------------------------------- */ | ||
1736 | /* exported stuff */ | ||
1737 | |||
1738 | static const struct v4l2_file_operations video_fops = | ||
1739 | { | ||
1740 | .owner = THIS_MODULE, | ||
1741 | .open = video_open, | ||
1742 | .release = video_release, | ||
1743 | .read = video_read, | ||
1744 | .poll = video_poll, | ||
1745 | .mmap = video_mmap, | ||
1746 | .unlocked_ioctl = video_ioctl2, | ||
1747 | }; | ||
1748 | |||
1749 | static const struct v4l2_ioctl_ops video_ioctl_ops = { | ||
1750 | .vidioc_querycap = vidioc_querycap, | ||
1751 | .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, | ||
1752 | .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, | ||
1753 | .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, | ||
1754 | .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, | ||
1755 | .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt, | ||
1756 | .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt, | ||
1757 | .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt, | ||
1758 | .vidioc_reqbufs = vidioc_reqbufs, | ||
1759 | .vidioc_querybuf = vidioc_querybuf, | ||
1760 | .vidioc_qbuf = vidioc_qbuf, | ||
1761 | .vidioc_dqbuf = vidioc_dqbuf, | ||
1762 | .vidioc_s_std = vidioc_s_std, | ||
1763 | .vidioc_enum_input = vidioc_enum_input, | ||
1764 | .vidioc_g_input = vidioc_g_input, | ||
1765 | .vidioc_s_input = vidioc_s_input, | ||
1766 | .vidioc_queryctrl = vidioc_queryctrl, | ||
1767 | .vidioc_g_ctrl = vidioc_g_ctrl, | ||
1768 | .vidioc_s_ctrl = vidioc_s_ctrl, | ||
1769 | .vidioc_streamon = vidioc_streamon, | ||
1770 | .vidioc_streamoff = vidioc_streamoff, | ||
1771 | .vidioc_g_tuner = vidioc_g_tuner, | ||
1772 | .vidioc_s_tuner = vidioc_s_tuner, | ||
1773 | .vidioc_g_frequency = vidioc_g_frequency, | ||
1774 | .vidioc_s_frequency = vidioc_s_frequency, | ||
1775 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1776 | .vidioc_g_register = vidioc_g_register, | ||
1777 | .vidioc_s_register = vidioc_s_register, | ||
1778 | #endif | ||
1779 | }; | ||
1780 | |||
1781 | static struct video_device cx8800_vbi_template; | ||
1782 | |||
1783 | static const struct video_device cx8800_video_template = { | ||
1784 | .name = "cx8800-video", | ||
1785 | .fops = &video_fops, | ||
1786 | .ioctl_ops = &video_ioctl_ops, | ||
1787 | .tvnorms = CX88_NORMS, | ||
1788 | .current_norm = V4L2_STD_NTSC_M, | ||
1789 | }; | ||
1790 | |||
1791 | static const struct v4l2_file_operations radio_fops = | ||
1792 | { | ||
1793 | .owner = THIS_MODULE, | ||
1794 | .open = video_open, | ||
1795 | .release = video_release, | ||
1796 | .unlocked_ioctl = video_ioctl2, | ||
1797 | }; | ||
1798 | |||
1799 | static const struct v4l2_ioctl_ops radio_ioctl_ops = { | ||
1800 | .vidioc_querycap = radio_querycap, | ||
1801 | .vidioc_g_tuner = radio_g_tuner, | ||
1802 | .vidioc_enum_input = radio_enum_input, | ||
1803 | .vidioc_g_audio = radio_g_audio, | ||
1804 | .vidioc_s_tuner = radio_s_tuner, | ||
1805 | .vidioc_s_audio = radio_s_audio, | ||
1806 | .vidioc_s_input = radio_s_input, | ||
1807 | .vidioc_queryctrl = radio_queryctrl, | ||
1808 | .vidioc_g_ctrl = vidioc_g_ctrl, | ||
1809 | .vidioc_s_ctrl = vidioc_s_ctrl, | ||
1810 | .vidioc_g_frequency = vidioc_g_frequency, | ||
1811 | .vidioc_s_frequency = vidioc_s_frequency, | ||
1812 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1813 | .vidioc_g_register = vidioc_g_register, | ||
1814 | .vidioc_s_register = vidioc_s_register, | ||
1815 | #endif | ||
1816 | }; | ||
1817 | |||
1818 | static const struct video_device cx8800_radio_template = { | ||
1819 | .name = "cx8800-radio", | ||
1820 | .fops = &radio_fops, | ||
1821 | .ioctl_ops = &radio_ioctl_ops, | ||
1822 | }; | ||
1823 | |||
1824 | /* ----------------------------------------------------------- */ | ||
1825 | |||
1826 | static void cx8800_unregister_video(struct cx8800_dev *dev) | ||
1827 | { | ||
1828 | if (dev->radio_dev) { | ||
1829 | if (video_is_registered(dev->radio_dev)) | ||
1830 | video_unregister_device(dev->radio_dev); | ||
1831 | else | ||
1832 | video_device_release(dev->radio_dev); | ||
1833 | dev->radio_dev = NULL; | ||
1834 | } | ||
1835 | if (dev->vbi_dev) { | ||
1836 | if (video_is_registered(dev->vbi_dev)) | ||
1837 | video_unregister_device(dev->vbi_dev); | ||
1838 | else | ||
1839 | video_device_release(dev->vbi_dev); | ||
1840 | dev->vbi_dev = NULL; | ||
1841 | } | ||
1842 | if (dev->video_dev) { | ||
1843 | if (video_is_registered(dev->video_dev)) | ||
1844 | video_unregister_device(dev->video_dev); | ||
1845 | else | ||
1846 | video_device_release(dev->video_dev); | ||
1847 | dev->video_dev = NULL; | ||
1848 | } | ||
1849 | } | ||
1850 | |||
1851 | static int __devinit cx8800_initdev(struct pci_dev *pci_dev, | ||
1852 | const struct pci_device_id *pci_id) | ||
1853 | { | ||
1854 | struct cx8800_dev *dev; | ||
1855 | struct cx88_core *core; | ||
1856 | |||
1857 | int err; | ||
1858 | |||
1859 | dev = kzalloc(sizeof(*dev),GFP_KERNEL); | ||
1860 | if (NULL == dev) | ||
1861 | return -ENOMEM; | ||
1862 | |||
1863 | /* pci init */ | ||
1864 | dev->pci = pci_dev; | ||
1865 | if (pci_enable_device(pci_dev)) { | ||
1866 | err = -EIO; | ||
1867 | goto fail_free; | ||
1868 | } | ||
1869 | core = cx88_core_get(dev->pci); | ||
1870 | if (NULL == core) { | ||
1871 | err = -EINVAL; | ||
1872 | goto fail_free; | ||
1873 | } | ||
1874 | dev->core = core; | ||
1875 | |||
1876 | /* print pci info */ | ||
1877 | dev->pci_rev = pci_dev->revision; | ||
1878 | pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); | ||
1879 | printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " | ||
1880 | "latency: %d, mmio: 0x%llx\n", core->name, | ||
1881 | pci_name(pci_dev), dev->pci_rev, pci_dev->irq, | ||
1882 | dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0)); | ||
1883 | |||
1884 | pci_set_master(pci_dev); | ||
1885 | if (!pci_dma_supported(pci_dev,DMA_BIT_MASK(32))) { | ||
1886 | printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name); | ||
1887 | err = -EIO; | ||
1888 | goto fail_core; | ||
1889 | } | ||
1890 | |||
1891 | /* Initialize VBI template */ | ||
1892 | memcpy( &cx8800_vbi_template, &cx8800_video_template, | ||
1893 | sizeof(cx8800_vbi_template) ); | ||
1894 | strcpy(cx8800_vbi_template.name,"cx8800-vbi"); | ||
1895 | |||
1896 | /* initialize driver struct */ | ||
1897 | spin_lock_init(&dev->slock); | ||
1898 | core->tvnorm = cx8800_video_template.current_norm; | ||
1899 | |||
1900 | /* init video dma queues */ | ||
1901 | INIT_LIST_HEAD(&dev->vidq.active); | ||
1902 | INIT_LIST_HEAD(&dev->vidq.queued); | ||
1903 | dev->vidq.timeout.function = cx8800_vid_timeout; | ||
1904 | dev->vidq.timeout.data = (unsigned long)dev; | ||
1905 | init_timer(&dev->vidq.timeout); | ||
1906 | cx88_risc_stopper(dev->pci,&dev->vidq.stopper, | ||
1907 | MO_VID_DMACNTRL,0x11,0x00); | ||
1908 | |||
1909 | /* init vbi dma queues */ | ||
1910 | INIT_LIST_HEAD(&dev->vbiq.active); | ||
1911 | INIT_LIST_HEAD(&dev->vbiq.queued); | ||
1912 | dev->vbiq.timeout.function = cx8800_vbi_timeout; | ||
1913 | dev->vbiq.timeout.data = (unsigned long)dev; | ||
1914 | init_timer(&dev->vbiq.timeout); | ||
1915 | cx88_risc_stopper(dev->pci,&dev->vbiq.stopper, | ||
1916 | MO_VID_DMACNTRL,0x88,0x00); | ||
1917 | |||
1918 | /* get irq */ | ||
1919 | err = request_irq(pci_dev->irq, cx8800_irq, | ||
1920 | IRQF_SHARED | IRQF_DISABLED, core->name, dev); | ||
1921 | if (err < 0) { | ||
1922 | printk(KERN_ERR "%s/0: can't get IRQ %d\n", | ||
1923 | core->name,pci_dev->irq); | ||
1924 | goto fail_core; | ||
1925 | } | ||
1926 | cx_set(MO_PCI_INTMSK, core->pci_irqmask); | ||
1927 | |||
1928 | /* load and configure helper modules */ | ||
1929 | |||
1930 | if (core->board.audio_chip == V4L2_IDENT_WM8775) { | ||
1931 | struct i2c_board_info wm8775_info = { | ||
1932 | .type = "wm8775", | ||
1933 | .addr = 0x36 >> 1, | ||
1934 | .platform_data = &core->wm8775_data, | ||
1935 | }; | ||
1936 | struct v4l2_subdev *sd; | ||
1937 | |||
1938 | if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1) | ||
1939 | core->wm8775_data.is_nova_s = true; | ||
1940 | else | ||
1941 | core->wm8775_data.is_nova_s = false; | ||
1942 | |||
1943 | sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap, | ||
1944 | &wm8775_info, NULL); | ||
1945 | if (sd != NULL) | ||
1946 | sd->grp_id = WM8775_GID; | ||
1947 | } | ||
1948 | |||
1949 | if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) { | ||
1950 | /* This probes for a tda9874 as is used on some | ||
1951 | Pixelview Ultra boards. */ | ||
1952 | v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, | ||
1953 | "tvaudio", 0, I2C_ADDRS(0xb0 >> 1)); | ||
1954 | } | ||
1955 | |||
1956 | switch (core->boardnr) { | ||
1957 | case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: | ||
1958 | case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: { | ||
1959 | static const struct i2c_board_info rtc_info = { | ||
1960 | I2C_BOARD_INFO("isl1208", 0x6f) | ||
1961 | }; | ||
1962 | |||
1963 | request_module("rtc-isl1208"); | ||
1964 | core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info); | ||
1965 | } | ||
1966 | /* break intentionally omitted */ | ||
1967 | case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: | ||
1968 | request_module("ir-kbd-i2c"); | ||
1969 | } | ||
1970 | |||
1971 | /* Sets device info at pci_dev */ | ||
1972 | pci_set_drvdata(pci_dev, dev); | ||
1973 | |||
1974 | /* initial device configuration */ | ||
1975 | mutex_lock(&core->lock); | ||
1976 | cx88_set_tvnorm(core, core->tvnorm); | ||
1977 | init_controls(core); | ||
1978 | cx88_video_mux(core, 0); | ||
1979 | |||
1980 | /* register v4l devices */ | ||
1981 | dev->video_dev = cx88_vdev_init(core,dev->pci, | ||
1982 | &cx8800_video_template,"video"); | ||
1983 | video_set_drvdata(dev->video_dev, dev); | ||
1984 | err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, | ||
1985 | video_nr[core->nr]); | ||
1986 | if (err < 0) { | ||
1987 | printk(KERN_ERR "%s/0: can't register video device\n", | ||
1988 | core->name); | ||
1989 | goto fail_unreg; | ||
1990 | } | ||
1991 | printk(KERN_INFO "%s/0: registered device %s [v4l2]\n", | ||
1992 | core->name, video_device_node_name(dev->video_dev)); | ||
1993 | |||
1994 | dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi"); | ||
1995 | video_set_drvdata(dev->vbi_dev, dev); | ||
1996 | err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, | ||
1997 | vbi_nr[core->nr]); | ||
1998 | if (err < 0) { | ||
1999 | printk(KERN_ERR "%s/0: can't register vbi device\n", | ||
2000 | core->name); | ||
2001 | goto fail_unreg; | ||
2002 | } | ||
2003 | printk(KERN_INFO "%s/0: registered device %s\n", | ||
2004 | core->name, video_device_node_name(dev->vbi_dev)); | ||
2005 | |||
2006 | if (core->board.radio.type == CX88_RADIO) { | ||
2007 | dev->radio_dev = cx88_vdev_init(core,dev->pci, | ||
2008 | &cx8800_radio_template,"radio"); | ||
2009 | video_set_drvdata(dev->radio_dev, dev); | ||
2010 | err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, | ||
2011 | radio_nr[core->nr]); | ||
2012 | if (err < 0) { | ||
2013 | printk(KERN_ERR "%s/0: can't register radio device\n", | ||
2014 | core->name); | ||
2015 | goto fail_unreg; | ||
2016 | } | ||
2017 | printk(KERN_INFO "%s/0: registered device %s\n", | ||
2018 | core->name, video_device_node_name(dev->radio_dev)); | ||
2019 | } | ||
2020 | |||
2021 | /* start tvaudio thread */ | ||
2022 | if (core->board.tuner_type != TUNER_ABSENT) { | ||
2023 | core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio"); | ||
2024 | if (IS_ERR(core->kthread)) { | ||
2025 | err = PTR_ERR(core->kthread); | ||
2026 | printk(KERN_ERR "%s/0: failed to create cx88 audio thread, err=%d\n", | ||
2027 | core->name, err); | ||
2028 | } | ||
2029 | } | ||
2030 | mutex_unlock(&core->lock); | ||
2031 | |||
2032 | return 0; | ||
2033 | |||
2034 | fail_unreg: | ||
2035 | cx8800_unregister_video(dev); | ||
2036 | free_irq(pci_dev->irq, dev); | ||
2037 | mutex_unlock(&core->lock); | ||
2038 | fail_core: | ||
2039 | cx88_core_put(core,dev->pci); | ||
2040 | fail_free: | ||
2041 | kfree(dev); | ||
2042 | return err; | ||
2043 | } | ||
2044 | |||
2045 | static void __devexit cx8800_finidev(struct pci_dev *pci_dev) | ||
2046 | { | ||
2047 | struct cx8800_dev *dev = pci_get_drvdata(pci_dev); | ||
2048 | struct cx88_core *core = dev->core; | ||
2049 | |||
2050 | /* stop thread */ | ||
2051 | if (core->kthread) { | ||
2052 | kthread_stop(core->kthread); | ||
2053 | core->kthread = NULL; | ||
2054 | } | ||
2055 | |||
2056 | if (core->ir) | ||
2057 | cx88_ir_stop(core); | ||
2058 | |||
2059 | cx88_shutdown(core); /* FIXME */ | ||
2060 | pci_disable_device(pci_dev); | ||
2061 | |||
2062 | /* unregister stuff */ | ||
2063 | |||
2064 | free_irq(pci_dev->irq, dev); | ||
2065 | cx8800_unregister_video(dev); | ||
2066 | pci_set_drvdata(pci_dev, NULL); | ||
2067 | |||
2068 | /* free memory */ | ||
2069 | btcx_riscmem_free(dev->pci,&dev->vidq.stopper); | ||
2070 | cx88_core_put(core,dev->pci); | ||
2071 | kfree(dev); | ||
2072 | } | ||
2073 | |||
2074 | #ifdef CONFIG_PM | ||
2075 | static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state) | ||
2076 | { | ||
2077 | struct cx8800_dev *dev = pci_get_drvdata(pci_dev); | ||
2078 | struct cx88_core *core = dev->core; | ||
2079 | |||
2080 | /* stop video+vbi capture */ | ||
2081 | spin_lock(&dev->slock); | ||
2082 | if (!list_empty(&dev->vidq.active)) { | ||
2083 | printk("%s/0: suspend video\n", core->name); | ||
2084 | stop_video_dma(dev); | ||
2085 | del_timer(&dev->vidq.timeout); | ||
2086 | } | ||
2087 | if (!list_empty(&dev->vbiq.active)) { | ||
2088 | printk("%s/0: suspend vbi\n", core->name); | ||
2089 | cx8800_stop_vbi_dma(dev); | ||
2090 | del_timer(&dev->vbiq.timeout); | ||
2091 | } | ||
2092 | spin_unlock(&dev->slock); | ||
2093 | |||
2094 | if (core->ir) | ||
2095 | cx88_ir_stop(core); | ||
2096 | /* FIXME -- shutdown device */ | ||
2097 | cx88_shutdown(core); | ||
2098 | |||
2099 | pci_save_state(pci_dev); | ||
2100 | if (0 != pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state))) { | ||
2101 | pci_disable_device(pci_dev); | ||
2102 | dev->state.disabled = 1; | ||
2103 | } | ||
2104 | return 0; | ||
2105 | } | ||
2106 | |||
2107 | static int cx8800_resume(struct pci_dev *pci_dev) | ||
2108 | { | ||
2109 | struct cx8800_dev *dev = pci_get_drvdata(pci_dev); | ||
2110 | struct cx88_core *core = dev->core; | ||
2111 | int err; | ||
2112 | |||
2113 | if (dev->state.disabled) { | ||
2114 | err=pci_enable_device(pci_dev); | ||
2115 | if (err) { | ||
2116 | printk(KERN_ERR "%s/0: can't enable device\n", | ||
2117 | core->name); | ||
2118 | return err; | ||
2119 | } | ||
2120 | |||
2121 | dev->state.disabled = 0; | ||
2122 | } | ||
2123 | err= pci_set_power_state(pci_dev, PCI_D0); | ||
2124 | if (err) { | ||
2125 | printk(KERN_ERR "%s/0: can't set power state\n", core->name); | ||
2126 | pci_disable_device(pci_dev); | ||
2127 | dev->state.disabled = 1; | ||
2128 | |||
2129 | return err; | ||
2130 | } | ||
2131 | pci_restore_state(pci_dev); | ||
2132 | |||
2133 | /* FIXME: re-initialize hardware */ | ||
2134 | cx88_reset(core); | ||
2135 | if (core->ir) | ||
2136 | cx88_ir_start(core); | ||
2137 | |||
2138 | cx_set(MO_PCI_INTMSK, core->pci_irqmask); | ||
2139 | |||
2140 | /* restart video+vbi capture */ | ||
2141 | spin_lock(&dev->slock); | ||
2142 | if (!list_empty(&dev->vidq.active)) { | ||
2143 | printk("%s/0: resume video\n", core->name); | ||
2144 | restart_video_queue(dev,&dev->vidq); | ||
2145 | } | ||
2146 | if (!list_empty(&dev->vbiq.active)) { | ||
2147 | printk("%s/0: resume vbi\n", core->name); | ||
2148 | cx8800_restart_vbi_queue(dev,&dev->vbiq); | ||
2149 | } | ||
2150 | spin_unlock(&dev->slock); | ||
2151 | |||
2152 | return 0; | ||
2153 | } | ||
2154 | #endif | ||
2155 | |||
2156 | /* ----------------------------------------------------------- */ | ||
2157 | |||
2158 | static const struct pci_device_id cx8800_pci_tbl[] = { | ||
2159 | { | ||
2160 | .vendor = 0x14f1, | ||
2161 | .device = 0x8800, | ||
2162 | .subvendor = PCI_ANY_ID, | ||
2163 | .subdevice = PCI_ANY_ID, | ||
2164 | },{ | ||
2165 | /* --- end of list --- */ | ||
2166 | } | ||
2167 | }; | ||
2168 | MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl); | ||
2169 | |||
2170 | static struct pci_driver cx8800_pci_driver = { | ||
2171 | .name = "cx8800", | ||
2172 | .id_table = cx8800_pci_tbl, | ||
2173 | .probe = cx8800_initdev, | ||
2174 | .remove = __devexit_p(cx8800_finidev), | ||
2175 | #ifdef CONFIG_PM | ||
2176 | .suspend = cx8800_suspend, | ||
2177 | .resume = cx8800_resume, | ||
2178 | #endif | ||
2179 | }; | ||
2180 | |||
2181 | static int __init cx8800_init(void) | ||
2182 | { | ||
2183 | printk(KERN_INFO "cx88/0: cx2388x v4l2 driver version %s loaded\n", | ||
2184 | CX88_VERSION); | ||
2185 | return pci_register_driver(&cx8800_pci_driver); | ||
2186 | } | ||
2187 | |||
2188 | static void __exit cx8800_fini(void) | ||
2189 | { | ||
2190 | pci_unregister_driver(&cx8800_pci_driver); | ||
2191 | } | ||
2192 | |||
2193 | module_init(cx8800_init); | ||
2194 | module_exit(cx8800_fini); | ||
diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.c b/drivers/media/video/cx88/cx88-vp3054-i2c.c new file mode 100644 index 00000000000..d77f8ecab9d --- /dev/null +++ b/drivers/media/video/cx88/cx88-vp3054-i2c.c | |||
@@ -0,0 +1,159 @@ | |||
1 | /* | ||
2 | |||
3 | cx88-vp3054-i2c.c -- support for the secondary I2C bus of the | ||
4 | DNTV Live! DVB-T Pro (VP-3054), wired as: | ||
5 | GPIO[0] -> SCL, GPIO[1] -> SDA | ||
6 | |||
7 | (c) 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> | ||
8 | |||
9 | This program is free software; you can redistribute it and/or modify | ||
10 | it under the terms of the GNU General Public License as published by | ||
11 | the Free Software Foundation; either version 2 of the License, or | ||
12 | (at your option) any later version. | ||
13 | |||
14 | This program is distributed in the hope that it will be useful, | ||
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | GNU General Public License for more details. | ||
18 | |||
19 | You should have received a copy of the GNU General Public License | ||
20 | along with this program; if not, write to the Free Software | ||
21 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | |||
23 | */ | ||
24 | |||
25 | #include <linux/module.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/init.h> | ||
28 | |||
29 | #include <asm/io.h> | ||
30 | |||
31 | #include "cx88.h" | ||
32 | #include "cx88-vp3054-i2c.h" | ||
33 | |||
34 | MODULE_DESCRIPTION("driver for cx2388x VP3054 design"); | ||
35 | MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); | ||
36 | MODULE_LICENSE("GPL"); | ||
37 | |||
38 | /* ----------------------------------------------------------------------- */ | ||
39 | |||
40 | static void vp3054_bit_setscl(void *data, int state) | ||
41 | { | ||
42 | struct cx8802_dev *dev = data; | ||
43 | struct cx88_core *core = dev->core; | ||
44 | struct vp3054_i2c_state *vp3054_i2c = dev->vp3054; | ||
45 | |||
46 | if (state) { | ||
47 | vp3054_i2c->state |= 0x0001; /* SCL high */ | ||
48 | vp3054_i2c->state &= ~0x0100; /* external pullup */ | ||
49 | } else { | ||
50 | vp3054_i2c->state &= ~0x0001; /* SCL low */ | ||
51 | vp3054_i2c->state |= 0x0100; /* drive pin */ | ||
52 | } | ||
53 | cx_write(MO_GP0_IO, 0x010000 | vp3054_i2c->state); | ||
54 | cx_read(MO_GP0_IO); | ||
55 | } | ||
56 | |||
57 | static void vp3054_bit_setsda(void *data, int state) | ||
58 | { | ||
59 | struct cx8802_dev *dev = data; | ||
60 | struct cx88_core *core = dev->core; | ||
61 | struct vp3054_i2c_state *vp3054_i2c = dev->vp3054; | ||
62 | |||
63 | if (state) { | ||
64 | vp3054_i2c->state |= 0x0002; /* SDA high */ | ||
65 | vp3054_i2c->state &= ~0x0200; /* tristate pin */ | ||
66 | } else { | ||
67 | vp3054_i2c->state &= ~0x0002; /* SDA low */ | ||
68 | vp3054_i2c->state |= 0x0200; /* drive pin */ | ||
69 | } | ||
70 | cx_write(MO_GP0_IO, 0x020000 | vp3054_i2c->state); | ||
71 | cx_read(MO_GP0_IO); | ||
72 | } | ||
73 | |||
74 | static int vp3054_bit_getscl(void *data) | ||
75 | { | ||
76 | struct cx8802_dev *dev = data; | ||
77 | struct cx88_core *core = dev->core; | ||
78 | u32 state; | ||
79 | |||
80 | state = cx_read(MO_GP0_IO); | ||
81 | return (state & 0x01) ? 1 : 0; | ||
82 | } | ||
83 | |||
84 | static int vp3054_bit_getsda(void *data) | ||
85 | { | ||
86 | struct cx8802_dev *dev = data; | ||
87 | struct cx88_core *core = dev->core; | ||
88 | u32 state; | ||
89 | |||
90 | state = cx_read(MO_GP0_IO); | ||
91 | return (state & 0x02) ? 1 : 0; | ||
92 | } | ||
93 | |||
94 | /* ----------------------------------------------------------------------- */ | ||
95 | |||
96 | static const struct i2c_algo_bit_data vp3054_i2c_algo_template = { | ||
97 | .setsda = vp3054_bit_setsda, | ||
98 | .setscl = vp3054_bit_setscl, | ||
99 | .getsda = vp3054_bit_getsda, | ||
100 | .getscl = vp3054_bit_getscl, | ||
101 | .udelay = 16, | ||
102 | .timeout = 200, | ||
103 | }; | ||
104 | |||
105 | /* ----------------------------------------------------------------------- */ | ||
106 | |||
107 | int vp3054_i2c_probe(struct cx8802_dev *dev) | ||
108 | { | ||
109 | struct cx88_core *core = dev->core; | ||
110 | struct vp3054_i2c_state *vp3054_i2c; | ||
111 | int rc; | ||
112 | |||
113 | if (core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO) | ||
114 | return 0; | ||
115 | |||
116 | vp3054_i2c = kzalloc(sizeof(*vp3054_i2c), GFP_KERNEL); | ||
117 | if (vp3054_i2c == NULL) | ||
118 | return -ENOMEM; | ||
119 | dev->vp3054 = vp3054_i2c; | ||
120 | |||
121 | memcpy(&vp3054_i2c->algo, &vp3054_i2c_algo_template, | ||
122 | sizeof(vp3054_i2c->algo)); | ||
123 | |||
124 | vp3054_i2c->adap.dev.parent = &dev->pci->dev; | ||
125 | strlcpy(vp3054_i2c->adap.name, core->name, | ||
126 | sizeof(vp3054_i2c->adap.name)); | ||
127 | vp3054_i2c->adap.owner = THIS_MODULE; | ||
128 | vp3054_i2c->algo.data = dev; | ||
129 | i2c_set_adapdata(&vp3054_i2c->adap, dev); | ||
130 | vp3054_i2c->adap.algo_data = &vp3054_i2c->algo; | ||
131 | |||
132 | vp3054_bit_setscl(dev,1); | ||
133 | vp3054_bit_setsda(dev,1); | ||
134 | |||
135 | rc = i2c_bit_add_bus(&vp3054_i2c->adap); | ||
136 | if (0 != rc) { | ||
137 | printk("%s: vp3054_i2c register FAILED\n", core->name); | ||
138 | |||
139 | kfree(dev->vp3054); | ||
140 | dev->vp3054 = NULL; | ||
141 | } | ||
142 | |||
143 | return rc; | ||
144 | } | ||
145 | |||
146 | void vp3054_i2c_remove(struct cx8802_dev *dev) | ||
147 | { | ||
148 | struct vp3054_i2c_state *vp3054_i2c = dev->vp3054; | ||
149 | |||
150 | if (vp3054_i2c == NULL || | ||
151 | dev->core->boardnr != CX88_BOARD_DNTV_LIVE_DVB_T_PRO) | ||
152 | return; | ||
153 | |||
154 | i2c_del_adapter(&vp3054_i2c->adap); | ||
155 | kfree(vp3054_i2c); | ||
156 | } | ||
157 | |||
158 | EXPORT_SYMBOL(vp3054_i2c_probe); | ||
159 | EXPORT_SYMBOL(vp3054_i2c_remove); | ||
diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.h b/drivers/media/video/cx88/cx88-vp3054-i2c.h new file mode 100644 index 00000000000..be99c931dc3 --- /dev/null +++ b/drivers/media/video/cx88/cx88-vp3054-i2c.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | |||
3 | cx88-vp3054-i2c.h -- support for the secondary I2C bus of the | ||
4 | DNTV Live! DVB-T Pro (VP-3054), wired as: | ||
5 | GPIO[0] -> SCL, GPIO[1] -> SDA | ||
6 | |||
7 | (c) 2005 Chris Pascoe <c.pascoe@itee.uq.edu.au> | ||
8 | |||
9 | This program is free software; you can redistribute it and/or modify | ||
10 | it under the terms of the GNU General Public License as published by | ||
11 | the Free Software Foundation; either version 2 of the License, or | ||
12 | (at your option) any later version. | ||
13 | |||
14 | This program is distributed in the hope that it will be useful, | ||
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | GNU General Public License for more details. | ||
18 | |||
19 | You should have received a copy of the GNU General Public License | ||
20 | along with this program; if not, write to the Free Software | ||
21 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | |||
23 | */ | ||
24 | |||
25 | /* ----------------------------------------------------------------------- */ | ||
26 | struct vp3054_i2c_state { | ||
27 | struct i2c_adapter adap; | ||
28 | struct i2c_algo_bit_data algo; | ||
29 | u32 state; | ||
30 | }; | ||
31 | |||
32 | /* ----------------------------------------------------------------------- */ | ||
33 | #if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) | ||
34 | int vp3054_i2c_probe(struct cx8802_dev *dev); | ||
35 | void vp3054_i2c_remove(struct cx8802_dev *dev); | ||
36 | #else | ||
37 | static inline int vp3054_i2c_probe(struct cx8802_dev *dev) | ||
38 | { return 0; } | ||
39 | static inline void vp3054_i2c_remove(struct cx8802_dev *dev) | ||
40 | { } | ||
41 | #endif | ||
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h new file mode 100644 index 00000000000..fa8d307e1a3 --- /dev/null +++ b/drivers/media/video/cx88/cx88.h | |||
@@ -0,0 +1,730 @@ | |||
1 | /* | ||
2 | * | ||
3 | * v4l2 device driver for cx2388x based TV cards | ||
4 | * | ||
5 | * (c) 2003,04 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs] | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/pci.h> | ||
23 | #include <linux/i2c.h> | ||
24 | #include <linux/i2c-algo-bit.h> | ||
25 | #include <linux/videodev2.h> | ||
26 | #include <linux/kdev_t.h> | ||
27 | |||
28 | #include <media/v4l2-device.h> | ||
29 | #include <media/tuner.h> | ||
30 | #include <media/tveeprom.h> | ||
31 | #include <media/videobuf-dma-sg.h> | ||
32 | #include <media/v4l2-chip-ident.h> | ||
33 | #include <media/cx2341x.h> | ||
34 | #include <media/videobuf-dvb.h> | ||
35 | #include <media/ir-kbd-i2c.h> | ||
36 | #include <media/wm8775.h> | ||
37 | |||
38 | #include "btcx-risc.h" | ||
39 | #include "cx88-reg.h" | ||
40 | #include "tuner-xc2028.h" | ||
41 | |||
42 | #include <linux/mutex.h> | ||
43 | |||
44 | #define CX88_VERSION "0.0.9" | ||
45 | |||
46 | #define UNSET (-1U) | ||
47 | |||
48 | #define CX88_MAXBOARDS 8 | ||
49 | |||
50 | /* Max number of inputs by card */ | ||
51 | #define MAX_CX88_INPUT 8 | ||
52 | |||
53 | /* ----------------------------------------------------------- */ | ||
54 | /* defines and enums */ | ||
55 | |||
56 | /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM/LC */ | ||
57 | #define CX88_NORMS (V4L2_STD_ALL \ | ||
58 | & ~V4L2_STD_PAL_H \ | ||
59 | & ~V4L2_STD_NTSC_M_KR \ | ||
60 | & ~V4L2_STD_SECAM_LC) | ||
61 | |||
62 | #define FORMAT_FLAGS_PACKED 0x01 | ||
63 | #define FORMAT_FLAGS_PLANAR 0x02 | ||
64 | |||
65 | #define VBI_LINE_COUNT 17 | ||
66 | #define VBI_LINE_LENGTH 2048 | ||
67 | |||
68 | #define AUD_RDS_LINES 4 | ||
69 | |||
70 | /* need "shadow" registers for some write-only ones ... */ | ||
71 | #define SHADOW_AUD_VOL_CTL 1 | ||
72 | #define SHADOW_AUD_BAL_CTL 2 | ||
73 | #define SHADOW_MAX 3 | ||
74 | |||
75 | /* FM Radio deemphasis type */ | ||
76 | enum cx88_deemph_type { | ||
77 | FM_NO_DEEMPH = 0, | ||
78 | FM_DEEMPH_50, | ||
79 | FM_DEEMPH_75 | ||
80 | }; | ||
81 | |||
82 | enum cx88_board_type { | ||
83 | CX88_BOARD_NONE = 0, | ||
84 | CX88_MPEG_DVB, | ||
85 | CX88_MPEG_BLACKBIRD | ||
86 | }; | ||
87 | |||
88 | enum cx8802_board_access { | ||
89 | CX8802_DRVCTL_SHARED = 1, | ||
90 | CX8802_DRVCTL_EXCLUSIVE = 2, | ||
91 | }; | ||
92 | |||
93 | /* ----------------------------------------------------------- */ | ||
94 | /* tv norms */ | ||
95 | |||
96 | static unsigned int inline norm_maxw(v4l2_std_id norm) | ||
97 | { | ||
98 | return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768; | ||
99 | } | ||
100 | |||
101 | |||
102 | static unsigned int inline norm_maxh(v4l2_std_id norm) | ||
103 | { | ||
104 | return (norm & V4L2_STD_625_50) ? 576 : 480; | ||
105 | } | ||
106 | |||
107 | /* ----------------------------------------------------------- */ | ||
108 | /* static data */ | ||
109 | |||
110 | struct cx8800_fmt { | ||
111 | const char *name; | ||
112 | u32 fourcc; /* v4l2 format id */ | ||
113 | int depth; | ||
114 | int flags; | ||
115 | u32 cxformat; | ||
116 | }; | ||
117 | |||
118 | struct cx88_ctrl { | ||
119 | struct v4l2_queryctrl v; | ||
120 | u32 off; | ||
121 | u32 reg; | ||
122 | u32 sreg; | ||
123 | u32 mask; | ||
124 | u32 shift; | ||
125 | }; | ||
126 | |||
127 | /* ----------------------------------------------------------- */ | ||
128 | /* SRAM memory management data (see cx88-core.c) */ | ||
129 | |||
130 | #define SRAM_CH21 0 /* video */ | ||
131 | #define SRAM_CH22 1 | ||
132 | #define SRAM_CH23 2 | ||
133 | #define SRAM_CH24 3 /* vbi */ | ||
134 | #define SRAM_CH25 4 /* audio */ | ||
135 | #define SRAM_CH26 5 | ||
136 | #define SRAM_CH28 6 /* mpeg */ | ||
137 | #define SRAM_CH27 7 /* audio rds */ | ||
138 | /* more */ | ||
139 | |||
140 | struct sram_channel { | ||
141 | const char *name; | ||
142 | u32 cmds_start; | ||
143 | u32 ctrl_start; | ||
144 | u32 cdt; | ||
145 | u32 fifo_start; | ||
146 | u32 fifo_size; | ||
147 | u32 ptr1_reg; | ||
148 | u32 ptr2_reg; | ||
149 | u32 cnt1_reg; | ||
150 | u32 cnt2_reg; | ||
151 | }; | ||
152 | extern const struct sram_channel const cx88_sram_channels[]; | ||
153 | |||
154 | /* ----------------------------------------------------------- */ | ||
155 | /* card configuration */ | ||
156 | |||
157 | #define CX88_BOARD_NOAUTO UNSET | ||
158 | #define CX88_BOARD_UNKNOWN 0 | ||
159 | #define CX88_BOARD_HAUPPAUGE 1 | ||
160 | #define CX88_BOARD_GDI 2 | ||
161 | #define CX88_BOARD_PIXELVIEW 3 | ||
162 | #define CX88_BOARD_ATI_WONDER_PRO 4 | ||
163 | #define CX88_BOARD_WINFAST2000XP_EXPERT 5 | ||
164 | #define CX88_BOARD_AVERTV_STUDIO_303 6 | ||
165 | #define CX88_BOARD_MSI_TVANYWHERE_MASTER 7 | ||
166 | #define CX88_BOARD_WINFAST_DV2000 8 | ||
167 | #define CX88_BOARD_LEADTEK_PVR2000 9 | ||
168 | #define CX88_BOARD_IODATA_GVVCP3PCI 10 | ||
169 | #define CX88_BOARD_PROLINK_PLAYTVPVR 11 | ||
170 | #define CX88_BOARD_ASUS_PVR_416 12 | ||
171 | #define CX88_BOARD_MSI_TVANYWHERE 13 | ||
172 | #define CX88_BOARD_KWORLD_DVB_T 14 | ||
173 | #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1 15 | ||
174 | #define CX88_BOARD_KWORLD_LTV883 16 | ||
175 | #define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q 17 | ||
176 | #define CX88_BOARD_HAUPPAUGE_DVB_T1 18 | ||
177 | #define CX88_BOARD_CONEXANT_DVB_T1 19 | ||
178 | #define CX88_BOARD_PROVIDEO_PV259 20 | ||
179 | #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS 21 | ||
180 | #define CX88_BOARD_PCHDTV_HD3000 22 | ||
181 | #define CX88_BOARD_DNTV_LIVE_DVB_T 23 | ||
182 | #define CX88_BOARD_HAUPPAUGE_ROSLYN 24 | ||
183 | #define CX88_BOARD_DIGITALLOGIC_MEC 25 | ||
184 | #define CX88_BOARD_IODATA_GVBCTV7E 26 | ||
185 | #define CX88_BOARD_PIXELVIEW_PLAYTV_ULTRA_PRO 27 | ||
186 | #define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_T 28 | ||
187 | #define CX88_BOARD_ADSTECH_DVB_T_PCI 29 | ||
188 | #define CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1 30 | ||
189 | #define CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD 31 | ||
190 | #define CX88_BOARD_AVERMEDIA_ULTRATV_MC_550 32 | ||
191 | #define CX88_BOARD_KWORLD_VSTREAM_EXPERT_DVD 33 | ||
192 | #define CX88_BOARD_ATI_HDTVWONDER 34 | ||
193 | #define CX88_BOARD_WINFAST_DTV1000 35 | ||
194 | #define CX88_BOARD_AVERTV_303 36 | ||
195 | #define CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1 37 | ||
196 | #define CX88_BOARD_HAUPPAUGE_NOVASE2_S1 38 | ||
197 | #define CX88_BOARD_KWORLD_DVBS_100 39 | ||
198 | #define CX88_BOARD_HAUPPAUGE_HVR1100 40 | ||
199 | #define CX88_BOARD_HAUPPAUGE_HVR1100LP 41 | ||
200 | #define CX88_BOARD_DNTV_LIVE_DVB_T_PRO 42 | ||
201 | #define CX88_BOARD_KWORLD_DVB_T_CX22702 43 | ||
202 | #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL 44 | ||
203 | #define CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT 45 | ||
204 | #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID 46 | ||
205 | #define CX88_BOARD_PCHDTV_HD5500 47 | ||
206 | #define CX88_BOARD_KWORLD_MCE200_DELUXE 48 | ||
207 | #define CX88_BOARD_PIXELVIEW_PLAYTV_P7000 49 | ||
208 | #define CX88_BOARD_NPGTECH_REALTV_TOP10FM 50 | ||
209 | #define CX88_BOARD_WINFAST_DTV2000H 51 | ||
210 | #define CX88_BOARD_GENIATECH_DVBS 52 | ||
211 | #define CX88_BOARD_HAUPPAUGE_HVR3000 53 | ||
212 | #define CX88_BOARD_NORWOOD_MICRO 54 | ||
213 | #define CX88_BOARD_TE_DTV_250_OEM_SWANN 55 | ||
214 | #define CX88_BOARD_HAUPPAUGE_HVR1300 56 | ||
215 | #define CX88_BOARD_ADSTECH_PTV_390 57 | ||
216 | #define CX88_BOARD_PINNACLE_PCTV_HD_800i 58 | ||
217 | #define CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO 59 | ||
218 | #define CX88_BOARD_PINNACLE_HYBRID_PCTV 60 | ||
219 | #define CX88_BOARD_WINFAST_TV2000_XP_GLOBAL 61 | ||
220 | #define CX88_BOARD_POWERCOLOR_REAL_ANGEL 62 | ||
221 | #define CX88_BOARD_GENIATECH_X8000_MT 63 | ||
222 | #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64 | ||
223 | #define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65 | ||
224 | #define CX88_BOARD_PROLINK_PV_8000GT 66 | ||
225 | #define CX88_BOARD_KWORLD_ATSC_120 67 | ||
226 | #define CX88_BOARD_HAUPPAUGE_HVR4000 68 | ||
227 | #define CX88_BOARD_HAUPPAUGE_HVR4000LITE 69 | ||
228 | #define CX88_BOARD_TEVII_S460 70 | ||
229 | #define CX88_BOARD_OMICOM_SS4_PCI 71 | ||
230 | #define CX88_BOARD_TBS_8920 72 | ||
231 | #define CX88_BOARD_TEVII_S420 73 | ||
232 | #define CX88_BOARD_PROLINK_PV_GLOBAL_XTREME 74 | ||
233 | #define CX88_BOARD_PROF_7300 75 | ||
234 | #define CX88_BOARD_SATTRADE_ST4200 76 | ||
235 | #define CX88_BOARD_TBS_8910 77 | ||
236 | #define CX88_BOARD_PROF_6200 78 | ||
237 | #define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79 | ||
238 | #define CX88_BOARD_HAUPPAUGE_IRONLY 80 | ||
239 | #define CX88_BOARD_WINFAST_DTV1800H 81 | ||
240 | #define CX88_BOARD_WINFAST_DTV2000H_J 82 | ||
241 | #define CX88_BOARD_PROF_7301 83 | ||
242 | #define CX88_BOARD_SAMSUNG_SMT_7020 84 | ||
243 | #define CX88_BOARD_TWINHAN_VP1027_DVBS 85 | ||
244 | #define CX88_BOARD_TEVII_S464 86 | ||
245 | #define CX88_BOARD_WINFAST_DTV2000H_PLUS 87 | ||
246 | #define CX88_BOARD_WINFAST_DTV1800H_XC4000 88 | ||
247 | |||
248 | enum cx88_itype { | ||
249 | CX88_VMUX_COMPOSITE1 = 1, | ||
250 | CX88_VMUX_COMPOSITE2, | ||
251 | CX88_VMUX_COMPOSITE3, | ||
252 | CX88_VMUX_COMPOSITE4, | ||
253 | CX88_VMUX_SVIDEO, | ||
254 | CX88_VMUX_TELEVISION, | ||
255 | CX88_VMUX_CABLE, | ||
256 | CX88_VMUX_DVB, | ||
257 | CX88_VMUX_DEBUG, | ||
258 | CX88_RADIO, | ||
259 | }; | ||
260 | |||
261 | struct cx88_input { | ||
262 | enum cx88_itype type; | ||
263 | u32 gpio0, gpio1, gpio2, gpio3; | ||
264 | unsigned int vmux:2; | ||
265 | unsigned int audioroute:4; | ||
266 | }; | ||
267 | |||
268 | struct cx88_board { | ||
269 | const char *name; | ||
270 | unsigned int tuner_type; | ||
271 | unsigned int radio_type; | ||
272 | unsigned char tuner_addr; | ||
273 | unsigned char radio_addr; | ||
274 | int tda9887_conf; | ||
275 | struct cx88_input input[MAX_CX88_INPUT]; | ||
276 | struct cx88_input radio; | ||
277 | enum cx88_board_type mpeg; | ||
278 | unsigned int audio_chip; | ||
279 | int num_frontends; | ||
280 | |||
281 | /* Used for I2S devices */ | ||
282 | int i2sinputcntl; | ||
283 | }; | ||
284 | |||
285 | struct cx88_subid { | ||
286 | u16 subvendor; | ||
287 | u16 subdevice; | ||
288 | u32 card; | ||
289 | }; | ||
290 | |||
291 | enum cx88_tvaudio { | ||
292 | WW_NONE = 1, | ||
293 | WW_BTSC, | ||
294 | WW_BG, | ||
295 | WW_DK, | ||
296 | WW_I, | ||
297 | WW_L, | ||
298 | WW_EIAJ, | ||
299 | WW_I2SPT, | ||
300 | WW_FM, | ||
301 | WW_I2SADC, | ||
302 | WW_M | ||
303 | }; | ||
304 | |||
305 | #define INPUT(nr) (core->board.input[nr]) | ||
306 | |||
307 | /* ----------------------------------------------------------- */ | ||
308 | /* device / file handle status */ | ||
309 | |||
310 | #define RESOURCE_OVERLAY 1 | ||
311 | #define RESOURCE_VIDEO 2 | ||
312 | #define RESOURCE_VBI 4 | ||
313 | |||
314 | #define BUFFER_TIMEOUT msecs_to_jiffies(2000) | ||
315 | |||
316 | /* buffer for one video frame */ | ||
317 | struct cx88_buffer { | ||
318 | /* common v4l buffer stuff -- must be first */ | ||
319 | struct videobuf_buffer vb; | ||
320 | |||
321 | /* cx88 specific */ | ||
322 | unsigned int bpl; | ||
323 | struct btcx_riscmem risc; | ||
324 | const struct cx8800_fmt *fmt; | ||
325 | u32 count; | ||
326 | }; | ||
327 | |||
328 | struct cx88_dmaqueue { | ||
329 | struct list_head active; | ||
330 | struct list_head queued; | ||
331 | struct timer_list timeout; | ||
332 | struct btcx_riscmem stopper; | ||
333 | u32 count; | ||
334 | }; | ||
335 | |||
336 | struct cx88_core { | ||
337 | struct list_head devlist; | ||
338 | atomic_t refcount; | ||
339 | |||
340 | /* board name */ | ||
341 | int nr; | ||
342 | char name[32]; | ||
343 | |||
344 | /* pci stuff */ | ||
345 | int pci_bus; | ||
346 | int pci_slot; | ||
347 | u32 __iomem *lmmio; | ||
348 | u8 __iomem *bmmio; | ||
349 | u32 shadow[SHADOW_MAX]; | ||
350 | int pci_irqmask; | ||
351 | |||
352 | /* i2c i/o */ | ||
353 | struct i2c_adapter i2c_adap; | ||
354 | struct i2c_algo_bit_data i2c_algo; | ||
355 | struct i2c_client i2c_client; | ||
356 | u32 i2c_state, i2c_rc; | ||
357 | |||
358 | /* config info -- analog */ | ||
359 | struct v4l2_device v4l2_dev; | ||
360 | struct i2c_client *i2c_rtc; | ||
361 | unsigned int boardnr; | ||
362 | struct cx88_board board; | ||
363 | |||
364 | /* Supported V4L _STD_ tuner formats */ | ||
365 | unsigned int tuner_formats; | ||
366 | |||
367 | /* config info -- dvb */ | ||
368 | #if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) | ||
369 | int (*prev_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); | ||
370 | #endif | ||
371 | void (*gate_ctrl)(struct cx88_core *core, int open); | ||
372 | |||
373 | /* state info */ | ||
374 | struct task_struct *kthread; | ||
375 | v4l2_std_id tvnorm; | ||
376 | enum cx88_tvaudio tvaudio; | ||
377 | u32 audiomode_manual; | ||
378 | u32 audiomode_current; | ||
379 | u32 input; | ||
380 | u32 last_analog_input; | ||
381 | u32 astat; | ||
382 | u32 use_nicam; | ||
383 | unsigned long last_change; | ||
384 | |||
385 | /* IR remote control state */ | ||
386 | struct cx88_IR *ir; | ||
387 | |||
388 | /* I2C remote data */ | ||
389 | struct IR_i2c_init_data init_data; | ||
390 | struct wm8775_platform_data wm8775_data; | ||
391 | |||
392 | struct mutex lock; | ||
393 | /* various v4l controls */ | ||
394 | u32 freq; | ||
395 | int users; | ||
396 | int mpeg_users; | ||
397 | |||
398 | /* cx88-video needs to access cx8802 for hybrid tuner pll access. */ | ||
399 | struct cx8802_dev *dvbdev; | ||
400 | enum cx88_board_type active_type_id; | ||
401 | int active_ref; | ||
402 | int active_fe_id; | ||
403 | }; | ||
404 | |||
405 | static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev) | ||
406 | { | ||
407 | return container_of(v4l2_dev, struct cx88_core, v4l2_dev); | ||
408 | } | ||
409 | |||
410 | #define WM8775_GID (1 << 0) | ||
411 | |||
412 | #define call_hw(core, grpid, o, f, args...) \ | ||
413 | do { \ | ||
414 | if (!core->i2c_rc) { \ | ||
415 | if (core->gate_ctrl) \ | ||
416 | core->gate_ctrl(core, 1); \ | ||
417 | v4l2_device_call_all(&core->v4l2_dev, grpid, o, f, ##args); \ | ||
418 | if (core->gate_ctrl) \ | ||
419 | core->gate_ctrl(core, 0); \ | ||
420 | } \ | ||
421 | } while (0) | ||
422 | |||
423 | #define call_all(core, o, f, args...) call_hw(core, 0, o, f, ##args) | ||
424 | |||
425 | struct cx8800_dev; | ||
426 | struct cx8802_dev; | ||
427 | |||
428 | /* ----------------------------------------------------------- */ | ||
429 | /* function 0: video stuff */ | ||
430 | |||
431 | struct cx8800_fh { | ||
432 | struct cx8800_dev *dev; | ||
433 | enum v4l2_buf_type type; | ||
434 | int radio; | ||
435 | unsigned int resources; | ||
436 | |||
437 | /* video overlay */ | ||
438 | struct v4l2_window win; | ||
439 | struct v4l2_clip *clips; | ||
440 | unsigned int nclips; | ||
441 | |||
442 | /* video capture */ | ||
443 | const struct cx8800_fmt *fmt; | ||
444 | unsigned int width,height; | ||
445 | struct videobuf_queue vidq; | ||
446 | |||
447 | /* vbi capture */ | ||
448 | struct videobuf_queue vbiq; | ||
449 | }; | ||
450 | |||
451 | struct cx8800_suspend_state { | ||
452 | int disabled; | ||
453 | }; | ||
454 | |||
455 | struct cx8800_dev { | ||
456 | struct cx88_core *core; | ||
457 | spinlock_t slock; | ||
458 | |||
459 | /* various device info */ | ||
460 | unsigned int resources; | ||
461 | struct video_device *video_dev; | ||
462 | struct video_device *vbi_dev; | ||
463 | struct video_device *radio_dev; | ||
464 | |||
465 | /* pci i/o */ | ||
466 | struct pci_dev *pci; | ||
467 | unsigned char pci_rev,pci_lat; | ||
468 | |||
469 | |||
470 | /* capture queues */ | ||
471 | struct cx88_dmaqueue vidq; | ||
472 | struct cx88_dmaqueue vbiq; | ||
473 | |||
474 | /* various v4l controls */ | ||
475 | |||
476 | /* other global state info */ | ||
477 | struct cx8800_suspend_state state; | ||
478 | }; | ||
479 | |||
480 | /* ----------------------------------------------------------- */ | ||
481 | /* function 1: audio/alsa stuff */ | ||
482 | /* =============> moved to cx88-alsa.c <====================== */ | ||
483 | |||
484 | |||
485 | /* ----------------------------------------------------------- */ | ||
486 | /* function 2: mpeg stuff */ | ||
487 | |||
488 | struct cx8802_fh { | ||
489 | struct cx8802_dev *dev; | ||
490 | struct videobuf_queue mpegq; | ||
491 | }; | ||
492 | |||
493 | struct cx8802_suspend_state { | ||
494 | int disabled; | ||
495 | }; | ||
496 | |||
497 | struct cx8802_driver { | ||
498 | struct cx88_core *core; | ||
499 | |||
500 | /* List of drivers attached to device */ | ||
501 | struct list_head drvlist; | ||
502 | |||
503 | /* Type of driver and access required */ | ||
504 | enum cx88_board_type type_id; | ||
505 | enum cx8802_board_access hw_access; | ||
506 | |||
507 | /* MPEG 8802 internal only */ | ||
508 | int (*suspend)(struct pci_dev *pci_dev, pm_message_t state); | ||
509 | int (*resume)(struct pci_dev *pci_dev); | ||
510 | |||
511 | /* Callers to the following functions must hold core->lock */ | ||
512 | |||
513 | /* MPEG 8802 -> mini driver - Driver probe and configuration */ | ||
514 | int (*probe)(struct cx8802_driver *drv); | ||
515 | int (*remove)(struct cx8802_driver *drv); | ||
516 | |||
517 | /* MPEG 8802 -> mini driver - Access for hardware control */ | ||
518 | int (*advise_acquire)(struct cx8802_driver *drv); | ||
519 | int (*advise_release)(struct cx8802_driver *drv); | ||
520 | |||
521 | /* MPEG 8802 <- mini driver - Access for hardware control */ | ||
522 | int (*request_acquire)(struct cx8802_driver *drv); | ||
523 | int (*request_release)(struct cx8802_driver *drv); | ||
524 | }; | ||
525 | |||
526 | struct cx8802_dev { | ||
527 | struct cx88_core *core; | ||
528 | spinlock_t slock; | ||
529 | |||
530 | /* pci i/o */ | ||
531 | struct pci_dev *pci; | ||
532 | unsigned char pci_rev,pci_lat; | ||
533 | |||
534 | /* dma queues */ | ||
535 | struct cx88_dmaqueue mpegq; | ||
536 | u32 ts_packet_size; | ||
537 | u32 ts_packet_count; | ||
538 | |||
539 | /* other global state info */ | ||
540 | struct cx8802_suspend_state state; | ||
541 | |||
542 | /* for blackbird only */ | ||
543 | struct list_head devlist; | ||
544 | #if defined(CONFIG_VIDEO_CX88_BLACKBIRD) || \ | ||
545 | defined(CONFIG_VIDEO_CX88_BLACKBIRD_MODULE) | ||
546 | struct video_device *mpeg_dev; | ||
547 | u32 mailbox; | ||
548 | int width; | ||
549 | int height; | ||
550 | unsigned char mpeg_active; /* nonzero if mpeg encoder is active */ | ||
551 | |||
552 | /* mpeg params */ | ||
553 | struct cx2341x_mpeg_params params; | ||
554 | #endif | ||
555 | |||
556 | #if defined(CONFIG_VIDEO_CX88_DVB) || defined(CONFIG_VIDEO_CX88_DVB_MODULE) | ||
557 | /* for dvb only */ | ||
558 | struct videobuf_dvb_frontends frontends; | ||
559 | #endif | ||
560 | |||
561 | #if defined(CONFIG_VIDEO_CX88_VP3054) || \ | ||
562 | defined(CONFIG_VIDEO_CX88_VP3054_MODULE) | ||
563 | /* For VP3045 secondary I2C bus support */ | ||
564 | struct vp3054_i2c_state *vp3054; | ||
565 | #endif | ||
566 | /* for switching modulation types */ | ||
567 | unsigned char ts_gen_cntrl; | ||
568 | |||
569 | /* List of attached drivers; must hold core->lock to access */ | ||
570 | struct list_head drvlist; | ||
571 | |||
572 | struct work_struct request_module_wk; | ||
573 | }; | ||
574 | |||
575 | /* ----------------------------------------------------------- */ | ||
576 | |||
577 | #define cx_read(reg) readl(core->lmmio + ((reg)>>2)) | ||
578 | #define cx_write(reg,value) writel((value), core->lmmio + ((reg)>>2)) | ||
579 | #define cx_writeb(reg,value) writeb((value), core->bmmio + (reg)) | ||
580 | |||
581 | #define cx_andor(reg,mask,value) \ | ||
582 | writel((readl(core->lmmio+((reg)>>2)) & ~(mask)) |\ | ||
583 | ((value) & (mask)), core->lmmio+((reg)>>2)) | ||
584 | #define cx_set(reg,bit) cx_andor((reg),(bit),(bit)) | ||
585 | #define cx_clear(reg,bit) cx_andor((reg),(bit),0) | ||
586 | |||
587 | #define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); } | ||
588 | |||
589 | /* shadow registers */ | ||
590 | #define cx_sread(sreg) (core->shadow[sreg]) | ||
591 | #define cx_swrite(sreg,reg,value) \ | ||
592 | (core->shadow[sreg] = value, \ | ||
593 | writel(core->shadow[sreg], core->lmmio + ((reg)>>2))) | ||
594 | #define cx_sandor(sreg,reg,mask,value) \ | ||
595 | (core->shadow[sreg] = (core->shadow[sreg] & ~(mask)) | ((value) & (mask)), \ | ||
596 | writel(core->shadow[sreg], core->lmmio + ((reg)>>2))) | ||
597 | |||
598 | /* ----------------------------------------------------------- */ | ||
599 | /* cx88-core.c */ | ||
600 | |||
601 | extern void cx88_print_irqbits(const char *name, const char *tag, const char *strings[], | ||
602 | int len, u32 bits, u32 mask); | ||
603 | |||
604 | extern int cx88_core_irq(struct cx88_core *core, u32 status); | ||
605 | extern void cx88_wakeup(struct cx88_core *core, | ||
606 | struct cx88_dmaqueue *q, u32 count); | ||
607 | extern void cx88_shutdown(struct cx88_core *core); | ||
608 | extern int cx88_reset(struct cx88_core *core); | ||
609 | |||
610 | extern int | ||
611 | cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
612 | struct scatterlist *sglist, | ||
613 | unsigned int top_offset, unsigned int bottom_offset, | ||
614 | unsigned int bpl, unsigned int padding, unsigned int lines); | ||
615 | extern int | ||
616 | cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
617 | struct scatterlist *sglist, unsigned int bpl, | ||
618 | unsigned int lines, unsigned int lpi); | ||
619 | extern int | ||
620 | cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, | ||
621 | u32 reg, u32 mask, u32 value); | ||
622 | extern void | ||
623 | cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf); | ||
624 | |||
625 | extern void cx88_risc_disasm(struct cx88_core *core, | ||
626 | struct btcx_riscmem *risc); | ||
627 | extern int cx88_sram_channel_setup(struct cx88_core *core, | ||
628 | const struct sram_channel *ch, | ||
629 | unsigned int bpl, u32 risc); | ||
630 | extern void cx88_sram_channel_dump(struct cx88_core *core, | ||
631 | const struct sram_channel *ch); | ||
632 | |||
633 | extern int cx88_set_scale(struct cx88_core *core, unsigned int width, | ||
634 | unsigned int height, enum v4l2_field field); | ||
635 | extern int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm); | ||
636 | |||
637 | extern struct video_device *cx88_vdev_init(struct cx88_core *core, | ||
638 | struct pci_dev *pci, | ||
639 | const struct video_device *template_, | ||
640 | const char *type); | ||
641 | extern struct cx88_core* cx88_core_get(struct pci_dev *pci); | ||
642 | extern void cx88_core_put(struct cx88_core *core, | ||
643 | struct pci_dev *pci); | ||
644 | |||
645 | extern int cx88_start_audio_dma(struct cx88_core *core); | ||
646 | extern int cx88_stop_audio_dma(struct cx88_core *core); | ||
647 | |||
648 | |||
649 | /* ----------------------------------------------------------- */ | ||
650 | /* cx88-vbi.c */ | ||
651 | |||
652 | /* Can be used as g_vbi_fmt, try_vbi_fmt and s_vbi_fmt */ | ||
653 | int cx8800_vbi_fmt (struct file *file, void *priv, | ||
654 | struct v4l2_format *f); | ||
655 | |||
656 | /* | ||
657 | int cx8800_start_vbi_dma(struct cx8800_dev *dev, | ||
658 | struct cx88_dmaqueue *q, | ||
659 | struct cx88_buffer *buf); | ||
660 | */ | ||
661 | int cx8800_stop_vbi_dma(struct cx8800_dev *dev); | ||
662 | int cx8800_restart_vbi_queue(struct cx8800_dev *dev, | ||
663 | struct cx88_dmaqueue *q); | ||
664 | void cx8800_vbi_timeout(unsigned long data); | ||
665 | |||
666 | extern const struct videobuf_queue_ops cx8800_vbi_qops; | ||
667 | |||
668 | /* ----------------------------------------------------------- */ | ||
669 | /* cx88-i2c.c */ | ||
670 | |||
671 | extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci); | ||
672 | |||
673 | |||
674 | /* ----------------------------------------------------------- */ | ||
675 | /* cx88-cards.c */ | ||
676 | |||
677 | extern int cx88_tuner_callback(void *dev, int component, int command, int arg); | ||
678 | extern int cx88_get_resources(const struct cx88_core *core, | ||
679 | struct pci_dev *pci); | ||
680 | extern struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr); | ||
681 | extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl); | ||
682 | |||
683 | /* ----------------------------------------------------------- */ | ||
684 | /* cx88-tvaudio.c */ | ||
685 | |||
686 | void cx88_set_tvaudio(struct cx88_core *core); | ||
687 | void cx88_newstation(struct cx88_core *core); | ||
688 | void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t); | ||
689 | void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual); | ||
690 | int cx88_audio_thread(void *data); | ||
691 | |||
692 | int cx8802_register_driver(struct cx8802_driver *drv); | ||
693 | int cx8802_unregister_driver(struct cx8802_driver *drv); | ||
694 | |||
695 | /* Caller must hold core->lock */ | ||
696 | struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype); | ||
697 | |||
698 | /* ----------------------------------------------------------- */ | ||
699 | /* cx88-dsp.c */ | ||
700 | |||
701 | s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core); | ||
702 | |||
703 | /* ----------------------------------------------------------- */ | ||
704 | /* cx88-input.c */ | ||
705 | |||
706 | int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci); | ||
707 | int cx88_ir_fini(struct cx88_core *core); | ||
708 | void cx88_ir_irq(struct cx88_core *core); | ||
709 | int cx88_ir_start(struct cx88_core *core); | ||
710 | void cx88_ir_stop(struct cx88_core *core); | ||
711 | extern void cx88_i2c_init_ir(struct cx88_core *core); | ||
712 | |||
713 | /* ----------------------------------------------------------- */ | ||
714 | /* cx88-mpeg.c */ | ||
715 | |||
716 | int cx8802_buf_prepare(struct videobuf_queue *q,struct cx8802_dev *dev, | ||
717 | struct cx88_buffer *buf, enum v4l2_field field); | ||
718 | void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf); | ||
719 | void cx8802_cancel_buffers(struct cx8802_dev *dev); | ||
720 | |||
721 | /* ----------------------------------------------------------- */ | ||
722 | /* cx88-video.c*/ | ||
723 | extern const u32 cx88_user_ctrls[]; | ||
724 | extern int cx8800_ctrl_query(struct cx88_core *core, | ||
725 | struct v4l2_queryctrl *qctrl); | ||
726 | int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i); | ||
727 | int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f); | ||
728 | int cx88_get_control(struct cx88_core *core, struct v4l2_control *ctl); | ||
729 | int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl); | ||
730 | int cx88_video_mux(struct cx88_core *core, unsigned int input); | ||