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/cx18 | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/media/video/cx18')
51 files changed, 15283 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig new file mode 100644 index 00000000000..53b3c770257 --- /dev/null +++ b/drivers/media/video/cx18/Kconfig | |||
@@ -0,0 +1,35 @@ | |||
1 | config VIDEO_CX18 | ||
2 | tristate "Conexant cx23418 MPEG encoder support" | ||
3 | depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL | ||
4 | select I2C_ALGOBIT | ||
5 | select VIDEOBUF_VMALLOC | ||
6 | depends on RC_CORE | ||
7 | select VIDEO_TUNER | ||
8 | select VIDEO_TVEEPROM | ||
9 | select VIDEO_CX2341X | ||
10 | select VIDEO_CS5345 | ||
11 | select DVB_S5H1409 if !DVB_FE_CUSTOMISE | ||
12 | select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE | ||
13 | select DVB_S5H1411 if !DVB_FE_CUSTOMISE | ||
14 | select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE | ||
15 | select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE | ||
16 | ---help--- | ||
17 | This is a video4linux driver for Conexant cx23418 based | ||
18 | PCI combo video recorder devices. | ||
19 | |||
20 | This is used in devices such as the Hauppauge HVR-1600 | ||
21 | cards. | ||
22 | |||
23 | To compile this driver as a module, choose M here: the | ||
24 | module will be called cx18. | ||
25 | |||
26 | config VIDEO_CX18_ALSA | ||
27 | tristate "Conexant 23418 DMA audio support" | ||
28 | depends on VIDEO_CX18 && SND && EXPERIMENTAL | ||
29 | select SND_PCM | ||
30 | ---help--- | ||
31 | This is a video4linux driver for direct (DMA) audio on | ||
32 | Conexant 23418 based TV cards using ALSA. | ||
33 | |||
34 | To compile this driver as a module, choose M here: the | ||
35 | module will be called cx18-alsa. | ||
diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile new file mode 100644 index 00000000000..2fadd9ded34 --- /dev/null +++ b/drivers/media/video/cx18/Makefile | |||
@@ -0,0 +1,13 @@ | |||
1 | cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \ | ||
2 | cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \ | ||
3 | cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \ | ||
4 | cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \ | ||
5 | cx18-dvb.o cx18-io.o | ||
6 | cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o | ||
7 | |||
8 | obj-$(CONFIG_VIDEO_CX18) += cx18.o | ||
9 | obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o | ||
10 | |||
11 | EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core | ||
12 | EXTRA_CFLAGS += -Idrivers/media/dvb/frontends | ||
13 | EXTRA_CFLAGS += -Idrivers/media/common/tuners | ||
diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c new file mode 100644 index 00000000000..a1e6c2a3247 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-main.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* | ||
2 | * ALSA interface to cx18 PCM capture streams | ||
3 | * | ||
4 | * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> | ||
5 | * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> | ||
6 | * | ||
7 | * Portions of this work were sponsored by ONELAN Limited. | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include <linux/init.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/device.h> | ||
30 | #include <linux/spinlock.h> | ||
31 | |||
32 | #include <media/v4l2-device.h> | ||
33 | |||
34 | #include <sound/core.h> | ||
35 | #include <sound/initval.h> | ||
36 | |||
37 | #include "cx18-driver.h" | ||
38 | #include "cx18-version.h" | ||
39 | #include "cx18-alsa.h" | ||
40 | #include "cx18-alsa-mixer.h" | ||
41 | #include "cx18-alsa-pcm.h" | ||
42 | |||
43 | int cx18_alsa_debug; | ||
44 | |||
45 | #define CX18_DEBUG_ALSA_INFO(fmt, arg...) \ | ||
46 | do { \ | ||
47 | if (cx18_alsa_debug & 2) \ | ||
48 | printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg); \ | ||
49 | } while (0); | ||
50 | |||
51 | module_param_named(debug, cx18_alsa_debug, int, 0644); | ||
52 | MODULE_PARM_DESC(debug, | ||
53 | "Debug level (bitmask). Default: 0\n" | ||
54 | "\t\t\t 1/0x0001: warning\n" | ||
55 | "\t\t\t 2/0x0002: info\n"); | ||
56 | |||
57 | MODULE_AUTHOR("Andy Walls"); | ||
58 | MODULE_DESCRIPTION("CX23418 ALSA Interface"); | ||
59 | MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder"); | ||
60 | MODULE_LICENSE("GPL"); | ||
61 | |||
62 | MODULE_VERSION(CX18_VERSION); | ||
63 | |||
64 | static inline | ||
65 | struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev) | ||
66 | { | ||
67 | return to_cx18(v4l2_dev)->alsa; | ||
68 | } | ||
69 | |||
70 | static inline | ||
71 | struct snd_cx18_card *p_to_snd_cx18_card(struct v4l2_device **v4l2_dev) | ||
72 | { | ||
73 | return container_of(v4l2_dev, struct snd_cx18_card, v4l2_dev); | ||
74 | } | ||
75 | |||
76 | static void snd_cx18_card_free(struct snd_cx18_card *cxsc) | ||
77 | { | ||
78 | if (cxsc == NULL) | ||
79 | return; | ||
80 | |||
81 | if (cxsc->v4l2_dev != NULL) | ||
82 | to_cx18(cxsc->v4l2_dev)->alsa = NULL; | ||
83 | |||
84 | /* FIXME - take any other stopping actions needed */ | ||
85 | |||
86 | kfree(cxsc); | ||
87 | } | ||
88 | |||
89 | static void snd_cx18_card_private_free(struct snd_card *sc) | ||
90 | { | ||
91 | if (sc == NULL) | ||
92 | return; | ||
93 | snd_cx18_card_free(sc->private_data); | ||
94 | sc->private_data = NULL; | ||
95 | sc->private_free = NULL; | ||
96 | } | ||
97 | |||
98 | static int snd_cx18_card_create(struct v4l2_device *v4l2_dev, | ||
99 | struct snd_card *sc, | ||
100 | struct snd_cx18_card **cxsc) | ||
101 | { | ||
102 | *cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL); | ||
103 | if (*cxsc == NULL) | ||
104 | return -ENOMEM; | ||
105 | |||
106 | (*cxsc)->v4l2_dev = v4l2_dev; | ||
107 | (*cxsc)->sc = sc; | ||
108 | |||
109 | sc->private_data = *cxsc; | ||
110 | sc->private_free = snd_cx18_card_private_free; | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc) | ||
116 | { | ||
117 | struct cx18 *cx = to_cx18(cxsc->v4l2_dev); | ||
118 | struct snd_card *sc = cxsc->sc; | ||
119 | |||
120 | /* sc->driver is used by alsa-lib's configurator: simple, unique */ | ||
121 | strlcpy(sc->driver, "CX23418", sizeof(sc->driver)); | ||
122 | |||
123 | /* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */ | ||
124 | snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d", | ||
125 | cx->instance); | ||
126 | |||
127 | /* sc->longname is read from /proc/asound/cards */ | ||
128 | snprintf(sc->longname, sizeof(sc->longname), | ||
129 | "CX23418 #%d %s TV/FM Radio/Line-In Capture", | ||
130 | cx->instance, cx->card_name); | ||
131 | |||
132 | return 0; | ||
133 | } | ||
134 | |||
135 | static int snd_cx18_init(struct v4l2_device *v4l2_dev) | ||
136 | { | ||
137 | struct cx18 *cx = to_cx18(v4l2_dev); | ||
138 | struct snd_card *sc = NULL; | ||
139 | struct snd_cx18_card *cxsc; | ||
140 | int ret; | ||
141 | |||
142 | /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ | ||
143 | |||
144 | /* (1) Check and increment the device index */ | ||
145 | /* This is a no-op for us. We'll use the cx->instance */ | ||
146 | |||
147 | /* (2) Create a card instance */ | ||
148 | ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */ | ||
149 | SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ | ||
150 | THIS_MODULE, 0, &sc); | ||
151 | if (ret) { | ||
152 | CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n", | ||
153 | __func__, ret); | ||
154 | goto err_exit; | ||
155 | } | ||
156 | |||
157 | /* (3) Create a main component */ | ||
158 | ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc); | ||
159 | if (ret) { | ||
160 | CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n", | ||
161 | __func__, ret); | ||
162 | goto err_exit_free; | ||
163 | } | ||
164 | |||
165 | /* (4) Set the driver ID and name strings */ | ||
166 | snd_cx18_card_set_names(cxsc); | ||
167 | |||
168 | |||
169 | ret = snd_cx18_pcm_create(cxsc); | ||
170 | if (ret) { | ||
171 | CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", | ||
172 | __func__, ret); | ||
173 | goto err_exit_free; | ||
174 | } | ||
175 | /* FIXME - proc files */ | ||
176 | |||
177 | /* (7) Set the driver data and return 0 */ | ||
178 | /* We do this out of normal order for PCI drivers to avoid races */ | ||
179 | cx->alsa = cxsc; | ||
180 | |||
181 | /* (6) Register the card instance */ | ||
182 | ret = snd_card_register(sc); | ||
183 | if (ret) { | ||
184 | cx->alsa = NULL; | ||
185 | CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n", | ||
186 | __func__, ret); | ||
187 | goto err_exit_free; | ||
188 | } | ||
189 | |||
190 | return 0; | ||
191 | |||
192 | err_exit_free: | ||
193 | if (sc != NULL) | ||
194 | snd_card_free(sc); | ||
195 | kfree(cxsc); | ||
196 | err_exit: | ||
197 | return ret; | ||
198 | } | ||
199 | |||
200 | int cx18_alsa_load(struct cx18 *cx) | ||
201 | { | ||
202 | struct v4l2_device *v4l2_dev = &cx->v4l2_dev; | ||
203 | struct cx18_stream *s; | ||
204 | |||
205 | if (v4l2_dev == NULL) { | ||
206 | printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n", | ||
207 | __func__); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | cx = to_cx18(v4l2_dev); | ||
212 | if (cx == NULL) { | ||
213 | printk(KERN_ERR "cx18-alsa cx is NULL\n"); | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; | ||
218 | if (s->video_dev == NULL) { | ||
219 | CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - " | ||
220 | "skipping\n", __func__); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | if (cx->alsa != NULL) { | ||
225 | CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n", | ||
226 | __func__); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | if (snd_cx18_init(v4l2_dev)) { | ||
231 | CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n", | ||
232 | __func__); | ||
233 | } else { | ||
234 | CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance " | ||
235 | "\n", __func__); | ||
236 | } | ||
237 | return 0; | ||
238 | } | ||
239 | |||
240 | static int __init cx18_alsa_init(void) | ||
241 | { | ||
242 | printk(KERN_INFO "cx18-alsa: module loading...\n"); | ||
243 | cx18_ext_init = &cx18_alsa_load; | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc) | ||
248 | { | ||
249 | struct cx18 *cx = to_cx18(cxsc->v4l2_dev); | ||
250 | |||
251 | /* FIXME - pointer checks & shutdown cxsc */ | ||
252 | |||
253 | snd_card_free(cxsc->sc); | ||
254 | cx->alsa = NULL; | ||
255 | } | ||
256 | |||
257 | static int __exit cx18_alsa_exit_callback(struct device *dev, void *data) | ||
258 | { | ||
259 | struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); | ||
260 | struct snd_cx18_card *cxsc; | ||
261 | |||
262 | if (v4l2_dev == NULL) { | ||
263 | printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n", | ||
264 | __func__); | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | cxsc = to_snd_cx18_card(v4l2_dev); | ||
269 | if (cxsc == NULL) { | ||
270 | CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n", | ||
271 | __func__); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | snd_cx18_exit(cxsc); | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static void __exit cx18_alsa_exit(void) | ||
280 | { | ||
281 | struct device_driver *drv; | ||
282 | int ret; | ||
283 | |||
284 | printk(KERN_INFO "cx18-alsa: module unloading...\n"); | ||
285 | |||
286 | drv = driver_find("cx18", &pci_bus_type); | ||
287 | ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback); | ||
288 | put_driver(drv); | ||
289 | |||
290 | cx18_ext_init = NULL; | ||
291 | printk(KERN_INFO "cx18-alsa: module unload complete\n"); | ||
292 | } | ||
293 | |||
294 | module_init(cx18_alsa_init); | ||
295 | module_exit(cx18_alsa_exit); | ||
diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.c b/drivers/media/video/cx18/cx18-alsa-mixer.c new file mode 100644 index 00000000000..341bddc00b7 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-mixer.c | |||
@@ -0,0 +1,175 @@ | |||
1 | /* | ||
2 | * ALSA mixer controls for the | ||
3 | * ALSA interface to cx18 PCM capture streams | ||
4 | * | ||
5 | * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <linux/init.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/device.h> | ||
26 | #include <linux/spinlock.h> | ||
27 | #include <linux/videodev2.h> | ||
28 | |||
29 | #include <media/v4l2-device.h> | ||
30 | |||
31 | #include <sound/core.h> | ||
32 | #include <sound/control.h> | ||
33 | #include <sound/tlv.h> | ||
34 | |||
35 | #include "cx18-alsa.h" | ||
36 | #include "cx18-driver.h" | ||
37 | |||
38 | /* | ||
39 | * Note the cx18-av-core volume scale is funny, due to the alignment of the | ||
40 | * scale with another chip's range: | ||
41 | * | ||
42 | * v4l2_control value /512 indicated dB actual dB reg 0x8d4 | ||
43 | * 0x0000 - 0x01ff 0 -119 -96 228 | ||
44 | * 0x0200 - 0x02ff 1 -118 -96 228 | ||
45 | * ... | ||
46 | * 0x2c00 - 0x2dff 22 -97 -96 228 | ||
47 | * 0x2e00 - 0x2fff 23 -96 -96 228 | ||
48 | * 0x3000 - 0x31ff 24 -95 -95 226 | ||
49 | * ... | ||
50 | * 0xee00 - 0xefff 119 0 0 36 | ||
51 | * ... | ||
52 | * 0xfe00 - 0xffff 127 +8 +8 20 | ||
53 | */ | ||
54 | static inline int dB_to_cx18_av_vol(int dB) | ||
55 | { | ||
56 | if (dB < -96) | ||
57 | dB = -96; | ||
58 | else if (dB > 8) | ||
59 | dB = 8; | ||
60 | return (dB + 119) << 9; | ||
61 | } | ||
62 | |||
63 | static inline int cx18_av_vol_to_dB(int v) | ||
64 | { | ||
65 | if (v < (23 << 9)) | ||
66 | v = (23 << 9); | ||
67 | else if (v > (127 << 9)) | ||
68 | v = (127 << 9); | ||
69 | return (v >> 9) - 119; | ||
70 | } | ||
71 | |||
72 | static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol, | ||
73 | struct snd_ctl_elem_info *uinfo) | ||
74 | { | ||
75 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
76 | uinfo->count = 1; | ||
77 | /* We're already translating values, just keep this control in dB */ | ||
78 | uinfo->value.integer.min = -96; | ||
79 | uinfo->value.integer.max = 8; | ||
80 | uinfo->value.integer.step = 1; | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl, | ||
85 | struct snd_ctl_elem_value *uctl) | ||
86 | { | ||
87 | struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); | ||
88 | struct cx18 *cx = to_cx18(cxsc->v4l2_dev); | ||
89 | struct v4l2_control vctrl; | ||
90 | int ret; | ||
91 | |||
92 | vctrl.id = V4L2_CID_AUDIO_VOLUME; | ||
93 | vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); | ||
94 | |||
95 | snd_cx18_lock(cxsc); | ||
96 | ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); | ||
97 | snd_cx18_unlock(cxsc); | ||
98 | |||
99 | if (!ret) | ||
100 | uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value); | ||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl, | ||
105 | struct snd_ctl_elem_value *uctl) | ||
106 | { | ||
107 | struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); | ||
108 | struct cx18 *cx = to_cx18(cxsc->v4l2_dev); | ||
109 | struct v4l2_control vctrl; | ||
110 | int ret; | ||
111 | |||
112 | vctrl.id = V4L2_CID_AUDIO_VOLUME; | ||
113 | vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); | ||
114 | |||
115 | snd_cx18_lock(cxsc); | ||
116 | |||
117 | /* Fetch current state */ | ||
118 | ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); | ||
119 | |||
120 | if (ret || | ||
121 | (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) { | ||
122 | |||
123 | /* Set, if needed */ | ||
124 | vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); | ||
125 | ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl); | ||
126 | if (!ret) | ||
127 | ret = 1; /* Indicate control was changed w/o error */ | ||
128 | } | ||
129 | snd_cx18_unlock(cxsc); | ||
130 | |||
131 | return ret; | ||
132 | } | ||
133 | |||
134 | |||
135 | /* This is a bit of overkill, the slider is already in dB internally */ | ||
136 | static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0); | ||
137 | |||
138 | static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = { | ||
139 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
140 | .name = "Analog TV Capture Volume", | ||
141 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
142 | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | ||
143 | .info = snd_cx18_mixer_tv_volume_info, | ||
144 | .get = snd_cx18_mixer_tv_volume_get, | ||
145 | .put = snd_cx18_mixer_tv_volume_put, | ||
146 | .tlv.p = snd_cx18_mixer_tv_vol_db_scale | ||
147 | }; | ||
148 | |||
149 | /* FIXME - add mute switch and balance, bass, treble sliders: | ||
150 | V4L2_CID_AUDIO_MUTE | ||
151 | |||
152 | V4L2_CID_AUDIO_BALANCE | ||
153 | |||
154 | V4L2_CID_AUDIO_BASS | ||
155 | V4L2_CID_AUDIO_TREBLE | ||
156 | */ | ||
157 | |||
158 | /* FIXME - add stereo, lang1, lang2, mono menu */ | ||
159 | /* FIXME - add CS5345 I2S volume for HVR-1600 */ | ||
160 | |||
161 | int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc) | ||
162 | { | ||
163 | struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; | ||
164 | struct snd_card *sc = cxsc->sc; | ||
165 | int ret; | ||
166 | |||
167 | strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername)); | ||
168 | |||
169 | ret = snd_ctl_add(sc, snd_ctl_new1(snd_cx18_mixer_tv_vol, cxsc)); | ||
170 | if (ret) { | ||
171 | CX18_ALSA_WARN("%s: failed to add %s control, err %d\n", | ||
172 | __func__, snd_cx18_mixer_tv_vol.name, ret); | ||
173 | } | ||
174 | return ret; | ||
175 | } | ||
diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.h b/drivers/media/video/cx18/cx18-alsa-mixer.h new file mode 100644 index 00000000000..ec9238793f6 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-mixer.h | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * ALSA mixer controls for the | ||
3 | * ALSA interface to cx18 PCM capture streams | ||
4 | * | ||
5 | * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc); | ||
diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c new file mode 100644 index 00000000000..82d195be919 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c | |||
@@ -0,0 +1,360 @@ | |||
1 | /* | ||
2 | * ALSA PCM device for the | ||
3 | * ALSA interface to cx18 PCM capture streams | ||
4 | * | ||
5 | * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> | ||
6 | * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> | ||
7 | * | ||
8 | * Portions of this work were sponsored by ONELAN Limited. | ||
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 | ||
23 | * 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/init.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/vmalloc.h> | ||
29 | |||
30 | #include <media/v4l2-device.h> | ||
31 | |||
32 | #include <sound/core.h> | ||
33 | #include <sound/pcm.h> | ||
34 | |||
35 | #include "cx18-driver.h" | ||
36 | #include "cx18-queue.h" | ||
37 | #include "cx18-streams.h" | ||
38 | #include "cx18-fileops.h" | ||
39 | #include "cx18-alsa.h" | ||
40 | |||
41 | static unsigned int pcm_debug; | ||
42 | module_param(pcm_debug, int, 0644); | ||
43 | MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); | ||
44 | |||
45 | #define dprintk(fmt, arg...) do { \ | ||
46 | if (pcm_debug) \ | ||
47 | printk(KERN_INFO "cx18-alsa-pcm %s: " fmt, \ | ||
48 | __func__, ##arg); \ | ||
49 | } while (0) | ||
50 | |||
51 | static struct snd_pcm_hardware snd_cx18_hw_capture = { | ||
52 | .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
53 | SNDRV_PCM_INFO_MMAP | | ||
54 | SNDRV_PCM_INFO_INTERLEAVED | | ||
55 | SNDRV_PCM_INFO_MMAP_VALID, | ||
56 | |||
57 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | ||
58 | |||
59 | .rates = SNDRV_PCM_RATE_48000, | ||
60 | |||
61 | .rate_min = 48000, | ||
62 | .rate_max = 48000, | ||
63 | .channels_min = 2, | ||
64 | .channels_max = 2, | ||
65 | .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ | ||
66 | .period_bytes_min = 64, /* 12544/2, */ | ||
67 | .period_bytes_max = 12544, | ||
68 | .periods_min = 2, | ||
69 | .periods_max = 98, /* 12544, */ | ||
70 | }; | ||
71 | |||
72 | void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data, | ||
73 | size_t num_bytes) | ||
74 | { | ||
75 | struct snd_pcm_substream *substream; | ||
76 | struct snd_pcm_runtime *runtime; | ||
77 | unsigned int oldptr; | ||
78 | unsigned int stride; | ||
79 | int period_elapsed = 0; | ||
80 | int length; | ||
81 | |||
82 | dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zd\n", cxsc, | ||
83 | pcm_data, num_bytes); | ||
84 | |||
85 | substream = cxsc->capture_pcm_substream; | ||
86 | if (substream == NULL) { | ||
87 | dprintk("substream was NULL\n"); | ||
88 | return; | ||
89 | } | ||
90 | |||
91 | runtime = substream->runtime; | ||
92 | if (runtime == NULL) { | ||
93 | dprintk("runtime was NULL\n"); | ||
94 | return; | ||
95 | } | ||
96 | |||
97 | stride = runtime->frame_bits >> 3; | ||
98 | if (stride == 0) { | ||
99 | dprintk("stride is zero\n"); | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | length = num_bytes / stride; | ||
104 | if (length == 0) { | ||
105 | dprintk("%s: length was zero\n", __func__); | ||
106 | return; | ||
107 | } | ||
108 | |||
109 | if (runtime->dma_area == NULL) { | ||
110 | dprintk("dma area was NULL - ignoring\n"); | ||
111 | return; | ||
112 | } | ||
113 | |||
114 | oldptr = cxsc->hwptr_done_capture; | ||
115 | if (oldptr + length >= runtime->buffer_size) { | ||
116 | unsigned int cnt = | ||
117 | runtime->buffer_size - oldptr; | ||
118 | memcpy(runtime->dma_area + oldptr * stride, pcm_data, | ||
119 | cnt * stride); | ||
120 | memcpy(runtime->dma_area, pcm_data + cnt * stride, | ||
121 | length * stride - cnt * stride); | ||
122 | } else { | ||
123 | memcpy(runtime->dma_area + oldptr * stride, pcm_data, | ||
124 | length * stride); | ||
125 | } | ||
126 | snd_pcm_stream_lock(substream); | ||
127 | |||
128 | cxsc->hwptr_done_capture += length; | ||
129 | if (cxsc->hwptr_done_capture >= | ||
130 | runtime->buffer_size) | ||
131 | cxsc->hwptr_done_capture -= | ||
132 | runtime->buffer_size; | ||
133 | |||
134 | cxsc->capture_transfer_done += length; | ||
135 | if (cxsc->capture_transfer_done >= | ||
136 | runtime->period_size) { | ||
137 | cxsc->capture_transfer_done -= | ||
138 | runtime->period_size; | ||
139 | period_elapsed = 1; | ||
140 | } | ||
141 | |||
142 | snd_pcm_stream_unlock(substream); | ||
143 | |||
144 | if (period_elapsed) | ||
145 | snd_pcm_period_elapsed(substream); | ||
146 | } | ||
147 | |||
148 | static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) | ||
149 | { | ||
150 | struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); | ||
151 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
152 | struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; | ||
153 | struct cx18 *cx = to_cx18(v4l2_dev); | ||
154 | struct cx18_stream *s; | ||
155 | struct cx18_open_id item; | ||
156 | int ret; | ||
157 | |||
158 | /* Instruct the cx18 to start sending packets */ | ||
159 | snd_cx18_lock(cxsc); | ||
160 | s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; | ||
161 | |||
162 | item.cx = cx; | ||
163 | item.type = s->type; | ||
164 | item.open_id = cx->open_id++; | ||
165 | |||
166 | /* See if the stream is available */ | ||
167 | if (cx18_claim_stream(&item, item.type)) { | ||
168 | /* No, it's already in use */ | ||
169 | snd_cx18_unlock(cxsc); | ||
170 | return -EBUSY; | ||
171 | } | ||
172 | |||
173 | if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) || | ||
174 | test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) { | ||
175 | /* We're already streaming. No additional action required */ | ||
176 | snd_cx18_unlock(cxsc); | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | |||
181 | runtime->hw = snd_cx18_hw_capture; | ||
182 | snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); | ||
183 | cxsc->capture_pcm_substream = substream; | ||
184 | runtime->private_data = cx; | ||
185 | |||
186 | cx->pcm_announce_callback = cx18_alsa_announce_pcm_data; | ||
187 | |||
188 | /* Not currently streaming, so start it up */ | ||
189 | set_bit(CX18_F_S_STREAMING, &s->s_flags); | ||
190 | ret = cx18_start_v4l2_encode_stream(s); | ||
191 | snd_cx18_unlock(cxsc); | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) | ||
197 | { | ||
198 | struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); | ||
199 | struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; | ||
200 | struct cx18 *cx = to_cx18(v4l2_dev); | ||
201 | struct cx18_stream *s; | ||
202 | int ret; | ||
203 | |||
204 | /* Instruct the cx18 to stop sending packets */ | ||
205 | snd_cx18_lock(cxsc); | ||
206 | s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; | ||
207 | ret = cx18_stop_v4l2_encode_stream(s, 0); | ||
208 | clear_bit(CX18_F_S_STREAMING, &s->s_flags); | ||
209 | |||
210 | cx18_release_stream(s); | ||
211 | |||
212 | cx->pcm_announce_callback = NULL; | ||
213 | snd_cx18_unlock(cxsc); | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static int snd_cx18_pcm_ioctl(struct snd_pcm_substream *substream, | ||
219 | unsigned int cmd, void *arg) | ||
220 | { | ||
221 | struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); | ||
222 | int ret; | ||
223 | |||
224 | snd_cx18_lock(cxsc); | ||
225 | ret = snd_pcm_lib_ioctl(substream, cmd, arg); | ||
226 | snd_cx18_unlock(cxsc); | ||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | |||
231 | static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, | ||
232 | size_t size) | ||
233 | { | ||
234 | struct snd_pcm_runtime *runtime = subs->runtime; | ||
235 | |||
236 | dprintk("Allocating vbuffer\n"); | ||
237 | if (runtime->dma_area) { | ||
238 | if (runtime->dma_bytes > size) | ||
239 | return 0; | ||
240 | |||
241 | vfree(runtime->dma_area); | ||
242 | } | ||
243 | runtime->dma_area = vmalloc(size); | ||
244 | if (!runtime->dma_area) | ||
245 | return -ENOMEM; | ||
246 | |||
247 | runtime->dma_bytes = size; | ||
248 | |||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream, | ||
253 | struct snd_pcm_hw_params *params) | ||
254 | { | ||
255 | int ret; | ||
256 | |||
257 | dprintk("%s called\n", __func__); | ||
258 | |||
259 | ret = snd_pcm_alloc_vmalloc_buffer(substream, | ||
260 | params_buffer_bytes(params)); | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream) | ||
265 | { | ||
266 | struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); | ||
267 | unsigned long flags; | ||
268 | |||
269 | spin_lock_irqsave(&cxsc->slock, flags); | ||
270 | if (substream->runtime->dma_area) { | ||
271 | dprintk("freeing pcm capture region\n"); | ||
272 | vfree(substream->runtime->dma_area); | ||
273 | substream->runtime->dma_area = NULL; | ||
274 | } | ||
275 | spin_unlock_irqrestore(&cxsc->slock, flags); | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream) | ||
281 | { | ||
282 | struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); | ||
283 | |||
284 | cxsc->hwptr_done_capture = 0; | ||
285 | cxsc->capture_transfer_done = 0; | ||
286 | |||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
291 | { | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static | ||
296 | snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream) | ||
297 | { | ||
298 | unsigned long flags; | ||
299 | snd_pcm_uframes_t hwptr_done; | ||
300 | struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); | ||
301 | |||
302 | spin_lock_irqsave(&cxsc->slock, flags); | ||
303 | hwptr_done = cxsc->hwptr_done_capture; | ||
304 | spin_unlock_irqrestore(&cxsc->slock, flags); | ||
305 | |||
306 | return hwptr_done; | ||
307 | } | ||
308 | |||
309 | static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, | ||
310 | unsigned long offset) | ||
311 | { | ||
312 | void *pageptr = subs->runtime->dma_area + offset; | ||
313 | |||
314 | return vmalloc_to_page(pageptr); | ||
315 | } | ||
316 | |||
317 | static struct snd_pcm_ops snd_cx18_pcm_capture_ops = { | ||
318 | .open = snd_cx18_pcm_capture_open, | ||
319 | .close = snd_cx18_pcm_capture_close, | ||
320 | .ioctl = snd_cx18_pcm_ioctl, | ||
321 | .hw_params = snd_cx18_pcm_hw_params, | ||
322 | .hw_free = snd_cx18_pcm_hw_free, | ||
323 | .prepare = snd_cx18_pcm_prepare, | ||
324 | .trigger = snd_cx18_pcm_trigger, | ||
325 | .pointer = snd_cx18_pcm_pointer, | ||
326 | .page = snd_pcm_get_vmalloc_page, | ||
327 | }; | ||
328 | |||
329 | int snd_cx18_pcm_create(struct snd_cx18_card *cxsc) | ||
330 | { | ||
331 | struct snd_pcm *sp; | ||
332 | struct snd_card *sc = cxsc->sc; | ||
333 | struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; | ||
334 | struct cx18 *cx = to_cx18(v4l2_dev); | ||
335 | int ret; | ||
336 | |||
337 | ret = snd_pcm_new(sc, "CX23418 PCM", | ||
338 | 0, /* PCM device 0, the only one for this card */ | ||
339 | 0, /* 0 playback substreams */ | ||
340 | 1, /* 1 capture substream */ | ||
341 | &sp); | ||
342 | if (ret) { | ||
343 | CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", | ||
344 | __func__, ret); | ||
345 | goto err_exit; | ||
346 | } | ||
347 | |||
348 | spin_lock_init(&cxsc->slock); | ||
349 | |||
350 | snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, | ||
351 | &snd_cx18_pcm_capture_ops); | ||
352 | sp->info_flags = 0; | ||
353 | sp->private_data = cxsc; | ||
354 | strlcpy(sp->name, cx->card_name, sizeof(sp->name)); | ||
355 | |||
356 | return 0; | ||
357 | |||
358 | err_exit: | ||
359 | return ret; | ||
360 | } | ||
diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.h b/drivers/media/video/cx18/cx18-alsa-pcm.h new file mode 100644 index 00000000000..d26e51f9457 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-pcm.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /* | ||
2 | * ALSA PCM device for the | ||
3 | * ALSA interface to cx18 PCM capture streams | ||
4 | * | ||
5 | * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc); | ||
24 | |||
25 | /* Used by cx18-mailbox to announce the PCM data to the module */ | ||
26 | void cx18_alsa_announce_pcm_data(struct snd_cx18_card *card, u8 *pcm_data, | ||
27 | size_t num_bytes); | ||
diff --git a/drivers/media/video/cx18/cx18-alsa.h b/drivers/media/video/cx18/cx18-alsa.h new file mode 100644 index 00000000000..447da374c9e --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa.h | |||
@@ -0,0 +1,75 @@ | |||
1 | /* | ||
2 | * ALSA interface to cx18 PCM capture streams | ||
3 | * | ||
4 | * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
19 | * 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | struct snd_card; | ||
23 | |||
24 | struct snd_cx18_card { | ||
25 | struct v4l2_device *v4l2_dev; | ||
26 | struct snd_card *sc; | ||
27 | unsigned int capture_transfer_done; | ||
28 | unsigned int hwptr_done_capture; | ||
29 | struct snd_pcm_substream *capture_pcm_substream; | ||
30 | spinlock_t slock; | ||
31 | }; | ||
32 | |||
33 | extern int cx18_alsa_debug; | ||
34 | |||
35 | /* | ||
36 | * File operations that manipulate the encoder or video or audio subdevices | ||
37 | * need to be serialized. Use the same lock we use for v4l2 file ops. | ||
38 | */ | ||
39 | static inline void snd_cx18_lock(struct snd_cx18_card *cxsc) | ||
40 | { | ||
41 | struct cx18 *cx = to_cx18(cxsc->v4l2_dev); | ||
42 | mutex_lock(&cx->serialize_lock); | ||
43 | } | ||
44 | |||
45 | static inline void snd_cx18_unlock(struct snd_cx18_card *cxsc) | ||
46 | { | ||
47 | struct cx18 *cx = to_cx18(cxsc->v4l2_dev); | ||
48 | mutex_unlock(&cx->serialize_lock); | ||
49 | } | ||
50 | |||
51 | #define CX18_ALSA_DBGFLG_WARN (1 << 0) | ||
52 | #define CX18_ALSA_DBGFLG_WARN (1 << 0) | ||
53 | #define CX18_ALSA_DBGFLG_INFO (1 << 1) | ||
54 | |||
55 | #define CX18_ALSA_DEBUG(x, type, fmt, args...) \ | ||
56 | do { \ | ||
57 | if ((x) & cx18_alsa_debug) \ | ||
58 | printk(KERN_INFO "%s-alsa: " type ": " fmt, \ | ||
59 | v4l2_dev->name , ## args); \ | ||
60 | } while (0) | ||
61 | |||
62 | #define CX18_ALSA_DEBUG_WARN(fmt, args...) \ | ||
63 | CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_WARN, "warning", fmt , ## args) | ||
64 | |||
65 | #define CX18_ALSA_DEBUG_INFO(fmt, args...) \ | ||
66 | CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_INFO, "info", fmt , ## args) | ||
67 | |||
68 | #define CX18_ALSA_ERR(fmt, args...) \ | ||
69 | printk(KERN_ERR "%s-alsa: " fmt, v4l2_dev->name , ## args) | ||
70 | |||
71 | #define CX18_ALSA_WARN(fmt, args...) \ | ||
72 | printk(KERN_WARNING "%s-alsa: " fmt, v4l2_dev->name , ## args) | ||
73 | |||
74 | #define CX18_ALSA_INFO(fmt, args...) \ | ||
75 | printk(KERN_INFO "%s-alsa: " fmt, v4l2_dev->name , ## args) | ||
diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c new file mode 100644 index 00000000000..35268923911 --- /dev/null +++ b/drivers/media/video/cx18/cx18-audio.c | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * cx18 audio-related functions | ||
3 | * | ||
4 | * Derived from ivtv-audio.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
21 | * 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | #include "cx18-driver.h" | ||
25 | #include "cx18-io.h" | ||
26 | #include "cx18-cards.h" | ||
27 | #include "cx18-audio.h" | ||
28 | |||
29 | #define CX18_AUDIO_ENABLE 0xc72014 | ||
30 | #define CX18_AI1_MUX_MASK 0x30 | ||
31 | #define CX18_AI1_MUX_I2S1 0x00 | ||
32 | #define CX18_AI1_MUX_I2S2 0x10 | ||
33 | #define CX18_AI1_MUX_843_I2S 0x20 | ||
34 | |||
35 | /* Selects the audio input and output according to the current | ||
36 | settings. */ | ||
37 | int cx18_audio_set_io(struct cx18 *cx) | ||
38 | { | ||
39 | const struct cx18_card_audio_input *in; | ||
40 | u32 u, v; | ||
41 | int err; | ||
42 | |||
43 | /* Determine which input to use */ | ||
44 | if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) | ||
45 | in = &cx->card->radio_input; | ||
46 | else | ||
47 | in = &cx->card->audio_inputs[cx->audio_input]; | ||
48 | |||
49 | /* handle muxer chips */ | ||
50 | v4l2_subdev_call(cx->sd_extmux, audio, s_routing, | ||
51 | (u32) in->muxer_input, 0, 0); | ||
52 | |||
53 | err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl, | ||
54 | audio, s_routing, in->audio_input, 0, 0); | ||
55 | if (err) | ||
56 | return err; | ||
57 | |||
58 | /* FIXME - this internal mux should be abstracted to a subdev */ | ||
59 | u = cx18_read_reg(cx, CX18_AUDIO_ENABLE); | ||
60 | v = u & ~CX18_AI1_MUX_MASK; | ||
61 | switch (in->audio_input) { | ||
62 | case CX18_AV_AUDIO_SERIAL1: | ||
63 | v |= CX18_AI1_MUX_I2S1; | ||
64 | break; | ||
65 | case CX18_AV_AUDIO_SERIAL2: | ||
66 | v |= CX18_AI1_MUX_I2S2; | ||
67 | break; | ||
68 | default: | ||
69 | v |= CX18_AI1_MUX_843_I2S; | ||
70 | break; | ||
71 | } | ||
72 | if (v == u) { | ||
73 | /* force a toggle of some AI1 MUX control bits */ | ||
74 | u &= ~CX18_AI1_MUX_MASK; | ||
75 | switch (in->audio_input) { | ||
76 | case CX18_AV_AUDIO_SERIAL1: | ||
77 | u |= CX18_AI1_MUX_843_I2S; | ||
78 | break; | ||
79 | case CX18_AV_AUDIO_SERIAL2: | ||
80 | u |= CX18_AI1_MUX_843_I2S; | ||
81 | break; | ||
82 | default: | ||
83 | u |= CX18_AI1_MUX_I2S1; | ||
84 | break; | ||
85 | } | ||
86 | cx18_write_reg_expect(cx, u | 0xb00, CX18_AUDIO_ENABLE, | ||
87 | u, CX18_AI1_MUX_MASK); | ||
88 | } | ||
89 | cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, | ||
90 | v, CX18_AI1_MUX_MASK); | ||
91 | return 0; | ||
92 | } | ||
diff --git a/drivers/media/video/cx18/cx18-audio.h b/drivers/media/video/cx18/cx18-audio.h new file mode 100644 index 00000000000..2731d29b0ab --- /dev/null +++ b/drivers/media/video/cx18/cx18-audio.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * cx18 audio-related functions | ||
3 | * | ||
4 | * Derived from ivtv-audio.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
21 | * 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | int cx18_audio_set_io(struct cx18 *cx); | ||
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c new file mode 100644 index 00000000000..4a24ffb17a7 --- /dev/null +++ b/drivers/media/video/cx18/cx18-av-audio.c | |||
@@ -0,0 +1,471 @@ | |||
1 | /* | ||
2 | * cx18 ADEC audio functions | ||
3 | * | ||
4 | * Derived from cx25840-audio.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version 2 | ||
12 | * of the License, or (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., 51 Franklin Street, Fifth Floor, Boston, MA | ||
22 | * 02110-1301, USA. | ||
23 | */ | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | |||
27 | static int set_audclk_freq(struct cx18 *cx, u32 freq) | ||
28 | { | ||
29 | struct cx18_av_state *state = &cx->av_state; | ||
30 | |||
31 | if (freq != 32000 && freq != 44100 && freq != 48000) | ||
32 | return -EINVAL; | ||
33 | |||
34 | /* | ||
35 | * The PLL parameters are based on the external crystal frequency that | ||
36 | * would ideally be: | ||
37 | * | ||
38 | * NTSC Color subcarrier freq * 8 = | ||
39 | * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz | ||
40 | * | ||
41 | * The accidents of history and rationale that explain from where this | ||
42 | * combination of magic numbers originate can be found in: | ||
43 | * | ||
44 | * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in | ||
45 | * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80 | ||
46 | * | ||
47 | * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the | ||
48 | * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83 | ||
49 | * | ||
50 | * As Mike Bradley has rightly pointed out, it's not the exact crystal | ||
51 | * frequency that matters, only that all parts of the driver and | ||
52 | * firmware are using the same value (close to the ideal value). | ||
53 | * | ||
54 | * Since I have a strong suspicion that, if the firmware ever assumes a | ||
55 | * crystal value at all, it will assume 28.636360 MHz, the crystal | ||
56 | * freq used in calculations in this driver will be: | ||
57 | * | ||
58 | * xtal_freq = 28.636360 MHz | ||
59 | * | ||
60 | * an error of less than 0.13 ppm which is way, way better than any off | ||
61 | * the shelf crystal will have for accuracy anyway. | ||
62 | * | ||
63 | * Below I aim to run the PLLs' VCOs near 400 MHz to minimze error. | ||
64 | * | ||
65 | * Many thanks to Jeff Campbell and Mike Bradley for their extensive | ||
66 | * investigation, experimentation, testing, and suggested solutions of | ||
67 | * of audio/video sync problems with SVideo and CVBS captures. | ||
68 | */ | ||
69 | |||
70 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | ||
71 | switch (freq) { | ||
72 | case 32000: | ||
73 | /* | ||
74 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
75 | * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20 | ||
76 | */ | ||
77 | cx18_av_write4(cx, 0x108, 0x200d040f); | ||
78 | |||
79 | /* VID_PLL Fraction = 0x2be2fe */ | ||
80 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
81 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
82 | |||
83 | /* AUX_PLL Fraction = 0x176740c */ | ||
84 | /* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/ | ||
85 | cx18_av_write4(cx, 0x110, 0x0176740c); | ||
86 | |||
87 | /* src3/4/6_ctl */ | ||
88 | /* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */ | ||
89 | cx18_av_write4(cx, 0x900, 0x0801f77f); | ||
90 | cx18_av_write4(cx, 0x904, 0x0801f77f); | ||
91 | cx18_av_write4(cx, 0x90c, 0x0801f77f); | ||
92 | |||
93 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ | ||
94 | cx18_av_write(cx, 0x127, 0x60); | ||
95 | |||
96 | /* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */ | ||
97 | cx18_av_write4(cx, 0x12c, 0x11202fff); | ||
98 | |||
99 | /* | ||
100 | * EN_AV_LOCK = 0 | ||
101 | * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = | ||
102 | * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 | ||
103 | */ | ||
104 | cx18_av_write4(cx, 0x128, 0xa00d2ef8); | ||
105 | break; | ||
106 | |||
107 | case 44100: | ||
108 | /* | ||
109 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
110 | * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18 | ||
111 | */ | ||
112 | cx18_av_write4(cx, 0x108, 0x180e040f); | ||
113 | |||
114 | /* VID_PLL Fraction = 0x2be2fe */ | ||
115 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
116 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
117 | |||
118 | /* AUX_PLL Fraction = 0x062a1f2 */ | ||
119 | /* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/ | ||
120 | cx18_av_write4(cx, 0x110, 0x0062a1f2); | ||
121 | |||
122 | /* src3/4/6_ctl */ | ||
123 | /* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */ | ||
124 | cx18_av_write4(cx, 0x900, 0x08016d59); | ||
125 | cx18_av_write4(cx, 0x904, 0x08016d59); | ||
126 | cx18_av_write4(cx, 0x90c, 0x08016d59); | ||
127 | |||
128 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */ | ||
129 | cx18_av_write(cx, 0x127, 0x58); | ||
130 | |||
131 | /* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */ | ||
132 | cx18_av_write4(cx, 0x12c, 0x112092ff); | ||
133 | |||
134 | /* | ||
135 | * EN_AV_LOCK = 0 | ||
136 | * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = | ||
137 | * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 | ||
138 | */ | ||
139 | cx18_av_write4(cx, 0x128, 0xa01d4bf8); | ||
140 | break; | ||
141 | |||
142 | case 48000: | ||
143 | /* | ||
144 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
145 | * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16 | ||
146 | */ | ||
147 | cx18_av_write4(cx, 0x108, 0x160e040f); | ||
148 | |||
149 | /* VID_PLL Fraction = 0x2be2fe */ | ||
150 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
151 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
152 | |||
153 | /* AUX_PLL Fraction = 0x05227ad */ | ||
154 | /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/ | ||
155 | cx18_av_write4(cx, 0x110, 0x005227ad); | ||
156 | |||
157 | /* src3/4/6_ctl */ | ||
158 | /* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */ | ||
159 | cx18_av_write4(cx, 0x900, 0x08014faa); | ||
160 | cx18_av_write4(cx, 0x904, 0x08014faa); | ||
161 | cx18_av_write4(cx, 0x90c, 0x08014faa); | ||
162 | |||
163 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ | ||
164 | cx18_av_write(cx, 0x127, 0x56); | ||
165 | |||
166 | /* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */ | ||
167 | cx18_av_write4(cx, 0x12c, 0x11205fff); | ||
168 | |||
169 | /* | ||
170 | * EN_AV_LOCK = 0 | ||
171 | * VID_COUNT = 0x1193f8 = 143999.000 * 8 = | ||
172 | * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 | ||
173 | */ | ||
174 | cx18_av_write4(cx, 0x128, 0xa01193f8); | ||
175 | break; | ||
176 | } | ||
177 | } else { | ||
178 | switch (freq) { | ||
179 | case 32000: | ||
180 | /* | ||
181 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
182 | * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30 | ||
183 | */ | ||
184 | cx18_av_write4(cx, 0x108, 0x300d040f); | ||
185 | |||
186 | /* VID_PLL Fraction = 0x2be2fe */ | ||
187 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
188 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
189 | |||
190 | /* AUX_PLL Fraction = 0x176740c */ | ||
191 | /* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/ | ||
192 | cx18_av_write4(cx, 0x110, 0x0176740c); | ||
193 | |||
194 | /* src1_ctl */ | ||
195 | /* 0x1.0000 = 32000/32000 */ | ||
196 | cx18_av_write4(cx, 0x8f8, 0x08010000); | ||
197 | |||
198 | /* src3/4/6_ctl */ | ||
199 | /* 0x2.0000 = 2 * (32000/32000) */ | ||
200 | cx18_av_write4(cx, 0x900, 0x08020000); | ||
201 | cx18_av_write4(cx, 0x904, 0x08020000); | ||
202 | cx18_av_write4(cx, 0x90c, 0x08020000); | ||
203 | |||
204 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */ | ||
205 | cx18_av_write(cx, 0x127, 0x70); | ||
206 | |||
207 | /* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */ | ||
208 | cx18_av_write4(cx, 0x12c, 0x11201fff); | ||
209 | |||
210 | /* | ||
211 | * EN_AV_LOCK = 0 | ||
212 | * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = | ||
213 | * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 | ||
214 | */ | ||
215 | cx18_av_write4(cx, 0x128, 0xa00d2ef8); | ||
216 | break; | ||
217 | |||
218 | case 44100: | ||
219 | /* | ||
220 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
221 | * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24 | ||
222 | */ | ||
223 | cx18_av_write4(cx, 0x108, 0x240e040f); | ||
224 | |||
225 | /* VID_PLL Fraction = 0x2be2fe */ | ||
226 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
227 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
228 | |||
229 | /* AUX_PLL Fraction = 0x062a1f2 */ | ||
230 | /* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/ | ||
231 | cx18_av_write4(cx, 0x110, 0x0062a1f2); | ||
232 | |||
233 | /* src1_ctl */ | ||
234 | /* 0x1.60cd = 44100/32000 */ | ||
235 | cx18_av_write4(cx, 0x8f8, 0x080160cd); | ||
236 | |||
237 | /* src3/4/6_ctl */ | ||
238 | /* 0x1.7385 = 2 * (32000/44100) */ | ||
239 | cx18_av_write4(cx, 0x900, 0x08017385); | ||
240 | cx18_av_write4(cx, 0x904, 0x08017385); | ||
241 | cx18_av_write4(cx, 0x90c, 0x08017385); | ||
242 | |||
243 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */ | ||
244 | cx18_av_write(cx, 0x127, 0x64); | ||
245 | |||
246 | /* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */ | ||
247 | cx18_av_write4(cx, 0x12c, 0x112061ff); | ||
248 | |||
249 | /* | ||
250 | * EN_AV_LOCK = 0 | ||
251 | * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = | ||
252 | * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 | ||
253 | */ | ||
254 | cx18_av_write4(cx, 0x128, 0xa01d4bf8); | ||
255 | break; | ||
256 | |||
257 | case 48000: | ||
258 | /* | ||
259 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
260 | * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20 | ||
261 | */ | ||
262 | cx18_av_write4(cx, 0x108, 0x200d040f); | ||
263 | |||
264 | /* VID_PLL Fraction = 0x2be2fe */ | ||
265 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
266 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
267 | |||
268 | /* AUX_PLL Fraction = 0x176740c */ | ||
269 | /* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/ | ||
270 | cx18_av_write4(cx, 0x110, 0x0176740c); | ||
271 | |||
272 | /* src1_ctl */ | ||
273 | /* 0x1.8000 = 48000/32000 */ | ||
274 | cx18_av_write4(cx, 0x8f8, 0x08018000); | ||
275 | |||
276 | /* src3/4/6_ctl */ | ||
277 | /* 0x1.5555 = 2 * (32000/48000) */ | ||
278 | cx18_av_write4(cx, 0x900, 0x08015555); | ||
279 | cx18_av_write4(cx, 0x904, 0x08015555); | ||
280 | cx18_av_write4(cx, 0x90c, 0x08015555); | ||
281 | |||
282 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ | ||
283 | cx18_av_write(cx, 0x127, 0x60); | ||
284 | |||
285 | /* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */ | ||
286 | cx18_av_write4(cx, 0x12c, 0x11203fff); | ||
287 | |||
288 | /* | ||
289 | * EN_AV_LOCK = 0 | ||
290 | * VID_COUNT = 0x1193f8 = 143999.000 * 8 = | ||
291 | * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 | ||
292 | */ | ||
293 | cx18_av_write4(cx, 0x128, 0xa01193f8); | ||
294 | break; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | state->audclk_freq = freq; | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | void cx18_av_audio_set_path(struct cx18 *cx) | ||
304 | { | ||
305 | struct cx18_av_state *state = &cx->av_state; | ||
306 | u8 v; | ||
307 | |||
308 | /* stop microcontroller */ | ||
309 | v = cx18_av_read(cx, 0x803) & ~0x10; | ||
310 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
311 | |||
312 | /* assert soft reset */ | ||
313 | v = cx18_av_read(cx, 0x810) | 0x01; | ||
314 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | ||
315 | |||
316 | /* Mute everything to prevent the PFFT! */ | ||
317 | cx18_av_write(cx, 0x8d3, 0x1f); | ||
318 | |||
319 | if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) { | ||
320 | /* Set Path1 to Serial Audio Input */ | ||
321 | cx18_av_write4(cx, 0x8d0, 0x01011012); | ||
322 | |||
323 | /* The microcontroller should not be started for the | ||
324 | * non-tuner inputs: autodetection is specific for | ||
325 | * TV audio. */ | ||
326 | } else { | ||
327 | /* Set Path1 to Analog Demod Main Channel */ | ||
328 | cx18_av_write4(cx, 0x8d0, 0x1f063870); | ||
329 | } | ||
330 | |||
331 | set_audclk_freq(cx, state->audclk_freq); | ||
332 | |||
333 | /* deassert soft reset */ | ||
334 | v = cx18_av_read(cx, 0x810) & ~0x01; | ||
335 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | ||
336 | |||
337 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | ||
338 | /* When the microcontroller detects the | ||
339 | * audio format, it will unmute the lines */ | ||
340 | v = cx18_av_read(cx, 0x803) | 0x10; | ||
341 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
342 | } | ||
343 | } | ||
344 | |||
345 | static void set_volume(struct cx18 *cx, int volume) | ||
346 | { | ||
347 | /* First convert the volume to msp3400 values (0-127) */ | ||
348 | int vol = volume >> 9; | ||
349 | /* now scale it up to cx18_av values | ||
350 | * -114dB to -96dB maps to 0 | ||
351 | * this should be 19, but in my testing that was 4dB too loud */ | ||
352 | if (vol <= 23) | ||
353 | vol = 0; | ||
354 | else | ||
355 | vol -= 23; | ||
356 | |||
357 | /* PATH1_VOLUME */ | ||
358 | cx18_av_write(cx, 0x8d4, 228 - (vol * 2)); | ||
359 | } | ||
360 | |||
361 | static void set_bass(struct cx18 *cx, int bass) | ||
362 | { | ||
363 | /* PATH1_EQ_BASS_VOL */ | ||
364 | cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff)); | ||
365 | } | ||
366 | |||
367 | static void set_treble(struct cx18 *cx, int treble) | ||
368 | { | ||
369 | /* PATH1_EQ_TREBLE_VOL */ | ||
370 | cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff)); | ||
371 | } | ||
372 | |||
373 | static void set_balance(struct cx18 *cx, int balance) | ||
374 | { | ||
375 | int bal = balance >> 8; | ||
376 | if (bal > 0x80) { | ||
377 | /* PATH1_BAL_LEFT */ | ||
378 | cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80); | ||
379 | /* PATH1_BAL_LEVEL */ | ||
380 | cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f); | ||
381 | } else { | ||
382 | /* PATH1_BAL_LEFT */ | ||
383 | cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00); | ||
384 | /* PATH1_BAL_LEVEL */ | ||
385 | cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal); | ||
386 | } | ||
387 | } | ||
388 | |||
389 | static void set_mute(struct cx18 *cx, int mute) | ||
390 | { | ||
391 | struct cx18_av_state *state = &cx->av_state; | ||
392 | u8 v; | ||
393 | |||
394 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | ||
395 | /* Must turn off microcontroller in order to mute sound. | ||
396 | * Not sure if this is the best method, but it does work. | ||
397 | * If the microcontroller is running, then it will undo any | ||
398 | * changes to the mute register. */ | ||
399 | v = cx18_av_read(cx, 0x803); | ||
400 | if (mute) { | ||
401 | /* disable microcontroller */ | ||
402 | v &= ~0x10; | ||
403 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
404 | cx18_av_write(cx, 0x8d3, 0x1f); | ||
405 | } else { | ||
406 | /* enable microcontroller */ | ||
407 | v |= 0x10; | ||
408 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
409 | } | ||
410 | } else { | ||
411 | /* SRC1_MUTE_EN */ | ||
412 | cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq) | ||
417 | { | ||
418 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
419 | struct cx18_av_state *state = &cx->av_state; | ||
420 | int retval; | ||
421 | u8 v; | ||
422 | |||
423 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | ||
424 | v = cx18_av_read(cx, 0x803) & ~0x10; | ||
425 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
426 | cx18_av_write(cx, 0x8d3, 0x1f); | ||
427 | } | ||
428 | v = cx18_av_read(cx, 0x810) | 0x1; | ||
429 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | ||
430 | |||
431 | retval = set_audclk_freq(cx, freq); | ||
432 | |||
433 | v = cx18_av_read(cx, 0x810) & ~0x1; | ||
434 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | ||
435 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | ||
436 | v = cx18_av_read(cx, 0x803) | 0x10; | ||
437 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
438 | } | ||
439 | return retval; | ||
440 | } | ||
441 | |||
442 | static int cx18_av_audio_s_ctrl(struct v4l2_ctrl *ctrl) | ||
443 | { | ||
444 | struct v4l2_subdev *sd = to_sd(ctrl); | ||
445 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
446 | |||
447 | switch (ctrl->id) { | ||
448 | case V4L2_CID_AUDIO_VOLUME: | ||
449 | set_volume(cx, ctrl->val); | ||
450 | break; | ||
451 | case V4L2_CID_AUDIO_BASS: | ||
452 | set_bass(cx, ctrl->val); | ||
453 | break; | ||
454 | case V4L2_CID_AUDIO_TREBLE: | ||
455 | set_treble(cx, ctrl->val); | ||
456 | break; | ||
457 | case V4L2_CID_AUDIO_BALANCE: | ||
458 | set_balance(cx, ctrl->val); | ||
459 | break; | ||
460 | case V4L2_CID_AUDIO_MUTE: | ||
461 | set_mute(cx, ctrl->val); | ||
462 | break; | ||
463 | default: | ||
464 | return -EINVAL; | ||
465 | } | ||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops = { | ||
470 | .s_ctrl = cx18_av_audio_s_ctrl, | ||
471 | }; | ||
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c new file mode 100644 index 00000000000..f164b7f610a --- /dev/null +++ b/drivers/media/video/cx18/cx18-av-core.c | |||
@@ -0,0 +1,1401 @@ | |||
1 | /* | ||
2 | * cx18 ADEC audio functions | ||
3 | * | ||
4 | * Derived from cx25840-core.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version 2 | ||
12 | * of the License, or (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., 51 Franklin Street, Fifth Floor, Boston, MA | ||
22 | * 02110-1301, USA. | ||
23 | */ | ||
24 | |||
25 | #include <media/v4l2-chip-ident.h> | ||
26 | #include "cx18-driver.h" | ||
27 | #include "cx18-io.h" | ||
28 | #include "cx18-cards.h" | ||
29 | |||
30 | int cx18_av_write(struct cx18 *cx, u16 addr, u8 value) | ||
31 | { | ||
32 | u32 reg = 0xc40000 + (addr & ~3); | ||
33 | u32 mask = 0xff; | ||
34 | int shift = (addr & 3) * 8; | ||
35 | u32 x = cx18_read_reg(cx, reg); | ||
36 | |||
37 | x = (x & ~(mask << shift)) | ((u32)value << shift); | ||
38 | cx18_write_reg(cx, x, reg); | ||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask) | ||
43 | { | ||
44 | u32 reg = 0xc40000 + (addr & ~3); | ||
45 | int shift = (addr & 3) * 8; | ||
46 | u32 x = cx18_read_reg(cx, reg); | ||
47 | |||
48 | x = (x & ~((u32)0xff << shift)) | ((u32)value << shift); | ||
49 | cx18_write_reg_expect(cx, x, reg, | ||
50 | ((u32)eval << shift), ((u32)mask << shift)); | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value) | ||
55 | { | ||
56 | cx18_write_reg(cx, value, 0xc40000 + addr); | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | int | ||
61 | cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, u32 mask) | ||
62 | { | ||
63 | cx18_write_reg_expect(cx, value, 0xc40000 + addr, eval, mask); | ||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value) | ||
68 | { | ||
69 | cx18_write_reg_noretry(cx, value, 0xc40000 + addr); | ||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | u8 cx18_av_read(struct cx18 *cx, u16 addr) | ||
74 | { | ||
75 | u32 x = cx18_read_reg(cx, 0xc40000 + (addr & ~3)); | ||
76 | int shift = (addr & 3) * 8; | ||
77 | |||
78 | return (x >> shift) & 0xff; | ||
79 | } | ||
80 | |||
81 | u32 cx18_av_read4(struct cx18 *cx, u16 addr) | ||
82 | { | ||
83 | return cx18_read_reg(cx, 0xc40000 + addr); | ||
84 | } | ||
85 | |||
86 | int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask, | ||
87 | u8 or_value) | ||
88 | { | ||
89 | return cx18_av_write(cx, addr, | ||
90 | (cx18_av_read(cx, addr) & and_mask) | | ||
91 | or_value); | ||
92 | } | ||
93 | |||
94 | int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, | ||
95 | u32 or_value) | ||
96 | { | ||
97 | return cx18_av_write4(cx, addr, | ||
98 | (cx18_av_read4(cx, addr) & and_mask) | | ||
99 | or_value); | ||
100 | } | ||
101 | |||
102 | static void cx18_av_init(struct cx18 *cx) | ||
103 | { | ||
104 | /* | ||
105 | * The crystal freq used in calculations in this driver will be | ||
106 | * 28.636360 MHz. | ||
107 | * Aim to run the PLLs' VCOs near 400 MHz to minimze errors. | ||
108 | */ | ||
109 | |||
110 | /* | ||
111 | * VDCLK Integer = 0x0f, Post Divider = 0x04 | ||
112 | * AIMCLK Integer = 0x0e, Post Divider = 0x16 | ||
113 | */ | ||
114 | cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f); | ||
115 | |||
116 | /* VDCLK Fraction = 0x2be2fe */ | ||
117 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */ | ||
118 | cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe); | ||
119 | |||
120 | /* AIMCLK Fraction = 0x05227ad */ | ||
121 | /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/ | ||
122 | cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad); | ||
123 | |||
124 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ | ||
125 | cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); | ||
126 | } | ||
127 | |||
128 | static void cx18_av_initialize(struct v4l2_subdev *sd) | ||
129 | { | ||
130 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
131 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
132 | int default_volume; | ||
133 | u32 v; | ||
134 | |||
135 | cx18_av_loadfw(cx); | ||
136 | /* Stop 8051 code execution */ | ||
137 | cx18_av_write4_expect(cx, CXADEC_DL_CTL, 0x03000000, | ||
138 | 0x03000000, 0x13000000); | ||
139 | |||
140 | /* initallize the PLL by toggling sleep bit */ | ||
141 | v = cx18_av_read4(cx, CXADEC_HOST_REG1); | ||
142 | /* enable sleep mode - register appears to be read only... */ | ||
143 | cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v | 1, v, 0xfffe); | ||
144 | /* disable sleep mode */ | ||
145 | cx18_av_write4_expect(cx, CXADEC_HOST_REG1, v & 0xfffe, | ||
146 | v & 0xfffe, 0xffff); | ||
147 | |||
148 | /* initialize DLLs */ | ||
149 | v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF; | ||
150 | /* disable FLD */ | ||
151 | cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v); | ||
152 | /* enable FLD */ | ||
153 | cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v | 0x10000100); | ||
154 | |||
155 | v = cx18_av_read4(cx, CXADEC_DLL2_DIAG_CTRL) & 0xE1FFFEFF; | ||
156 | /* disable FLD */ | ||
157 | cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v); | ||
158 | /* enable FLD */ | ||
159 | cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v | 0x06000100); | ||
160 | |||
161 | /* set analog bias currents. Set Vreg to 1.20V. */ | ||
162 | cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL1, 0x000A1802); | ||
163 | |||
164 | v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1; | ||
165 | /* enable TUNE_FIL_RST */ | ||
166 | cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, v, v, 0x03009F0F); | ||
167 | /* disable TUNE_FIL_RST */ | ||
168 | cx18_av_write4_expect(cx, CXADEC_AFE_DIAG_CTRL3, | ||
169 | v & 0xFFFFFFFE, v & 0xFFFFFFFE, 0x03009F0F); | ||
170 | |||
171 | /* enable 656 output */ | ||
172 | cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00); | ||
173 | |||
174 | /* video output drive strength */ | ||
175 | cx18_av_and_or4(cx, CXADEC_PIN_CTRL2, ~0, 0x2); | ||
176 | |||
177 | /* reset video */ | ||
178 | cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000); | ||
179 | cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0); | ||
180 | |||
181 | /* | ||
182 | * Disable Video Auto-config of the Analog Front End and Video PLL. | ||
183 | * | ||
184 | * Since we only use BT.656 pixel mode, which works for both 525 and 625 | ||
185 | * line systems, it's just easier for us to set registers | ||
186 | * 0x102 (CXADEC_CHIP_CTRL), 0x104-0x106 (CXADEC_AFE_CTRL), | ||
187 | * 0x108-0x109 (CXADEC_PLL_CTRL1), and 0x10c-0x10f (CXADEC_VID_PLL_FRAC) | ||
188 | * ourselves, than to run around cleaning up after the auto-config. | ||
189 | * | ||
190 | * (Note: my CX23418 chip doesn't seem to let the ACFG_DIS bit | ||
191 | * get set to 1, but OTOH, it doesn't seem to do AFE and VID PLL | ||
192 | * autoconfig either.) | ||
193 | * | ||
194 | * As a default, also turn off Dual mode for ADC2 and set ADC2 to CH3. | ||
195 | */ | ||
196 | cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000); | ||
197 | |||
198 | /* Setup the Video and and Aux/Audio PLLs */ | ||
199 | cx18_av_init(cx); | ||
200 | |||
201 | /* set video to auto-detect */ | ||
202 | /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ | ||
203 | /* set the comb notch = 1 */ | ||
204 | cx18_av_and_or4(cx, CXADEC_MODE_CTRL, 0xFFF7E7F0, 0x02040800); | ||
205 | |||
206 | /* Enable wtw_en in CRUSH_CTRL (Set bit 22) */ | ||
207 | /* Enable maj_sel in CRUSH_CTRL (Set bit 20) */ | ||
208 | cx18_av_and_or4(cx, CXADEC_CRUSH_CTRL, ~0, 0x00500000); | ||
209 | |||
210 | /* Set VGA_TRACK_RANGE to 0x20 */ | ||
211 | cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000); | ||
212 | |||
213 | /* | ||
214 | * Initial VBI setup | ||
215 | * VIP-1.1, 10 bit mode, enable Raw, disable sliced, | ||
216 | * don't clamp raw samples when codes are in use, 1 byte user D-words, | ||
217 | * IDID0 has line #, RP code V bit transition on VBLANK, data during | ||
218 | * blanking intervals | ||
219 | */ | ||
220 | cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4013252e); | ||
221 | |||
222 | /* Set the video input. | ||
223 | The setting in MODE_CTRL gets lost when we do the above setup */ | ||
224 | /* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */ | ||
225 | /* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */ | ||
226 | |||
227 | /* | ||
228 | * Analog Front End (AFE) | ||
229 | * Default to luma on ch1/ADC1, chroma on ch2/ADC2, SIF on ch3/ADC2 | ||
230 | * bypass_ch[1-3] use filter | ||
231 | * droop_comp_ch[1-3] disable | ||
232 | * clamp_en_ch[1-3] disable | ||
233 | * aud_in_sel ADC2 | ||
234 | * luma_in_sel ADC1 | ||
235 | * chroma_in_sel ADC2 | ||
236 | * clamp_sel_ch[2-3] midcode | ||
237 | * clamp_sel_ch1 video decoder | ||
238 | * vga_sel_ch3 audio decoder | ||
239 | * vga_sel_ch[1-2] video decoder | ||
240 | * half_bw_ch[1-3] disable | ||
241 | * +12db_ch[1-3] disable | ||
242 | */ | ||
243 | cx18_av_and_or4(cx, CXADEC_AFE_CTRL, 0xFF000000, 0x00005D00); | ||
244 | |||
245 | /* if(dwEnable && dw3DCombAvailable) { */ | ||
246 | /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */ | ||
247 | /* } else { */ | ||
248 | /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */ | ||
249 | /* } */ | ||
250 | cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F); | ||
251 | default_volume = cx18_av_read(cx, 0x8d4); | ||
252 | /* | ||
253 | * Enforce the legacy volume scale mapping limits to avoid | ||
254 | * -ERANGE errors when initializing the volume control | ||
255 | */ | ||
256 | if (default_volume > 228) { | ||
257 | /* Bottom out at -96 dB, v4l2 vol range 0x2e00-0x2fff */ | ||
258 | default_volume = 228; | ||
259 | cx18_av_write(cx, 0x8d4, 228); | ||
260 | } else if (default_volume < 20) { | ||
261 | /* Top out at + 8 dB, v4l2 vol range 0xfe00-0xffff */ | ||
262 | default_volume = 20; | ||
263 | cx18_av_write(cx, 0x8d4, 20); | ||
264 | } | ||
265 | default_volume = (((228 - default_volume) >> 1) + 23) << 9; | ||
266 | state->volume->cur.val = state->volume->default_value = default_volume; | ||
267 | v4l2_ctrl_handler_setup(&state->hdl); | ||
268 | } | ||
269 | |||
270 | static int cx18_av_reset(struct v4l2_subdev *sd, u32 val) | ||
271 | { | ||
272 | cx18_av_initialize(sd); | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static int cx18_av_load_fw(struct v4l2_subdev *sd) | ||
277 | { | ||
278 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
279 | |||
280 | if (!state->is_initialized) { | ||
281 | /* initialize on first use */ | ||
282 | state->is_initialized = 1; | ||
283 | cx18_av_initialize(sd); | ||
284 | } | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | void cx18_av_std_setup(struct cx18 *cx) | ||
289 | { | ||
290 | struct cx18_av_state *state = &cx->av_state; | ||
291 | struct v4l2_subdev *sd = &state->sd; | ||
292 | v4l2_std_id std = state->std; | ||
293 | |||
294 | /* | ||
295 | * Video ADC crystal clock to pixel clock SRC decimation ratio | ||
296 | * 28.636360 MHz/13.5 Mpps * 256 = 0x21f.07b | ||
297 | */ | ||
298 | const int src_decimation = 0x21f; | ||
299 | |||
300 | int hblank, hactive, burst, vblank, vactive, sc; | ||
301 | int vblank656; | ||
302 | int luma_lpf, uv_lpf, comb; | ||
303 | u32 pll_int, pll_frac, pll_post; | ||
304 | |||
305 | /* datasheet startup, step 8d */ | ||
306 | if (std & ~V4L2_STD_NTSC) | ||
307 | cx18_av_write(cx, 0x49f, 0x11); | ||
308 | else | ||
309 | cx18_av_write(cx, 0x49f, 0x14); | ||
310 | |||
311 | /* | ||
312 | * Note: At the end of a field, there are 3 sets of half line duration | ||
313 | * (double horizontal rate) pulses: | ||
314 | * | ||
315 | * 5 (625) or 6 (525) half-lines to blank for the vertical retrace | ||
316 | * 5 (625) or 6 (525) vertical sync pulses of half line duration | ||
317 | * 5 (625) or 6 (525) half-lines of equalization pulses | ||
318 | */ | ||
319 | if (std & V4L2_STD_625_50) { | ||
320 | /* | ||
321 | * The following relationships of half line counts should hold: | ||
322 | * 625 = vblank656 + vactive | ||
323 | * 10 = vblank656 - vblank = vsync pulses + equalization pulses | ||
324 | * | ||
325 | * vblank656: half lines after line 625/mid-313 of blanked video | ||
326 | * vblank: half lines, after line 5/317, of blanked video | ||
327 | * vactive: half lines of active video + | ||
328 | * 5 half lines after the end of active video | ||
329 | * | ||
330 | * As far as I can tell: | ||
331 | * vblank656 starts counting from the falling edge of the first | ||
332 | * vsync pulse (start of line 1 or mid-313) | ||
333 | * vblank starts counting from the after the 5 vsync pulses and | ||
334 | * 5 or 4 equalization pulses (start of line 6 or 318) | ||
335 | * | ||
336 | * For 625 line systems the driver will extract VBI information | ||
337 | * from lines 6-23 and lines 318-335 (but the slicer can only | ||
338 | * handle 17 lines, not the 18 in the vblank region). | ||
339 | * In addition, we need vblank656 and vblank to be one whole | ||
340 | * line longer, to cover line 24 and 336, so the SAV/EAV RP | ||
341 | * codes get generated such that the encoder can actually | ||
342 | * extract line 23 & 335 (WSS). We'll lose 1 line in each field | ||
343 | * at the top of the screen. | ||
344 | * | ||
345 | * It appears the 5 half lines that happen after active | ||
346 | * video must be included in vactive (579 instead of 574), | ||
347 | * otherwise the colors get badly displayed in various regions | ||
348 | * of the screen. I guess the chroma comb filter gets confused | ||
349 | * without them (at least when a PVR-350 is the PAL source). | ||
350 | */ | ||
351 | vblank656 = 48; /* lines 1 - 24 & 313 - 336 */ | ||
352 | vblank = 38; /* lines 6 - 24 & 318 - 336 */ | ||
353 | vactive = 579; /* lines 24 - 313 & 337 - 626 */ | ||
354 | |||
355 | /* | ||
356 | * For a 13.5 Mpps clock and 15,625 Hz line rate, a line is | ||
357 | * is 864 pixels = 720 active + 144 blanking. ITU-R BT.601 | ||
358 | * specifies 12 luma clock periods or ~ 0.9 * 13.5 Mpps after | ||
359 | * the end of active video to start a horizontal line, so that | ||
360 | * leaves 132 pixels of hblank to ignore. | ||
361 | */ | ||
362 | hblank = 132; | ||
363 | hactive = 720; | ||
364 | |||
365 | /* | ||
366 | * Burst gate delay (for 625 line systems) | ||
367 | * Hsync leading edge to color burst rise = 5.6 us | ||
368 | * Color burst width = 2.25 us | ||
369 | * Gate width = 4 pixel clocks | ||
370 | * (5.6 us + 2.25/2 us) * 13.5 Mpps + 4/2 clocks = 92.79 clocks | ||
371 | */ | ||
372 | burst = 93; | ||
373 | luma_lpf = 2; | ||
374 | if (std & V4L2_STD_PAL) { | ||
375 | uv_lpf = 1; | ||
376 | comb = 0x20; | ||
377 | /* sc = 4433618.75 * src_decimation/28636360 * 2^13 */ | ||
378 | sc = 688700; | ||
379 | } else if (std == V4L2_STD_PAL_Nc) { | ||
380 | uv_lpf = 1; | ||
381 | comb = 0x20; | ||
382 | /* sc = 3582056.25 * src_decimation/28636360 * 2^13 */ | ||
383 | sc = 556422; | ||
384 | } else { /* SECAM */ | ||
385 | uv_lpf = 0; | ||
386 | comb = 0; | ||
387 | /* (fr + fb)/2 = (4406260 + 4250000)/2 = 4328130 */ | ||
388 | /* sc = 4328130 * src_decimation/28636360 * 2^13 */ | ||
389 | sc = 672314; | ||
390 | } | ||
391 | } else { | ||
392 | /* | ||
393 | * The following relationships of half line counts should hold: | ||
394 | * 525 = prevsync + vblank656 + vactive | ||
395 | * 12 = vblank656 - vblank = vsync pulses + equalization pulses | ||
396 | * | ||
397 | * prevsync: 6 half-lines before the vsync pulses | ||
398 | * vblank656: half lines, after line 3/mid-266, of blanked video | ||
399 | * vblank: half lines, after line 9/272, of blanked video | ||
400 | * vactive: half lines of active video | ||
401 | * | ||
402 | * As far as I can tell: | ||
403 | * vblank656 starts counting from the falling edge of the first | ||
404 | * vsync pulse (start of line 4 or mid-266) | ||
405 | * vblank starts counting from the after the 6 vsync pulses and | ||
406 | * 6 or 5 equalization pulses (start of line 10 or 272) | ||
407 | * | ||
408 | * For 525 line systems the driver will extract VBI information | ||
409 | * from lines 10-21 and lines 273-284. | ||
410 | */ | ||
411 | vblank656 = 38; /* lines 4 - 22 & 266 - 284 */ | ||
412 | vblank = 26; /* lines 10 - 22 & 272 - 284 */ | ||
413 | vactive = 481; /* lines 23 - 263 & 285 - 525 */ | ||
414 | |||
415 | /* | ||
416 | * For a 13.5 Mpps clock and 15,734.26 Hz line rate, a line is | ||
417 | * is 858 pixels = 720 active + 138 blanking. The Hsync leading | ||
418 | * edge should happen 1.2 us * 13.5 Mpps ~= 16 pixels after the | ||
419 | * end of active video, leaving 122 pixels of hblank to ignore | ||
420 | * before active video starts. | ||
421 | */ | ||
422 | hactive = 720; | ||
423 | hblank = 122; | ||
424 | luma_lpf = 1; | ||
425 | uv_lpf = 1; | ||
426 | |||
427 | /* | ||
428 | * Burst gate delay (for 525 line systems) | ||
429 | * Hsync leading edge to color burst rise = 5.3 us | ||
430 | * Color burst width = 2.5 us | ||
431 | * Gate width = 4 pixel clocks | ||
432 | * (5.3 us + 2.5/2 us) * 13.5 Mpps + 4/2 clocks = 90.425 clocks | ||
433 | */ | ||
434 | if (std == V4L2_STD_PAL_60) { | ||
435 | burst = 90; | ||
436 | luma_lpf = 2; | ||
437 | comb = 0x20; | ||
438 | /* sc = 4433618.75 * src_decimation/28636360 * 2^13 */ | ||
439 | sc = 688700; | ||
440 | } else if (std == V4L2_STD_PAL_M) { | ||
441 | /* The 97 needs to be verified against PAL-M timings */ | ||
442 | burst = 97; | ||
443 | comb = 0x20; | ||
444 | /* sc = 3575611.49 * src_decimation/28636360 * 2^13 */ | ||
445 | sc = 555421; | ||
446 | } else { | ||
447 | burst = 90; | ||
448 | comb = 0x66; | ||
449 | /* sc = 3579545.45.. * src_decimation/28636360 * 2^13 */ | ||
450 | sc = 556032; | ||
451 | } | ||
452 | } | ||
453 | |||
454 | /* DEBUG: Displays configured PLL frequency */ | ||
455 | pll_int = cx18_av_read(cx, 0x108); | ||
456 | pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff; | ||
457 | pll_post = cx18_av_read(cx, 0x109); | ||
458 | CX18_DEBUG_INFO_DEV(sd, "PLL regs = int: %u, frac: %u, post: %u\n", | ||
459 | pll_int, pll_frac, pll_post); | ||
460 | |||
461 | if (pll_post) { | ||
462 | int fsc, pll; | ||
463 | u64 tmp; | ||
464 | |||
465 | pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25; | ||
466 | pll /= pll_post; | ||
467 | CX18_DEBUG_INFO_DEV(sd, "Video PLL = %d.%06d MHz\n", | ||
468 | pll / 1000000, pll % 1000000); | ||
469 | CX18_DEBUG_INFO_DEV(sd, "Pixel rate = %d.%06d Mpixel/sec\n", | ||
470 | pll / 8000000, (pll / 8) % 1000000); | ||
471 | |||
472 | CX18_DEBUG_INFO_DEV(sd, "ADC XTAL/pixel clock decimation ratio " | ||
473 | "= %d.%03d\n", src_decimation / 256, | ||
474 | ((src_decimation % 256) * 1000) / 256); | ||
475 | |||
476 | tmp = 28636360 * (u64) sc; | ||
477 | do_div(tmp, src_decimation); | ||
478 | fsc = tmp >> 13; | ||
479 | CX18_DEBUG_INFO_DEV(sd, | ||
480 | "Chroma sub-carrier initial freq = %d.%06d " | ||
481 | "MHz\n", fsc / 1000000, fsc % 1000000); | ||
482 | |||
483 | CX18_DEBUG_INFO_DEV(sd, "hblank %i, hactive %i, vblank %i, " | ||
484 | "vactive %i, vblank656 %i, src_dec %i, " | ||
485 | "burst 0x%02x, luma_lpf %i, uv_lpf %i, " | ||
486 | "comb 0x%02x, sc 0x%06x\n", | ||
487 | hblank, hactive, vblank, vactive, vblank656, | ||
488 | src_decimation, burst, luma_lpf, uv_lpf, | ||
489 | comb, sc); | ||
490 | } | ||
491 | |||
492 | /* Sets horizontal blanking delay and active lines */ | ||
493 | cx18_av_write(cx, 0x470, hblank); | ||
494 | cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) | | ||
495 | (hactive << 4))); | ||
496 | cx18_av_write(cx, 0x472, hactive >> 4); | ||
497 | |||
498 | /* Sets burst gate delay */ | ||
499 | cx18_av_write(cx, 0x473, burst); | ||
500 | |||
501 | /* Sets vertical blanking delay and active duration */ | ||
502 | cx18_av_write(cx, 0x474, vblank); | ||
503 | cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) | | ||
504 | (vactive << 4))); | ||
505 | cx18_av_write(cx, 0x476, vactive >> 4); | ||
506 | cx18_av_write(cx, 0x477, vblank656); | ||
507 | |||
508 | /* Sets src decimation rate */ | ||
509 | cx18_av_write(cx, 0x478, 0xff & src_decimation); | ||
510 | cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8)); | ||
511 | |||
512 | /* Sets Luma and UV Low pass filters */ | ||
513 | cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); | ||
514 | |||
515 | /* Enables comb filters */ | ||
516 | cx18_av_write(cx, 0x47b, comb); | ||
517 | |||
518 | /* Sets SC Step*/ | ||
519 | cx18_av_write(cx, 0x47c, sc); | ||
520 | cx18_av_write(cx, 0x47d, 0xff & sc >> 8); | ||
521 | cx18_av_write(cx, 0x47e, 0xff & sc >> 16); | ||
522 | |||
523 | if (std & V4L2_STD_625_50) { | ||
524 | state->slicer_line_delay = 1; | ||
525 | state->slicer_line_offset = (6 + state->slicer_line_delay - 2); | ||
526 | } else { | ||
527 | state->slicer_line_delay = 0; | ||
528 | state->slicer_line_offset = (10 + state->slicer_line_delay - 2); | ||
529 | } | ||
530 | cx18_av_write(cx, 0x47f, state->slicer_line_delay); | ||
531 | } | ||
532 | |||
533 | static void input_change(struct cx18 *cx) | ||
534 | { | ||
535 | struct cx18_av_state *state = &cx->av_state; | ||
536 | v4l2_std_id std = state->std; | ||
537 | u8 v; | ||
538 | |||
539 | /* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */ | ||
540 | cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); | ||
541 | cx18_av_and_or(cx, 0x401, ~0x60, 0); | ||
542 | cx18_av_and_or(cx, 0x401, ~0x60, 0x60); | ||
543 | |||
544 | if (std & V4L2_STD_525_60) { | ||
545 | if (std == V4L2_STD_NTSC_M_JP) { | ||
546 | /* Japan uses EIAJ audio standard */ | ||
547 | cx18_av_write_expect(cx, 0x808, 0xf7, 0xf7, 0xff); | ||
548 | cx18_av_write_expect(cx, 0x80b, 0x02, 0x02, 0x3f); | ||
549 | } else if (std == V4L2_STD_NTSC_M_KR) { | ||
550 | /* South Korea uses A2 audio standard */ | ||
551 | cx18_av_write_expect(cx, 0x808, 0xf8, 0xf8, 0xff); | ||
552 | cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f); | ||
553 | } else { | ||
554 | /* Others use the BTSC audio standard */ | ||
555 | cx18_av_write_expect(cx, 0x808, 0xf6, 0xf6, 0xff); | ||
556 | cx18_av_write_expect(cx, 0x80b, 0x01, 0x01, 0x3f); | ||
557 | } | ||
558 | } else if (std & V4L2_STD_PAL) { | ||
559 | /* Follow tuner change procedure for PAL */ | ||
560 | cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff); | ||
561 | cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f); | ||
562 | } else if (std & V4L2_STD_SECAM) { | ||
563 | /* Select autodetect for SECAM */ | ||
564 | cx18_av_write_expect(cx, 0x808, 0xff, 0xff, 0xff); | ||
565 | cx18_av_write_expect(cx, 0x80b, 0x03, 0x03, 0x3f); | ||
566 | } | ||
567 | |||
568 | v = cx18_av_read(cx, 0x803); | ||
569 | if (v & 0x10) { | ||
570 | /* restart audio decoder microcontroller */ | ||
571 | v &= ~0x10; | ||
572 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
573 | v |= 0x10; | ||
574 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
575 | } | ||
576 | } | ||
577 | |||
578 | static int cx18_av_s_frequency(struct v4l2_subdev *sd, | ||
579 | struct v4l2_frequency *freq) | ||
580 | { | ||
581 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
582 | input_change(cx); | ||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, | ||
587 | enum cx18_av_audio_input aud_input) | ||
588 | { | ||
589 | struct cx18_av_state *state = &cx->av_state; | ||
590 | struct v4l2_subdev *sd = &state->sd; | ||
591 | |||
592 | enum analog_signal_type { | ||
593 | NONE, CVBS, Y, C, SIF, Pb, Pr | ||
594 | } ch[3] = {NONE, NONE, NONE}; | ||
595 | |||
596 | u8 afe_mux_cfg; | ||
597 | u8 adc2_cfg; | ||
598 | u8 input_mode; | ||
599 | u32 afe_cfg; | ||
600 | int i; | ||
601 | |||
602 | CX18_DEBUG_INFO_DEV(sd, "decoder set video input %d, audio input %d\n", | ||
603 | vid_input, aud_input); | ||
604 | |||
605 | if (vid_input >= CX18_AV_COMPOSITE1 && | ||
606 | vid_input <= CX18_AV_COMPOSITE8) { | ||
607 | afe_mux_cfg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); | ||
608 | ch[0] = CVBS; | ||
609 | input_mode = 0x0; | ||
610 | } else if (vid_input >= CX18_AV_COMPONENT_LUMA1) { | ||
611 | int luma = vid_input & 0xf000; | ||
612 | int r_chroma = vid_input & 0xf0000; | ||
613 | int b_chroma = vid_input & 0xf00000; | ||
614 | |||
615 | if ((vid_input & ~0xfff000) || | ||
616 | luma < CX18_AV_COMPONENT_LUMA1 || | ||
617 | luma > CX18_AV_COMPONENT_LUMA8 || | ||
618 | r_chroma < CX18_AV_COMPONENT_R_CHROMA4 || | ||
619 | r_chroma > CX18_AV_COMPONENT_R_CHROMA6 || | ||
620 | b_chroma < CX18_AV_COMPONENT_B_CHROMA7 || | ||
621 | b_chroma > CX18_AV_COMPONENT_B_CHROMA8) { | ||
622 | CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n", | ||
623 | vid_input); | ||
624 | return -EINVAL; | ||
625 | } | ||
626 | afe_mux_cfg = (luma - CX18_AV_COMPONENT_LUMA1) >> 12; | ||
627 | ch[0] = Y; | ||
628 | afe_mux_cfg |= (r_chroma - CX18_AV_COMPONENT_R_CHROMA4) >> 12; | ||
629 | ch[1] = Pr; | ||
630 | afe_mux_cfg |= (b_chroma - CX18_AV_COMPONENT_B_CHROMA7) >> 14; | ||
631 | ch[2] = Pb; | ||
632 | input_mode = 0x6; | ||
633 | } else { | ||
634 | int luma = vid_input & 0xf0; | ||
635 | int chroma = vid_input & 0xf00; | ||
636 | |||
637 | if ((vid_input & ~0xff0) || | ||
638 | luma < CX18_AV_SVIDEO_LUMA1 || | ||
639 | luma > CX18_AV_SVIDEO_LUMA8 || | ||
640 | chroma < CX18_AV_SVIDEO_CHROMA4 || | ||
641 | chroma > CX18_AV_SVIDEO_CHROMA8) { | ||
642 | CX18_ERR_DEV(sd, "0x%06x is not a valid video input!\n", | ||
643 | vid_input); | ||
644 | return -EINVAL; | ||
645 | } | ||
646 | afe_mux_cfg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4); | ||
647 | ch[0] = Y; | ||
648 | if (chroma >= CX18_AV_SVIDEO_CHROMA7) { | ||
649 | afe_mux_cfg &= 0x3f; | ||
650 | afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2; | ||
651 | ch[2] = C; | ||
652 | } else { | ||
653 | afe_mux_cfg &= 0xcf; | ||
654 | afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; | ||
655 | ch[1] = C; | ||
656 | } | ||
657 | input_mode = 0x2; | ||
658 | } | ||
659 | |||
660 | switch (aud_input) { | ||
661 | case CX18_AV_AUDIO_SERIAL1: | ||
662 | case CX18_AV_AUDIO_SERIAL2: | ||
663 | /* do nothing, use serial audio input */ | ||
664 | break; | ||
665 | case CX18_AV_AUDIO4: | ||
666 | afe_mux_cfg &= ~0x30; | ||
667 | ch[1] = SIF; | ||
668 | break; | ||
669 | case CX18_AV_AUDIO5: | ||
670 | afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x10; | ||
671 | ch[1] = SIF; | ||
672 | break; | ||
673 | case CX18_AV_AUDIO6: | ||
674 | afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x20; | ||
675 | ch[1] = SIF; | ||
676 | break; | ||
677 | case CX18_AV_AUDIO7: | ||
678 | afe_mux_cfg &= ~0xc0; | ||
679 | ch[2] = SIF; | ||
680 | break; | ||
681 | case CX18_AV_AUDIO8: | ||
682 | afe_mux_cfg = (afe_mux_cfg & ~0xc0) | 0x40; | ||
683 | ch[2] = SIF; | ||
684 | break; | ||
685 | |||
686 | default: | ||
687 | CX18_ERR_DEV(sd, "0x%04x is not a valid audio input!\n", | ||
688 | aud_input); | ||
689 | return -EINVAL; | ||
690 | } | ||
691 | |||
692 | /* Set up analog front end multiplexers */ | ||
693 | cx18_av_write_expect(cx, 0x103, afe_mux_cfg, afe_mux_cfg, 0xf7); | ||
694 | /* Set INPUT_MODE to Composite, S-Video, or Component */ | ||
695 | cx18_av_and_or(cx, 0x401, ~0x6, input_mode); | ||
696 | |||
697 | /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ | ||
698 | adc2_cfg = cx18_av_read(cx, 0x102); | ||
699 | if (ch[2] == NONE) | ||
700 | adc2_cfg &= ~0x2; /* No sig on CH3, set ADC2 to CH2 for input */ | ||
701 | else | ||
702 | adc2_cfg |= 0x2; /* Signal on CH3, set ADC2 to CH3 for input */ | ||
703 | |||
704 | /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ | ||
705 | if (ch[1] != NONE && ch[2] != NONE) | ||
706 | adc2_cfg |= 0x4; /* Set dual mode */ | ||
707 | else | ||
708 | adc2_cfg &= ~0x4; /* Clear dual mode */ | ||
709 | cx18_av_write_expect(cx, 0x102, adc2_cfg, adc2_cfg, 0x17); | ||
710 | |||
711 | /* Configure the analog front end */ | ||
712 | afe_cfg = cx18_av_read4(cx, CXADEC_AFE_CTRL); | ||
713 | afe_cfg &= 0xff000000; | ||
714 | afe_cfg |= 0x00005000; /* CHROMA_IN, AUD_IN: ADC2; LUMA_IN: ADC1 */ | ||
715 | if (ch[1] != NONE && ch[2] != NONE) | ||
716 | afe_cfg |= 0x00000030; /* half_bw_ch[2-3] since in dual mode */ | ||
717 | |||
718 | for (i = 0; i < 3; i++) { | ||
719 | switch (ch[i]) { | ||
720 | default: | ||
721 | case NONE: | ||
722 | /* CLAMP_SEL = Fixed to midcode clamp level */ | ||
723 | afe_cfg |= (0x00000200 << i); | ||
724 | break; | ||
725 | case CVBS: | ||
726 | case Y: | ||
727 | if (i > 0) | ||
728 | afe_cfg |= 0x00002000; /* LUMA_IN_SEL: ADC2 */ | ||
729 | break; | ||
730 | case C: | ||
731 | case Pb: | ||
732 | case Pr: | ||
733 | /* CLAMP_SEL = Fixed to midcode clamp level */ | ||
734 | afe_cfg |= (0x00000200 << i); | ||
735 | if (i == 0 && ch[i] == C) | ||
736 | afe_cfg &= ~0x00001000; /* CHROMA_IN_SEL ADC1 */ | ||
737 | break; | ||
738 | case SIF: | ||
739 | /* | ||
740 | * VGA_GAIN_SEL = Audio Decoder | ||
741 | * CLAMP_SEL = Fixed to midcode clamp level | ||
742 | */ | ||
743 | afe_cfg |= (0x00000240 << i); | ||
744 | if (i == 0) | ||
745 | afe_cfg &= ~0x00004000; /* AUD_IN_SEL ADC1 */ | ||
746 | break; | ||
747 | } | ||
748 | } | ||
749 | |||
750 | cx18_av_write4(cx, CXADEC_AFE_CTRL, afe_cfg); | ||
751 | |||
752 | state->vid_input = vid_input; | ||
753 | state->aud_input = aud_input; | ||
754 | cx18_av_audio_set_path(cx); | ||
755 | input_change(cx); | ||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | static int cx18_av_s_video_routing(struct v4l2_subdev *sd, | ||
760 | u32 input, u32 output, u32 config) | ||
761 | { | ||
762 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
763 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
764 | return set_input(cx, input, state->aud_input); | ||
765 | } | ||
766 | |||
767 | static int cx18_av_s_audio_routing(struct v4l2_subdev *sd, | ||
768 | u32 input, u32 output, u32 config) | ||
769 | { | ||
770 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
771 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
772 | return set_input(cx, state->vid_input, input); | ||
773 | } | ||
774 | |||
775 | static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) | ||
776 | { | ||
777 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
778 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
779 | u8 vpres; | ||
780 | u8 mode; | ||
781 | int val = 0; | ||
782 | |||
783 | if (state->radio) | ||
784 | return 0; | ||
785 | |||
786 | vpres = cx18_av_read(cx, 0x40e) & 0x20; | ||
787 | vt->signal = vpres ? 0xffff : 0x0; | ||
788 | |||
789 | vt->capability |= | ||
790 | V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | | ||
791 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP; | ||
792 | |||
793 | mode = cx18_av_read(cx, 0x804); | ||
794 | |||
795 | /* get rxsubchans and audmode */ | ||
796 | if ((mode & 0xf) == 1) | ||
797 | val |= V4L2_TUNER_SUB_STEREO; | ||
798 | else | ||
799 | val |= V4L2_TUNER_SUB_MONO; | ||
800 | |||
801 | if (mode == 2 || mode == 4) | ||
802 | val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; | ||
803 | |||
804 | if (mode & 0x10) | ||
805 | val |= V4L2_TUNER_SUB_SAP; | ||
806 | |||
807 | vt->rxsubchans = val; | ||
808 | vt->audmode = state->audmode; | ||
809 | return 0; | ||
810 | } | ||
811 | |||
812 | static int cx18_av_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) | ||
813 | { | ||
814 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
815 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
816 | u8 v; | ||
817 | |||
818 | if (state->radio) | ||
819 | return 0; | ||
820 | |||
821 | v = cx18_av_read(cx, 0x809); | ||
822 | v &= ~0xf; | ||
823 | |||
824 | switch (vt->audmode) { | ||
825 | case V4L2_TUNER_MODE_MONO: | ||
826 | /* mono -> mono | ||
827 | stereo -> mono | ||
828 | bilingual -> lang1 */ | ||
829 | break; | ||
830 | case V4L2_TUNER_MODE_STEREO: | ||
831 | case V4L2_TUNER_MODE_LANG1: | ||
832 | /* mono -> mono | ||
833 | stereo -> stereo | ||
834 | bilingual -> lang1 */ | ||
835 | v |= 0x4; | ||
836 | break; | ||
837 | case V4L2_TUNER_MODE_LANG1_LANG2: | ||
838 | /* mono -> mono | ||
839 | stereo -> stereo | ||
840 | bilingual -> lang1/lang2 */ | ||
841 | v |= 0x7; | ||
842 | break; | ||
843 | case V4L2_TUNER_MODE_LANG2: | ||
844 | /* mono -> mono | ||
845 | stereo -> stereo | ||
846 | bilingual -> lang2 */ | ||
847 | v |= 0x1; | ||
848 | break; | ||
849 | default: | ||
850 | return -EINVAL; | ||
851 | } | ||
852 | cx18_av_write_expect(cx, 0x809, v, v, 0xff); | ||
853 | state->audmode = vt->audmode; | ||
854 | return 0; | ||
855 | } | ||
856 | |||
857 | static int cx18_av_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) | ||
858 | { | ||
859 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
860 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
861 | |||
862 | u8 fmt = 0; /* zero is autodetect */ | ||
863 | u8 pal_m = 0; | ||
864 | |||
865 | if (state->radio == 0 && state->std == norm) | ||
866 | return 0; | ||
867 | |||
868 | state->radio = 0; | ||
869 | state->std = norm; | ||
870 | |||
871 | /* First tests should be against specific std */ | ||
872 | if (state->std == V4L2_STD_NTSC_M_JP) { | ||
873 | fmt = 0x2; | ||
874 | } else if (state->std == V4L2_STD_NTSC_443) { | ||
875 | fmt = 0x3; | ||
876 | } else if (state->std == V4L2_STD_PAL_M) { | ||
877 | pal_m = 1; | ||
878 | fmt = 0x5; | ||
879 | } else if (state->std == V4L2_STD_PAL_N) { | ||
880 | fmt = 0x6; | ||
881 | } else if (state->std == V4L2_STD_PAL_Nc) { | ||
882 | fmt = 0x7; | ||
883 | } else if (state->std == V4L2_STD_PAL_60) { | ||
884 | fmt = 0x8; | ||
885 | } else { | ||
886 | /* Then, test against generic ones */ | ||
887 | if (state->std & V4L2_STD_NTSC) | ||
888 | fmt = 0x1; | ||
889 | else if (state->std & V4L2_STD_PAL) | ||
890 | fmt = 0x4; | ||
891 | else if (state->std & V4L2_STD_SECAM) | ||
892 | fmt = 0xc; | ||
893 | } | ||
894 | |||
895 | CX18_DEBUG_INFO_DEV(sd, "changing video std to fmt %i\n", fmt); | ||
896 | |||
897 | /* Follow step 9 of section 3.16 in the cx18_av datasheet. | ||
898 | Without this PAL may display a vertical ghosting effect. | ||
899 | This happens for example with the Yuan MPC622. */ | ||
900 | if (fmt >= 4 && fmt < 8) { | ||
901 | /* Set format to NTSC-M */ | ||
902 | cx18_av_and_or(cx, 0x400, ~0xf, 1); | ||
903 | /* Turn off LCOMB */ | ||
904 | cx18_av_and_or(cx, 0x47b, ~6, 0); | ||
905 | } | ||
906 | cx18_av_and_or(cx, 0x400, ~0x2f, fmt | 0x20); | ||
907 | cx18_av_and_or(cx, 0x403, ~0x3, pal_m); | ||
908 | cx18_av_std_setup(cx); | ||
909 | input_change(cx); | ||
910 | return 0; | ||
911 | } | ||
912 | |||
913 | static int cx18_av_s_radio(struct v4l2_subdev *sd) | ||
914 | { | ||
915 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
916 | state->radio = 1; | ||
917 | return 0; | ||
918 | } | ||
919 | |||
920 | static int cx18_av_s_ctrl(struct v4l2_ctrl *ctrl) | ||
921 | { | ||
922 | struct v4l2_subdev *sd = to_sd(ctrl); | ||
923 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
924 | |||
925 | switch (ctrl->id) { | ||
926 | case V4L2_CID_BRIGHTNESS: | ||
927 | cx18_av_write(cx, 0x414, ctrl->val - 128); | ||
928 | break; | ||
929 | |||
930 | case V4L2_CID_CONTRAST: | ||
931 | cx18_av_write(cx, 0x415, ctrl->val << 1); | ||
932 | break; | ||
933 | |||
934 | case V4L2_CID_SATURATION: | ||
935 | cx18_av_write(cx, 0x420, ctrl->val << 1); | ||
936 | cx18_av_write(cx, 0x421, ctrl->val << 1); | ||
937 | break; | ||
938 | |||
939 | case V4L2_CID_HUE: | ||
940 | cx18_av_write(cx, 0x422, ctrl->val); | ||
941 | break; | ||
942 | |||
943 | default: | ||
944 | return -EINVAL; | ||
945 | } | ||
946 | return 0; | ||
947 | } | ||
948 | |||
949 | static int cx18_av_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) | ||
950 | { | ||
951 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
952 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
953 | int HSC, VSC, Vsrc, Hsrc, filter, Vlines; | ||
954 | int is_50Hz = !(state->std & V4L2_STD_525_60); | ||
955 | |||
956 | if (fmt->code != V4L2_MBUS_FMT_FIXED) | ||
957 | return -EINVAL; | ||
958 | |||
959 | fmt->field = V4L2_FIELD_INTERLACED; | ||
960 | fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; | ||
961 | |||
962 | Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4; | ||
963 | Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4; | ||
964 | |||
965 | Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4; | ||
966 | Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4; | ||
967 | |||
968 | /* | ||
969 | * This adjustment reflects the excess of vactive, set in | ||
970 | * cx18_av_std_setup(), above standard values: | ||
971 | * | ||
972 | * 480 + 1 for 60 Hz systems | ||
973 | * 576 + 3 for 50 Hz systems | ||
974 | */ | ||
975 | Vlines = fmt->height + (is_50Hz ? 3 : 1); | ||
976 | |||
977 | /* | ||
978 | * Invalid height and width scaling requests are: | ||
979 | * 1. width less than 1/16 of the source width | ||
980 | * 2. width greater than the source width | ||
981 | * 3. height less than 1/8 of the source height | ||
982 | * 4. height greater than the source height | ||
983 | */ | ||
984 | if ((fmt->width * 16 < Hsrc) || (Hsrc < fmt->width) || | ||
985 | (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { | ||
986 | CX18_ERR_DEV(sd, "%dx%d is not a valid size!\n", | ||
987 | fmt->width, fmt->height); | ||
988 | return -ERANGE; | ||
989 | } | ||
990 | |||
991 | HSC = (Hsrc * (1 << 20)) / fmt->width - (1 << 20); | ||
992 | VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); | ||
993 | VSC &= 0x1fff; | ||
994 | |||
995 | if (fmt->width >= 385) | ||
996 | filter = 0; | ||
997 | else if (fmt->width > 192) | ||
998 | filter = 1; | ||
999 | else if (fmt->width > 96) | ||
1000 | filter = 2; | ||
1001 | else | ||
1002 | filter = 3; | ||
1003 | |||
1004 | CX18_DEBUG_INFO_DEV(sd, | ||
1005 | "decoder set size %dx%d -> scale %ux%u\n", | ||
1006 | fmt->width, fmt->height, HSC, VSC); | ||
1007 | |||
1008 | /* HSCALE=HSC */ | ||
1009 | cx18_av_write(cx, 0x418, HSC & 0xff); | ||
1010 | cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff); | ||
1011 | cx18_av_write(cx, 0x41a, HSC >> 16); | ||
1012 | /* VSCALE=VSC */ | ||
1013 | cx18_av_write(cx, 0x41c, VSC & 0xff); | ||
1014 | cx18_av_write(cx, 0x41d, VSC >> 8); | ||
1015 | /* VS_INTRLACE=1 VFILT=filter */ | ||
1016 | cx18_av_write(cx, 0x41e, 0x8 | filter); | ||
1017 | return 0; | ||
1018 | } | ||
1019 | |||
1020 | static int cx18_av_s_stream(struct v4l2_subdev *sd, int enable) | ||
1021 | { | ||
1022 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
1023 | |||
1024 | CX18_DEBUG_INFO_DEV(sd, "%s output\n", enable ? "enable" : "disable"); | ||
1025 | if (enable) { | ||
1026 | cx18_av_write(cx, 0x115, 0x8c); | ||
1027 | cx18_av_write(cx, 0x116, 0x07); | ||
1028 | } else { | ||
1029 | cx18_av_write(cx, 0x115, 0x00); | ||
1030 | cx18_av_write(cx, 0x116, 0x00); | ||
1031 | } | ||
1032 | return 0; | ||
1033 | } | ||
1034 | |||
1035 | static void log_video_status(struct cx18 *cx) | ||
1036 | { | ||
1037 | static const char *const fmt_strs[] = { | ||
1038 | "0x0", | ||
1039 | "NTSC-M", "NTSC-J", "NTSC-4.43", | ||
1040 | "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", | ||
1041 | "0x9", "0xA", "0xB", | ||
1042 | "SECAM", | ||
1043 | "0xD", "0xE", "0xF" | ||
1044 | }; | ||
1045 | |||
1046 | struct cx18_av_state *state = &cx->av_state; | ||
1047 | struct v4l2_subdev *sd = &state->sd; | ||
1048 | u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf; | ||
1049 | u8 gen_stat1 = cx18_av_read(cx, 0x40d); | ||
1050 | u8 gen_stat2 = cx18_av_read(cx, 0x40e); | ||
1051 | int vid_input = state->vid_input; | ||
1052 | |||
1053 | CX18_INFO_DEV(sd, "Video signal: %spresent\n", | ||
1054 | (gen_stat2 & 0x20) ? "" : "not "); | ||
1055 | CX18_INFO_DEV(sd, "Detected format: %s\n", | ||
1056 | fmt_strs[gen_stat1 & 0xf]); | ||
1057 | |||
1058 | CX18_INFO_DEV(sd, "Specified standard: %s\n", | ||
1059 | vidfmt_sel ? fmt_strs[vidfmt_sel] | ||
1060 | : "automatic detection"); | ||
1061 | |||
1062 | if (vid_input >= CX18_AV_COMPOSITE1 && | ||
1063 | vid_input <= CX18_AV_COMPOSITE8) { | ||
1064 | CX18_INFO_DEV(sd, "Specified video input: Composite %d\n", | ||
1065 | vid_input - CX18_AV_COMPOSITE1 + 1); | ||
1066 | } else { | ||
1067 | CX18_INFO_DEV(sd, "Specified video input: " | ||
1068 | "S-Video (Luma In%d, Chroma In%d)\n", | ||
1069 | (vid_input & 0xf0) >> 4, | ||
1070 | (vid_input & 0xf00) >> 8); | ||
1071 | } | ||
1072 | |||
1073 | CX18_INFO_DEV(sd, "Specified audioclock freq: %d Hz\n", | ||
1074 | state->audclk_freq); | ||
1075 | } | ||
1076 | |||
1077 | static void log_audio_status(struct cx18 *cx) | ||
1078 | { | ||
1079 | struct cx18_av_state *state = &cx->av_state; | ||
1080 | struct v4l2_subdev *sd = &state->sd; | ||
1081 | u8 download_ctl = cx18_av_read(cx, 0x803); | ||
1082 | u8 mod_det_stat0 = cx18_av_read(cx, 0x804); | ||
1083 | u8 mod_det_stat1 = cx18_av_read(cx, 0x805); | ||
1084 | u8 audio_config = cx18_av_read(cx, 0x808); | ||
1085 | u8 pref_mode = cx18_av_read(cx, 0x809); | ||
1086 | u8 afc0 = cx18_av_read(cx, 0x80b); | ||
1087 | u8 mute_ctl = cx18_av_read(cx, 0x8d3); | ||
1088 | int aud_input = state->aud_input; | ||
1089 | char *p; | ||
1090 | |||
1091 | switch (mod_det_stat0) { | ||
1092 | case 0x00: p = "mono"; break; | ||
1093 | case 0x01: p = "stereo"; break; | ||
1094 | case 0x02: p = "dual"; break; | ||
1095 | case 0x04: p = "tri"; break; | ||
1096 | case 0x10: p = "mono with SAP"; break; | ||
1097 | case 0x11: p = "stereo with SAP"; break; | ||
1098 | case 0x12: p = "dual with SAP"; break; | ||
1099 | case 0x14: p = "tri with SAP"; break; | ||
1100 | case 0xfe: p = "forced mode"; break; | ||
1101 | default: p = "not defined"; break; | ||
1102 | } | ||
1103 | CX18_INFO_DEV(sd, "Detected audio mode: %s\n", p); | ||
1104 | |||
1105 | switch (mod_det_stat1) { | ||
1106 | case 0x00: p = "not defined"; break; | ||
1107 | case 0x01: p = "EIAJ"; break; | ||
1108 | case 0x02: p = "A2-M"; break; | ||
1109 | case 0x03: p = "A2-BG"; break; | ||
1110 | case 0x04: p = "A2-DK1"; break; | ||
1111 | case 0x05: p = "A2-DK2"; break; | ||
1112 | case 0x06: p = "A2-DK3"; break; | ||
1113 | case 0x07: p = "A1 (6.0 MHz FM Mono)"; break; | ||
1114 | case 0x08: p = "AM-L"; break; | ||
1115 | case 0x09: p = "NICAM-BG"; break; | ||
1116 | case 0x0a: p = "NICAM-DK"; break; | ||
1117 | case 0x0b: p = "NICAM-I"; break; | ||
1118 | case 0x0c: p = "NICAM-L"; break; | ||
1119 | case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break; | ||
1120 | case 0x0e: p = "IF FM Radio"; break; | ||
1121 | case 0x0f: p = "BTSC"; break; | ||
1122 | case 0x10: p = "detected chrominance"; break; | ||
1123 | case 0xfd: p = "unknown audio standard"; break; | ||
1124 | case 0xfe: p = "forced audio standard"; break; | ||
1125 | case 0xff: p = "no detected audio standard"; break; | ||
1126 | default: p = "not defined"; break; | ||
1127 | } | ||
1128 | CX18_INFO_DEV(sd, "Detected audio standard: %s\n", p); | ||
1129 | CX18_INFO_DEV(sd, "Audio muted: %s\n", | ||
1130 | (mute_ctl & 0x2) ? "yes" : "no"); | ||
1131 | CX18_INFO_DEV(sd, "Audio microcontroller: %s\n", | ||
1132 | (download_ctl & 0x10) ? "running" : "stopped"); | ||
1133 | |||
1134 | switch (audio_config >> 4) { | ||
1135 | case 0x00: p = "undefined"; break; | ||
1136 | case 0x01: p = "BTSC"; break; | ||
1137 | case 0x02: p = "EIAJ"; break; | ||
1138 | case 0x03: p = "A2-M"; break; | ||
1139 | case 0x04: p = "A2-BG"; break; | ||
1140 | case 0x05: p = "A2-DK1"; break; | ||
1141 | case 0x06: p = "A2-DK2"; break; | ||
1142 | case 0x07: p = "A2-DK3"; break; | ||
1143 | case 0x08: p = "A1 (6.0 MHz FM Mono)"; break; | ||
1144 | case 0x09: p = "AM-L"; break; | ||
1145 | case 0x0a: p = "NICAM-BG"; break; | ||
1146 | case 0x0b: p = "NICAM-DK"; break; | ||
1147 | case 0x0c: p = "NICAM-I"; break; | ||
1148 | case 0x0d: p = "NICAM-L"; break; | ||
1149 | case 0x0e: p = "FM radio"; break; | ||
1150 | case 0x0f: p = "automatic detection"; break; | ||
1151 | default: p = "undefined"; break; | ||
1152 | } | ||
1153 | CX18_INFO_DEV(sd, "Configured audio standard: %s\n", p); | ||
1154 | |||
1155 | if ((audio_config >> 4) < 0xF) { | ||
1156 | switch (audio_config & 0xF) { | ||
1157 | case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break; | ||
1158 | case 0x01: p = "MONO2 (LANGUAGE B)"; break; | ||
1159 | case 0x02: p = "MONO3 (STEREO forced MONO)"; break; | ||
1160 | case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break; | ||
1161 | case 0x04: p = "STEREO"; break; | ||
1162 | case 0x05: p = "DUAL1 (AC)"; break; | ||
1163 | case 0x06: p = "DUAL2 (BC)"; break; | ||
1164 | case 0x07: p = "DUAL3 (AB)"; break; | ||
1165 | default: p = "undefined"; | ||
1166 | } | ||
1167 | CX18_INFO_DEV(sd, "Configured audio mode: %s\n", p); | ||
1168 | } else { | ||
1169 | switch (audio_config & 0xF) { | ||
1170 | case 0x00: p = "BG"; break; | ||
1171 | case 0x01: p = "DK1"; break; | ||
1172 | case 0x02: p = "DK2"; break; | ||
1173 | case 0x03: p = "DK3"; break; | ||
1174 | case 0x04: p = "I"; break; | ||
1175 | case 0x05: p = "L"; break; | ||
1176 | case 0x06: p = "BTSC"; break; | ||
1177 | case 0x07: p = "EIAJ"; break; | ||
1178 | case 0x08: p = "A2-M"; break; | ||
1179 | case 0x09: p = "FM Radio (4.5 MHz)"; break; | ||
1180 | case 0x0a: p = "FM Radio (5.5 MHz)"; break; | ||
1181 | case 0x0b: p = "S-Video"; break; | ||
1182 | case 0x0f: p = "automatic standard and mode detection"; break; | ||
1183 | default: p = "undefined"; break; | ||
1184 | } | ||
1185 | CX18_INFO_DEV(sd, "Configured audio system: %s\n", p); | ||
1186 | } | ||
1187 | |||
1188 | if (aud_input) | ||
1189 | CX18_INFO_DEV(sd, "Specified audio input: Tuner (In%d)\n", | ||
1190 | aud_input); | ||
1191 | else | ||
1192 | CX18_INFO_DEV(sd, "Specified audio input: External\n"); | ||
1193 | |||
1194 | switch (pref_mode & 0xf) { | ||
1195 | case 0: p = "mono/language A"; break; | ||
1196 | case 1: p = "language B"; break; | ||
1197 | case 2: p = "language C"; break; | ||
1198 | case 3: p = "analog fallback"; break; | ||
1199 | case 4: p = "stereo"; break; | ||
1200 | case 5: p = "language AC"; break; | ||
1201 | case 6: p = "language BC"; break; | ||
1202 | case 7: p = "language AB"; break; | ||
1203 | default: p = "undefined"; break; | ||
1204 | } | ||
1205 | CX18_INFO_DEV(sd, "Preferred audio mode: %s\n", p); | ||
1206 | |||
1207 | if ((audio_config & 0xf) == 0xf) { | ||
1208 | switch ((afc0 >> 3) & 0x1) { | ||
1209 | case 0: p = "system DK"; break; | ||
1210 | case 1: p = "system L"; break; | ||
1211 | } | ||
1212 | CX18_INFO_DEV(sd, "Selected 65 MHz format: %s\n", p); | ||
1213 | |||
1214 | switch (afc0 & 0x7) { | ||
1215 | case 0: p = "Chroma"; break; | ||
1216 | case 1: p = "BTSC"; break; | ||
1217 | case 2: p = "EIAJ"; break; | ||
1218 | case 3: p = "A2-M"; break; | ||
1219 | case 4: p = "autodetect"; break; | ||
1220 | default: p = "undefined"; break; | ||
1221 | } | ||
1222 | CX18_INFO_DEV(sd, "Selected 45 MHz format: %s\n", p); | ||
1223 | } | ||
1224 | } | ||
1225 | |||
1226 | static int cx18_av_log_status(struct v4l2_subdev *sd) | ||
1227 | { | ||
1228 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
1229 | log_video_status(cx); | ||
1230 | log_audio_status(cx); | ||
1231 | return 0; | ||
1232 | } | ||
1233 | |||
1234 | static inline int cx18_av_dbg_match(const struct v4l2_dbg_match *match) | ||
1235 | { | ||
1236 | return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 1; | ||
1237 | } | ||
1238 | |||
1239 | static int cx18_av_g_chip_ident(struct v4l2_subdev *sd, | ||
1240 | struct v4l2_dbg_chip_ident *chip) | ||
1241 | { | ||
1242 | struct cx18_av_state *state = to_cx18_av_state(sd); | ||
1243 | |||
1244 | if (cx18_av_dbg_match(&chip->match)) { | ||
1245 | chip->ident = state->id; | ||
1246 | chip->revision = state->rev; | ||
1247 | } | ||
1248 | return 0; | ||
1249 | } | ||
1250 | |||
1251 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1252 | static int cx18_av_g_register(struct v4l2_subdev *sd, | ||
1253 | struct v4l2_dbg_register *reg) | ||
1254 | { | ||
1255 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
1256 | |||
1257 | if (!cx18_av_dbg_match(®->match)) | ||
1258 | return -EINVAL; | ||
1259 | if ((reg->reg & 0x3) != 0) | ||
1260 | return -EINVAL; | ||
1261 | if (!capable(CAP_SYS_ADMIN)) | ||
1262 | return -EPERM; | ||
1263 | reg->size = 4; | ||
1264 | reg->val = cx18_av_read4(cx, reg->reg & 0x00000ffc); | ||
1265 | return 0; | ||
1266 | } | ||
1267 | |||
1268 | static int cx18_av_s_register(struct v4l2_subdev *sd, | ||
1269 | struct v4l2_dbg_register *reg) | ||
1270 | { | ||
1271 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
1272 | |||
1273 | if (!cx18_av_dbg_match(®->match)) | ||
1274 | return -EINVAL; | ||
1275 | if ((reg->reg & 0x3) != 0) | ||
1276 | return -EINVAL; | ||
1277 | if (!capable(CAP_SYS_ADMIN)) | ||
1278 | return -EPERM; | ||
1279 | cx18_av_write4(cx, reg->reg & 0x00000ffc, reg->val); | ||
1280 | return 0; | ||
1281 | } | ||
1282 | #endif | ||
1283 | |||
1284 | static const struct v4l2_ctrl_ops cx18_av_ctrl_ops = { | ||
1285 | .s_ctrl = cx18_av_s_ctrl, | ||
1286 | }; | ||
1287 | |||
1288 | static const struct v4l2_subdev_core_ops cx18_av_general_ops = { | ||
1289 | .g_chip_ident = cx18_av_g_chip_ident, | ||
1290 | .log_status = cx18_av_log_status, | ||
1291 | .load_fw = cx18_av_load_fw, | ||
1292 | .reset = cx18_av_reset, | ||
1293 | .g_ctrl = v4l2_subdev_g_ctrl, | ||
1294 | .s_ctrl = v4l2_subdev_s_ctrl, | ||
1295 | .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, | ||
1296 | .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, | ||
1297 | .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, | ||
1298 | .queryctrl = v4l2_subdev_queryctrl, | ||
1299 | .querymenu = v4l2_subdev_querymenu, | ||
1300 | .s_std = cx18_av_s_std, | ||
1301 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1302 | .g_register = cx18_av_g_register, | ||
1303 | .s_register = cx18_av_s_register, | ||
1304 | #endif | ||
1305 | }; | ||
1306 | |||
1307 | static const struct v4l2_subdev_tuner_ops cx18_av_tuner_ops = { | ||
1308 | .s_radio = cx18_av_s_radio, | ||
1309 | .s_frequency = cx18_av_s_frequency, | ||
1310 | .g_tuner = cx18_av_g_tuner, | ||
1311 | .s_tuner = cx18_av_s_tuner, | ||
1312 | }; | ||
1313 | |||
1314 | static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = { | ||
1315 | .s_clock_freq = cx18_av_s_clock_freq, | ||
1316 | .s_routing = cx18_av_s_audio_routing, | ||
1317 | }; | ||
1318 | |||
1319 | static const struct v4l2_subdev_video_ops cx18_av_video_ops = { | ||
1320 | .s_routing = cx18_av_s_video_routing, | ||
1321 | .s_stream = cx18_av_s_stream, | ||
1322 | .s_mbus_fmt = cx18_av_s_mbus_fmt, | ||
1323 | }; | ||
1324 | |||
1325 | static const struct v4l2_subdev_vbi_ops cx18_av_vbi_ops = { | ||
1326 | .decode_vbi_line = cx18_av_decode_vbi_line, | ||
1327 | .g_sliced_fmt = cx18_av_g_sliced_fmt, | ||
1328 | .s_sliced_fmt = cx18_av_s_sliced_fmt, | ||
1329 | .s_raw_fmt = cx18_av_s_raw_fmt, | ||
1330 | }; | ||
1331 | |||
1332 | static const struct v4l2_subdev_ops cx18_av_ops = { | ||
1333 | .core = &cx18_av_general_ops, | ||
1334 | .tuner = &cx18_av_tuner_ops, | ||
1335 | .audio = &cx18_av_audio_ops, | ||
1336 | .video = &cx18_av_video_ops, | ||
1337 | .vbi = &cx18_av_vbi_ops, | ||
1338 | }; | ||
1339 | |||
1340 | int cx18_av_probe(struct cx18 *cx) | ||
1341 | { | ||
1342 | struct cx18_av_state *state = &cx->av_state; | ||
1343 | struct v4l2_subdev *sd; | ||
1344 | int err; | ||
1345 | |||
1346 | state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff; | ||
1347 | state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO) | ||
1348 | ? V4L2_IDENT_CX23418_843 : V4L2_IDENT_UNKNOWN; | ||
1349 | |||
1350 | state->vid_input = CX18_AV_COMPOSITE7; | ||
1351 | state->aud_input = CX18_AV_AUDIO8; | ||
1352 | state->audclk_freq = 48000; | ||
1353 | state->audmode = V4L2_TUNER_MODE_LANG1; | ||
1354 | state->slicer_line_delay = 0; | ||
1355 | state->slicer_line_offset = (10 + state->slicer_line_delay - 2); | ||
1356 | |||
1357 | sd = &state->sd; | ||
1358 | v4l2_subdev_init(sd, &cx18_av_ops); | ||
1359 | v4l2_set_subdevdata(sd, cx); | ||
1360 | snprintf(sd->name, sizeof(sd->name), | ||
1361 | "%s %03x", cx->v4l2_dev.name, (state->rev >> 4)); | ||
1362 | sd->grp_id = CX18_HW_418_AV; | ||
1363 | v4l2_ctrl_handler_init(&state->hdl, 9); | ||
1364 | v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, | ||
1365 | V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); | ||
1366 | v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, | ||
1367 | V4L2_CID_CONTRAST, 0, 127, 1, 64); | ||
1368 | v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, | ||
1369 | V4L2_CID_SATURATION, 0, 127, 1, 64); | ||
1370 | v4l2_ctrl_new_std(&state->hdl, &cx18_av_ctrl_ops, | ||
1371 | V4L2_CID_HUE, -128, 127, 1, 0); | ||
1372 | |||
1373 | state->volume = v4l2_ctrl_new_std(&state->hdl, | ||
1374 | &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_VOLUME, | ||
1375 | 0, 65535, 65535 / 100, 0); | ||
1376 | v4l2_ctrl_new_std(&state->hdl, | ||
1377 | &cx18_av_audio_ctrl_ops, V4L2_CID_AUDIO_MUTE, | ||
1378 | 0, 1, 1, 0); | ||
1379 | v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops, | ||
1380 | V4L2_CID_AUDIO_BALANCE, | ||
1381 | 0, 65535, 65535 / 100, 32768); | ||
1382 | v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops, | ||
1383 | V4L2_CID_AUDIO_BASS, | ||
1384 | 0, 65535, 65535 / 100, 32768); | ||
1385 | v4l2_ctrl_new_std(&state->hdl, &cx18_av_audio_ctrl_ops, | ||
1386 | V4L2_CID_AUDIO_TREBLE, | ||
1387 | 0, 65535, 65535 / 100, 32768); | ||
1388 | sd->ctrl_handler = &state->hdl; | ||
1389 | if (state->hdl.error) { | ||
1390 | int err = state->hdl.error; | ||
1391 | |||
1392 | v4l2_ctrl_handler_free(&state->hdl); | ||
1393 | return err; | ||
1394 | } | ||
1395 | err = v4l2_device_register_subdev(&cx->v4l2_dev, sd); | ||
1396 | if (err) | ||
1397 | v4l2_ctrl_handler_free(&state->hdl); | ||
1398 | else | ||
1399 | cx18_av_init(cx); | ||
1400 | return err; | ||
1401 | } | ||
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h new file mode 100644 index 00000000000..e9c69d9c9e4 --- /dev/null +++ b/drivers/media/video/cx18/cx18-av-core.h | |||
@@ -0,0 +1,391 @@ | |||
1 | /* | ||
2 | * cx18 ADEC header | ||
3 | * | ||
4 | * Derived from cx25840-core.h | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version 2 | ||
12 | * of the License, or (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., 51 Franklin Street, Fifth Floor, Boston, MA | ||
22 | * 02110-1301, USA. | ||
23 | */ | ||
24 | |||
25 | #ifndef _CX18_AV_CORE_H_ | ||
26 | #define _CX18_AV_CORE_H_ | ||
27 | |||
28 | #include <media/v4l2-device.h> | ||
29 | #include <media/v4l2-ctrls.h> | ||
30 | |||
31 | struct cx18; | ||
32 | |||
33 | enum cx18_av_video_input { | ||
34 | /* Composite video inputs In1-In8 */ | ||
35 | CX18_AV_COMPOSITE1 = 1, | ||
36 | CX18_AV_COMPOSITE2, | ||
37 | CX18_AV_COMPOSITE3, | ||
38 | CX18_AV_COMPOSITE4, | ||
39 | CX18_AV_COMPOSITE5, | ||
40 | CX18_AV_COMPOSITE6, | ||
41 | CX18_AV_COMPOSITE7, | ||
42 | CX18_AV_COMPOSITE8, | ||
43 | |||
44 | /* S-Video inputs consist of one luma input (In1-In8) ORed with one | ||
45 | chroma input (In5-In8) */ | ||
46 | CX18_AV_SVIDEO_LUMA1 = 0x10, | ||
47 | CX18_AV_SVIDEO_LUMA2 = 0x20, | ||
48 | CX18_AV_SVIDEO_LUMA3 = 0x30, | ||
49 | CX18_AV_SVIDEO_LUMA4 = 0x40, | ||
50 | CX18_AV_SVIDEO_LUMA5 = 0x50, | ||
51 | CX18_AV_SVIDEO_LUMA6 = 0x60, | ||
52 | CX18_AV_SVIDEO_LUMA7 = 0x70, | ||
53 | CX18_AV_SVIDEO_LUMA8 = 0x80, | ||
54 | CX18_AV_SVIDEO_CHROMA4 = 0x400, | ||
55 | CX18_AV_SVIDEO_CHROMA5 = 0x500, | ||
56 | CX18_AV_SVIDEO_CHROMA6 = 0x600, | ||
57 | CX18_AV_SVIDEO_CHROMA7 = 0x700, | ||
58 | CX18_AV_SVIDEO_CHROMA8 = 0x800, | ||
59 | |||
60 | /* S-Video aliases for common luma/chroma combinations */ | ||
61 | CX18_AV_SVIDEO1 = 0x510, | ||
62 | CX18_AV_SVIDEO2 = 0x620, | ||
63 | CX18_AV_SVIDEO3 = 0x730, | ||
64 | CX18_AV_SVIDEO4 = 0x840, | ||
65 | |||
66 | /* Component Video inputs consist of one luma input (In1-In8) ORed | ||
67 | with a red chroma (In4-In6) and blue chroma input (In7-In8) */ | ||
68 | CX18_AV_COMPONENT_LUMA1 = 0x1000, | ||
69 | CX18_AV_COMPONENT_LUMA2 = 0x2000, | ||
70 | CX18_AV_COMPONENT_LUMA3 = 0x3000, | ||
71 | CX18_AV_COMPONENT_LUMA4 = 0x4000, | ||
72 | CX18_AV_COMPONENT_LUMA5 = 0x5000, | ||
73 | CX18_AV_COMPONENT_LUMA6 = 0x6000, | ||
74 | CX18_AV_COMPONENT_LUMA7 = 0x7000, | ||
75 | CX18_AV_COMPONENT_LUMA8 = 0x8000, | ||
76 | CX18_AV_COMPONENT_R_CHROMA4 = 0x40000, | ||
77 | CX18_AV_COMPONENT_R_CHROMA5 = 0x50000, | ||
78 | CX18_AV_COMPONENT_R_CHROMA6 = 0x60000, | ||
79 | CX18_AV_COMPONENT_B_CHROMA7 = 0x700000, | ||
80 | CX18_AV_COMPONENT_B_CHROMA8 = 0x800000, | ||
81 | |||
82 | /* Component Video aliases for common combinations */ | ||
83 | CX18_AV_COMPONENT1 = 0x861000, | ||
84 | }; | ||
85 | |||
86 | enum cx18_av_audio_input { | ||
87 | /* Audio inputs: serial or In4-In8 */ | ||
88 | CX18_AV_AUDIO_SERIAL1, | ||
89 | CX18_AV_AUDIO_SERIAL2, | ||
90 | CX18_AV_AUDIO4 = 4, | ||
91 | CX18_AV_AUDIO5, | ||
92 | CX18_AV_AUDIO6, | ||
93 | CX18_AV_AUDIO7, | ||
94 | CX18_AV_AUDIO8, | ||
95 | }; | ||
96 | |||
97 | struct cx18_av_state { | ||
98 | struct v4l2_subdev sd; | ||
99 | struct v4l2_ctrl_handler hdl; | ||
100 | struct v4l2_ctrl *volume; | ||
101 | int radio; | ||
102 | v4l2_std_id std; | ||
103 | enum cx18_av_video_input vid_input; | ||
104 | enum cx18_av_audio_input aud_input; | ||
105 | u32 audclk_freq; | ||
106 | int audmode; | ||
107 | u32 id; | ||
108 | u32 rev; | ||
109 | int is_initialized; | ||
110 | |||
111 | /* | ||
112 | * The VBI slicer starts operating and counting lines, beginning at | ||
113 | * slicer line count of 1, at D lines after the deassertion of VRESET. | ||
114 | * This staring field line, S, is 6 (& 319) or 10 (& 273) for 625 or 525 | ||
115 | * line systems respectively. Sliced ancillary data captured on VBI | ||
116 | * slicer line M is inserted after the VBI slicer is done with line M, | ||
117 | * when VBI slicer line count is N = M+1. Thus when the VBI slicer | ||
118 | * reports a VBI slicer line number with ancillary data, the IDID0 byte | ||
119 | * indicates VBI slicer line N. The actual field line that the captured | ||
120 | * data comes from is | ||
121 | * | ||
122 | * L = M+(S+D-1) = N-1+(S+D-1) = N + (S+D-2). | ||
123 | * | ||
124 | * L is the line in the field, not frame, from which the VBI data came. | ||
125 | * N is the line reported by the slicer in the ancillary data. | ||
126 | * D is the slicer_line_delay value programmed into register 0x47f. | ||
127 | * S is 6 for 625 line systems or 10 for 525 line systems | ||
128 | * (S+D-2) is the slicer_line_offset used to convert slicer reported | ||
129 | * line counts to actual field lines. | ||
130 | */ | ||
131 | int slicer_line_delay; | ||
132 | int slicer_line_offset; | ||
133 | }; | ||
134 | |||
135 | |||
136 | /* Registers */ | ||
137 | #define CXADEC_CHIP_TYPE_TIGER 0x837 | ||
138 | #define CXADEC_CHIP_TYPE_MAKO 0x843 | ||
139 | |||
140 | #define CXADEC_HOST_REG1 0x000 | ||
141 | #define CXADEC_HOST_REG2 0x001 | ||
142 | |||
143 | #define CXADEC_CHIP_CTRL 0x100 | ||
144 | #define CXADEC_AFE_CTRL 0x104 | ||
145 | #define CXADEC_PLL_CTRL1 0x108 | ||
146 | #define CXADEC_VID_PLL_FRAC 0x10C | ||
147 | #define CXADEC_AUX_PLL_FRAC 0x110 | ||
148 | #define CXADEC_PIN_CTRL1 0x114 | ||
149 | #define CXADEC_PIN_CTRL2 0x118 | ||
150 | #define CXADEC_PIN_CFG1 0x11C | ||
151 | #define CXADEC_PIN_CFG2 0x120 | ||
152 | |||
153 | #define CXADEC_PIN_CFG3 0x124 | ||
154 | #define CXADEC_I2S_MCLK 0x127 | ||
155 | |||
156 | #define CXADEC_AUD_LOCK1 0x128 | ||
157 | #define CXADEC_AUD_LOCK2 0x12C | ||
158 | #define CXADEC_POWER_CTRL 0x130 | ||
159 | #define CXADEC_AFE_DIAG_CTRL1 0x134 | ||
160 | #define CXADEC_AFE_DIAG_CTRL2 0x138 | ||
161 | #define CXADEC_AFE_DIAG_CTRL3 0x13C | ||
162 | #define CXADEC_PLL_DIAG_CTRL 0x140 | ||
163 | #define CXADEC_TEST_CTRL1 0x144 | ||
164 | #define CXADEC_TEST_CTRL2 0x148 | ||
165 | #define CXADEC_BIST_STAT 0x14C | ||
166 | #define CXADEC_DLL1_DIAG_CTRL 0x158 | ||
167 | #define CXADEC_DLL2_DIAG_CTRL 0x15C | ||
168 | |||
169 | /* IR registers */ | ||
170 | #define CXADEC_IR_CTRL_REG 0x200 | ||
171 | #define CXADEC_IR_TXCLK_REG 0x204 | ||
172 | #define CXADEC_IR_RXCLK_REG 0x208 | ||
173 | #define CXADEC_IR_CDUTY_REG 0x20C | ||
174 | #define CXADEC_IR_STAT_REG 0x210 | ||
175 | #define CXADEC_IR_IRQEN_REG 0x214 | ||
176 | #define CXADEC_IR_FILTER_REG 0x218 | ||
177 | #define CXADEC_IR_FIFO_REG 0x21C | ||
178 | |||
179 | /* Video Registers */ | ||
180 | #define CXADEC_MODE_CTRL 0x400 | ||
181 | #define CXADEC_OUT_CTRL1 0x404 | ||
182 | #define CXADEC_OUT_CTRL2 0x408 | ||
183 | #define CXADEC_GEN_STAT 0x40C | ||
184 | #define CXADEC_INT_STAT_MASK 0x410 | ||
185 | #define CXADEC_LUMA_CTRL 0x414 | ||
186 | |||
187 | #define CXADEC_BRIGHTNESS_CTRL_BYTE 0x414 | ||
188 | #define CXADEC_CONTRAST_CTRL_BYTE 0x415 | ||
189 | #define CXADEC_LUMA_CTRL_BYTE_3 0x416 | ||
190 | |||
191 | #define CXADEC_HSCALE_CTRL 0x418 | ||
192 | #define CXADEC_VSCALE_CTRL 0x41C | ||
193 | |||
194 | #define CXADEC_CHROMA_CTRL 0x420 | ||
195 | |||
196 | #define CXADEC_USAT_CTRL_BYTE 0x420 | ||
197 | #define CXADEC_VSAT_CTRL_BYTE 0x421 | ||
198 | #define CXADEC_HUE_CTRL_BYTE 0x422 | ||
199 | |||
200 | #define CXADEC_VBI_LINE_CTRL1 0x424 | ||
201 | #define CXADEC_VBI_LINE_CTRL2 0x428 | ||
202 | #define CXADEC_VBI_LINE_CTRL3 0x42C | ||
203 | #define CXADEC_VBI_LINE_CTRL4 0x430 | ||
204 | #define CXADEC_VBI_LINE_CTRL5 0x434 | ||
205 | #define CXADEC_VBI_FC_CFG 0x438 | ||
206 | #define CXADEC_VBI_MISC_CFG1 0x43C | ||
207 | #define CXADEC_VBI_MISC_CFG2 0x440 | ||
208 | #define CXADEC_VBI_PAY1 0x444 | ||
209 | #define CXADEC_VBI_PAY2 0x448 | ||
210 | #define CXADEC_VBI_CUST1_CFG1 0x44C | ||
211 | #define CXADEC_VBI_CUST1_CFG2 0x450 | ||
212 | #define CXADEC_VBI_CUST1_CFG3 0x454 | ||
213 | #define CXADEC_VBI_CUST2_CFG1 0x458 | ||
214 | #define CXADEC_VBI_CUST2_CFG2 0x45C | ||
215 | #define CXADEC_VBI_CUST2_CFG3 0x460 | ||
216 | #define CXADEC_VBI_CUST3_CFG1 0x464 | ||
217 | #define CXADEC_VBI_CUST3_CFG2 0x468 | ||
218 | #define CXADEC_VBI_CUST3_CFG3 0x46C | ||
219 | #define CXADEC_HORIZ_TIM_CTRL 0x470 | ||
220 | #define CXADEC_VERT_TIM_CTRL 0x474 | ||
221 | #define CXADEC_SRC_COMB_CFG 0x478 | ||
222 | #define CXADEC_CHROMA_VBIOFF_CFG 0x47C | ||
223 | #define CXADEC_FIELD_COUNT 0x480 | ||
224 | #define CXADEC_MISC_TIM_CTRL 0x484 | ||
225 | #define CXADEC_DFE_CTRL1 0x488 | ||
226 | #define CXADEC_DFE_CTRL2 0x48C | ||
227 | #define CXADEC_DFE_CTRL3 0x490 | ||
228 | #define CXADEC_PLL_CTRL2 0x494 | ||
229 | #define CXADEC_HTL_CTRL 0x498 | ||
230 | #define CXADEC_COMB_CTRL 0x49C | ||
231 | #define CXADEC_CRUSH_CTRL 0x4A0 | ||
232 | #define CXADEC_SOFT_RST_CTRL 0x4A4 | ||
233 | #define CXADEC_MV_DT_CTRL2 0x4A8 | ||
234 | #define CXADEC_MV_DT_CTRL3 0x4AC | ||
235 | #define CXADEC_MISC_DIAG_CTRL 0x4B8 | ||
236 | |||
237 | #define CXADEC_DL_CTL 0x800 | ||
238 | #define CXADEC_DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */ | ||
239 | #define CXADEC_DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */ | ||
240 | #define CXADEC_DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */ | ||
241 | #define CXADEC_DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */ | ||
242 | |||
243 | #define CXADEC_STD_DET_STATUS 0x804 | ||
244 | |||
245 | #define CXADEC_STD_DET_CTL 0x808 | ||
246 | #define CXADEC_STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */ | ||
247 | #define CXADEC_STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */ | ||
248 | |||
249 | #define CXADEC_DW8051_INT 0x80C | ||
250 | #define CXADEC_GENERAL_CTL 0x810 | ||
251 | #define CXADEC_AAGC_CTL 0x814 | ||
252 | #define CXADEC_IF_SRC_CTL 0x818 | ||
253 | #define CXADEC_ANLOG_DEMOD_CTL 0x81C | ||
254 | #define CXADEC_ROT_FREQ_CTL 0x820 | ||
255 | #define CXADEC_FM1_CTL 0x824 | ||
256 | #define CXADEC_PDF_CTL 0x828 | ||
257 | #define CXADEC_DFT1_CTL1 0x82C | ||
258 | #define CXADEC_DFT1_CTL2 0x830 | ||
259 | #define CXADEC_DFT_STATUS 0x834 | ||
260 | #define CXADEC_DFT2_CTL1 0x838 | ||
261 | #define CXADEC_DFT2_CTL2 0x83C | ||
262 | #define CXADEC_DFT2_STATUS 0x840 | ||
263 | #define CXADEC_DFT3_CTL1 0x844 | ||
264 | #define CXADEC_DFT3_CTL2 0x848 | ||
265 | #define CXADEC_DFT3_STATUS 0x84C | ||
266 | #define CXADEC_DFT4_CTL1 0x850 | ||
267 | #define CXADEC_DFT4_CTL2 0x854 | ||
268 | #define CXADEC_DFT4_STATUS 0x858 | ||
269 | #define CXADEC_AM_MTS_DET 0x85C | ||
270 | #define CXADEC_ANALOG_MUX_CTL 0x860 | ||
271 | #define CXADEC_DIG_PLL_CTL1 0x864 | ||
272 | #define CXADEC_DIG_PLL_CTL2 0x868 | ||
273 | #define CXADEC_DIG_PLL_CTL3 0x86C | ||
274 | #define CXADEC_DIG_PLL_CTL4 0x870 | ||
275 | #define CXADEC_DIG_PLL_CTL5 0x874 | ||
276 | #define CXADEC_DEEMPH_GAIN_CTL 0x878 | ||
277 | #define CXADEC_DEEMPH_COEF1 0x87C | ||
278 | #define CXADEC_DEEMPH_COEF2 0x880 | ||
279 | #define CXADEC_DBX1_CTL1 0x884 | ||
280 | #define CXADEC_DBX1_CTL2 0x888 | ||
281 | #define CXADEC_DBX1_STATUS 0x88C | ||
282 | #define CXADEC_DBX2_CTL1 0x890 | ||
283 | #define CXADEC_DBX2_CTL2 0x894 | ||
284 | #define CXADEC_DBX2_STATUS 0x898 | ||
285 | #define CXADEC_AM_FM_DIFF 0x89C | ||
286 | |||
287 | /* NICAM registers go here */ | ||
288 | #define CXADEC_NICAM_STATUS 0x8C8 | ||
289 | #define CXADEC_DEMATRIX_CTL 0x8CC | ||
290 | |||
291 | #define CXADEC_PATH1_CTL1 0x8D0 | ||
292 | #define CXADEC_PATH1_VOL_CTL 0x8D4 | ||
293 | #define CXADEC_PATH1_EQ_CTL 0x8D8 | ||
294 | #define CXADEC_PATH1_SC_CTL 0x8DC | ||
295 | |||
296 | #define CXADEC_PATH2_CTL1 0x8E0 | ||
297 | #define CXADEC_PATH2_VOL_CTL 0x8E4 | ||
298 | #define CXADEC_PATH2_EQ_CTL 0x8E8 | ||
299 | #define CXADEC_PATH2_SC_CTL 0x8EC | ||
300 | |||
301 | #define CXADEC_SRC_CTL 0x8F0 | ||
302 | #define CXADEC_SRC_LF_COEF 0x8F4 | ||
303 | #define CXADEC_SRC1_CTL 0x8F8 | ||
304 | #define CXADEC_SRC2_CTL 0x8FC | ||
305 | #define CXADEC_SRC3_CTL 0x900 | ||
306 | #define CXADEC_SRC4_CTL 0x904 | ||
307 | #define CXADEC_SRC5_CTL 0x908 | ||
308 | #define CXADEC_SRC6_CTL 0x90C | ||
309 | |||
310 | #define CXADEC_BASEBAND_OUT_SEL 0x910 | ||
311 | #define CXADEC_I2S_IN_CTL 0x914 | ||
312 | #define CXADEC_I2S_OUT_CTL 0x918 | ||
313 | #define CXADEC_AC97_CTL 0x91C | ||
314 | #define CXADEC_QAM_PDF 0x920 | ||
315 | #define CXADEC_QAM_CONST_DEC 0x924 | ||
316 | #define CXADEC_QAM_ROTATOR_FREQ 0x948 | ||
317 | |||
318 | /* Bit definitions / settings used in Mako Audio */ | ||
319 | #define CXADEC_PREF_MODE_MONO_LANGA 0 | ||
320 | #define CXADEC_PREF_MODE_MONO_LANGB 1 | ||
321 | #define CXADEC_PREF_MODE_MONO_LANGC 2 | ||
322 | #define CXADEC_PREF_MODE_FALLBACK 3 | ||
323 | #define CXADEC_PREF_MODE_STEREO 4 | ||
324 | #define CXADEC_PREF_MODE_DUAL_LANG_AC 5 | ||
325 | #define CXADEC_PREF_MODE_DUAL_LANG_BC 6 | ||
326 | #define CXADEC_PREF_MODE_DUAL_LANG_AB 7 | ||
327 | |||
328 | |||
329 | #define CXADEC_DETECT_STEREO 1 | ||
330 | #define CXADEC_DETECT_DUAL 2 | ||
331 | #define CXADEC_DETECT_TRI 4 | ||
332 | #define CXADEC_DETECT_SAP 0x10 | ||
333 | #define CXADEC_DETECT_NO_SIGNAL 0xFF | ||
334 | |||
335 | #define CXADEC_SELECT_AUDIO_STANDARD_BG 0xF0 /* NICAM BG and A2 BG */ | ||
336 | #define CXADEC_SELECT_AUDIO_STANDARD_DK1 0xF1 /* NICAM DK and A2 DK */ | ||
337 | #define CXADEC_SELECT_AUDIO_STANDARD_DK2 0xF2 | ||
338 | #define CXADEC_SELECT_AUDIO_STANDARD_DK3 0xF3 | ||
339 | #define CXADEC_SELECT_AUDIO_STANDARD_I 0xF4 /* NICAM I and A1 */ | ||
340 | #define CXADEC_SELECT_AUDIO_STANDARD_L 0xF5 /* NICAM L and System L AM */ | ||
341 | #define CXADEC_SELECT_AUDIO_STANDARD_BTSC 0xF6 | ||
342 | #define CXADEC_SELECT_AUDIO_STANDARD_EIAJ 0xF7 | ||
343 | #define CXADEC_SELECT_AUDIO_STANDARD_A2_M 0xF8 /* A2 M */ | ||
344 | #define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */ | ||
345 | #define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */ | ||
346 | |||
347 | static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd) | ||
348 | { | ||
349 | return container_of(sd, struct cx18_av_state, sd); | ||
350 | } | ||
351 | |||
352 | static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) | ||
353 | { | ||
354 | return &container_of(ctrl->handler, struct cx18_av_state, hdl)->sd; | ||
355 | } | ||
356 | |||
357 | /* ----------------------------------------------------------------------- */ | ||
358 | /* cx18_av-core.c */ | ||
359 | int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); | ||
360 | int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value); | ||
361 | int cx18_av_write4_noretry(struct cx18 *cx, u16 addr, u32 value); | ||
362 | int cx18_av_write_expect(struct cx18 *cx, u16 addr, u8 value, u8 eval, u8 mask); | ||
363 | int cx18_av_write4_expect(struct cx18 *cx, u16 addr, u32 value, u32 eval, | ||
364 | u32 mask); | ||
365 | u8 cx18_av_read(struct cx18 *cx, u16 addr); | ||
366 | u32 cx18_av_read4(struct cx18 *cx, u16 addr); | ||
367 | int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value); | ||
368 | int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value); | ||
369 | void cx18_av_std_setup(struct cx18 *cx); | ||
370 | |||
371 | int cx18_av_probe(struct cx18 *cx); | ||
372 | |||
373 | /* ----------------------------------------------------------------------- */ | ||
374 | /* cx18_av-firmware.c */ | ||
375 | int cx18_av_loadfw(struct cx18 *cx); | ||
376 | |||
377 | /* ----------------------------------------------------------------------- */ | ||
378 | /* cx18_av-audio.c */ | ||
379 | int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq); | ||
380 | void cx18_av_audio_set_path(struct cx18 *cx); | ||
381 | extern const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops; | ||
382 | |||
383 | /* ----------------------------------------------------------------------- */ | ||
384 | /* cx18_av-vbi.c */ | ||
385 | int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, | ||
386 | struct v4l2_decode_vbi_line *vbi); | ||
387 | int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt); | ||
388 | int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); | ||
389 | int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *fmt); | ||
390 | |||
391 | #endif | ||
diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c new file mode 100644 index 00000000000..280aa4d2248 --- /dev/null +++ b/drivers/media/video/cx18/cx18-av-firmware.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * cx18 ADEC firmware functions | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version 2 | ||
10 | * of the License, or (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., 51 Franklin Street, Fifth Floor, Boston, MA | ||
20 | * 02110-1301, USA. | ||
21 | */ | ||
22 | |||
23 | #include "cx18-driver.h" | ||
24 | #include "cx18-io.h" | ||
25 | #include <linux/firmware.h> | ||
26 | |||
27 | #define CX18_AUDIO_ENABLE 0xc72014 | ||
28 | #define CX18_AI1_MUX_MASK 0x30 | ||
29 | #define CX18_AI1_MUX_I2S1 0x00 | ||
30 | #define CX18_AI1_MUX_I2S2 0x10 | ||
31 | #define CX18_AI1_MUX_843_I2S 0x20 | ||
32 | #define CX18_AI1_MUX_INVALID 0x30 | ||
33 | |||
34 | #define FWFILE "v4l-cx23418-dig.fw" | ||
35 | |||
36 | static int cx18_av_verifyfw(struct cx18 *cx, const struct firmware *fw) | ||
37 | { | ||
38 | struct v4l2_subdev *sd = &cx->av_state.sd; | ||
39 | int ret = 0; | ||
40 | const u8 *data; | ||
41 | u32 size; | ||
42 | int addr; | ||
43 | u32 expected, dl_control; | ||
44 | |||
45 | /* Ensure we put the 8051 in reset and enable firmware upload mode */ | ||
46 | dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); | ||
47 | do { | ||
48 | dl_control &= 0x00ffffff; | ||
49 | dl_control |= 0x0f000000; | ||
50 | cx18_av_write4_noretry(cx, CXADEC_DL_CTL, dl_control); | ||
51 | dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); | ||
52 | } while ((dl_control & 0xff000000) != 0x0f000000); | ||
53 | |||
54 | /* Read and auto increment until at address 0x0000 */ | ||
55 | while (dl_control & 0x3fff) | ||
56 | dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); | ||
57 | |||
58 | data = fw->data; | ||
59 | size = fw->size; | ||
60 | for (addr = 0; addr < size; addr++) { | ||
61 | dl_control &= 0xffff3fff; /* ignore top 2 bits of address */ | ||
62 | expected = 0x0f000000 | ((u32)data[addr] << 16) | addr; | ||
63 | if (expected != dl_control) { | ||
64 | CX18_ERR_DEV(sd, "verification of %s firmware load " | ||
65 | "failed: expected %#010x got %#010x\n", | ||
66 | FWFILE, expected, dl_control); | ||
67 | ret = -EIO; | ||
68 | break; | ||
69 | } | ||
70 | dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); | ||
71 | } | ||
72 | if (ret == 0) | ||
73 | CX18_INFO_DEV(sd, "verified load of %s firmware (%d bytes)\n", | ||
74 | FWFILE, size); | ||
75 | return ret; | ||
76 | } | ||
77 | |||
78 | int cx18_av_loadfw(struct cx18 *cx) | ||
79 | { | ||
80 | struct v4l2_subdev *sd = &cx->av_state.sd; | ||
81 | const struct firmware *fw = NULL; | ||
82 | u32 size; | ||
83 | u32 u, v; | ||
84 | const u8 *ptr; | ||
85 | int i; | ||
86 | int retries1 = 0; | ||
87 | |||
88 | if (request_firmware(&fw, FWFILE, &cx->pci_dev->dev) != 0) { | ||
89 | CX18_ERR_DEV(sd, "unable to open firmware %s\n", FWFILE); | ||
90 | return -EINVAL; | ||
91 | } | ||
92 | |||
93 | /* The firmware load often has byte errors, so allow for several | ||
94 | retries, both at byte level and at the firmware load level. */ | ||
95 | while (retries1 < 5) { | ||
96 | cx18_av_write4_expect(cx, CXADEC_CHIP_CTRL, 0x00010000, | ||
97 | 0x00008430, 0xffffffff); /* cx25843 */ | ||
98 | cx18_av_write_expect(cx, CXADEC_STD_DET_CTL, 0xf6, 0xf6, 0xff); | ||
99 | |||
100 | /* Reset the Mako core, Register is alias of CXADEC_CHIP_CTRL */ | ||
101 | cx18_av_write4_expect(cx, 0x8100, 0x00010000, | ||
102 | 0x00008430, 0xffffffff); /* cx25843 */ | ||
103 | |||
104 | /* Put the 8051 in reset and enable firmware upload */ | ||
105 | cx18_av_write4_noretry(cx, CXADEC_DL_CTL, 0x0F000000); | ||
106 | |||
107 | ptr = fw->data; | ||
108 | size = fw->size; | ||
109 | |||
110 | for (i = 0; i < size; i++) { | ||
111 | u32 dl_control = 0x0F000000 | i | ((u32)ptr[i] << 16); | ||
112 | u32 value = 0; | ||
113 | int retries2; | ||
114 | int unrec_err = 0; | ||
115 | |||
116 | for (retries2 = 0; retries2 < CX18_MAX_MMIO_WR_RETRIES; | ||
117 | retries2++) { | ||
118 | cx18_av_write4_noretry(cx, CXADEC_DL_CTL, | ||
119 | dl_control); | ||
120 | udelay(10); | ||
121 | value = cx18_av_read4(cx, CXADEC_DL_CTL); | ||
122 | if (value == dl_control) | ||
123 | break; | ||
124 | /* Check if we can correct the byte by changing | ||
125 | the address. We can only write the lower | ||
126 | address byte of the address. */ | ||
127 | if ((value & 0x3F00) != (dl_control & 0x3F00)) { | ||
128 | unrec_err = 1; | ||
129 | break; | ||
130 | } | ||
131 | } | ||
132 | if (unrec_err || retries2 >= CX18_MAX_MMIO_WR_RETRIES) | ||
133 | break; | ||
134 | } | ||
135 | if (i == size) | ||
136 | break; | ||
137 | retries1++; | ||
138 | } | ||
139 | if (retries1 >= 5) { | ||
140 | CX18_ERR_DEV(sd, "unable to load firmware %s\n", FWFILE); | ||
141 | release_firmware(fw); | ||
142 | return -EIO; | ||
143 | } | ||
144 | |||
145 | cx18_av_write4_expect(cx, CXADEC_DL_CTL, | ||
146 | 0x03000000 | fw->size, 0x03000000, 0x13000000); | ||
147 | |||
148 | CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size); | ||
149 | |||
150 | if (cx18_av_verifyfw(cx, fw) == 0) | ||
151 | cx18_av_write4_expect(cx, CXADEC_DL_CTL, | ||
152 | 0x13000000 | fw->size, 0x13000000, 0x13000000); | ||
153 | |||
154 | /* Output to the 416 */ | ||
155 | cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000); | ||
156 | |||
157 | /* Audio input control 1 set to Sony mode */ | ||
158 | /* Audio output input 2 is 0 for slave operation input */ | ||
159 | /* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */ | ||
160 | /* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge | ||
161 | after WS transition for first bit of audio word. */ | ||
162 | cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0); | ||
163 | |||
164 | /* Audio output control 1 is set to Sony mode */ | ||
165 | /* Audio output control 2 is set to 1 for master mode */ | ||
166 | /* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */ | ||
167 | /* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge | ||
168 | after WS transition for first bit of audio word. */ | ||
169 | /* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT | ||
170 | are generated) */ | ||
171 | cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0); | ||
172 | |||
173 | /* set alt I2s master clock to /0x16 and enable alt divider i2s | ||
174 | passthrough */ | ||
175 | cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5600B687); | ||
176 | |||
177 | cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6, | ||
178 | 0x3F00FFFF); | ||
179 | /* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */ | ||
180 | |||
181 | /* Set bit 0 in register 0x9CC to signify that this is MiniMe. */ | ||
182 | /* Register 0x09CC is defined by the Merlin firmware, and doesn't | ||
183 | have a name in the spec. */ | ||
184 | cx18_av_write4(cx, 0x09CC, 1); | ||
185 | |||
186 | v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); | ||
187 | /* If bit 11 is 1, clear bit 10 */ | ||
188 | if (v & 0x800) | ||
189 | cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE, | ||
190 | 0, 0x400); | ||
191 | |||
192 | /* Toggle the AI1 MUX */ | ||
193 | v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); | ||
194 | u = v & CX18_AI1_MUX_MASK; | ||
195 | v &= ~CX18_AI1_MUX_MASK; | ||
196 | if (u == CX18_AI1_MUX_843_I2S || u == CX18_AI1_MUX_INVALID) { | ||
197 | /* Switch to I2S1 */ | ||
198 | v |= CX18_AI1_MUX_I2S1; | ||
199 | cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, | ||
200 | v, CX18_AI1_MUX_MASK); | ||
201 | /* Switch back to the A/V decoder core I2S output */ | ||
202 | v = (v & ~CX18_AI1_MUX_MASK) | CX18_AI1_MUX_843_I2S; | ||
203 | } else { | ||
204 | /* Switch to the A/V decoder core I2S output */ | ||
205 | v |= CX18_AI1_MUX_843_I2S; | ||
206 | cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, | ||
207 | v, CX18_AI1_MUX_MASK); | ||
208 | /* Switch back to I2S1 or I2S2 */ | ||
209 | v = (v & ~CX18_AI1_MUX_MASK) | u; | ||
210 | } | ||
211 | cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, | ||
212 | v, CX18_AI1_MUX_MASK); | ||
213 | |||
214 | /* Enable WW auto audio standard detection */ | ||
215 | v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); | ||
216 | v |= 0xFF; /* Auto by default */ | ||
217 | v |= 0x400; /* Stereo by default */ | ||
218 | v |= 0x14000000; | ||
219 | cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF); | ||
220 | |||
221 | release_firmware(fw); | ||
222 | return 0; | ||
223 | } | ||
diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c new file mode 100644 index 00000000000..baa36fbcd4d --- /dev/null +++ b/drivers/media/video/cx18/cx18-av-vbi.c | |||
@@ -0,0 +1,311 @@ | |||
1 | /* | ||
2 | * cx18 ADEC VBI functions | ||
3 | * | ||
4 | * Derived from cx25840-vbi.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or | ||
9 | * modify it under the terms of the GNU General Public License | ||
10 | * as published by the Free Software Foundation; either version 2 | ||
11 | * of the License, or (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., 51 Franklin Street, Fifth Floor, Boston, MA | ||
21 | * 02110-1301, USA. | ||
22 | */ | ||
23 | |||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | |||
27 | /* | ||
28 | * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode, | ||
29 | * NN counts 1 byte Dwords, an IDID with the VBI line # in it. | ||
30 | * Thus, according to the VIP-2 Spec, our VBI ancillary data lines | ||
31 | * (should!) look like: | ||
32 | * 4 byte EAV code: 0xff 0x00 0x00 0xRP | ||
33 | * unknown number of possible idle bytes | ||
34 | * 3 byte Anc data preamble: 0x00 0xff 0xff | ||
35 | * 1 byte data identifier: ne010iii (parity bits, 010, DID bits) | ||
36 | * 1 byte secondary data id: nessssss (parity bits, SDID bits) | ||
37 | * 1 byte data word count: necccccc (parity bits, NN Dword count) | ||
38 | * 2 byte Internal DID: VBI-line-# 0x80 | ||
39 | * NN data bytes | ||
40 | * 1 byte checksum | ||
41 | * Fill bytes needed to fil out to 4*NN bytes of payload | ||
42 | * | ||
43 | * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, & | ||
44 | * in the vertical blanking interval are: | ||
45 | * 0xb0 (Task 0 VerticalBlank HorizontalBlank 0 0 0 0) | ||
46 | * 0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0) | ||
47 | * | ||
48 | * Since the V bit is only allowed to toggle in the EAV RP code, just | ||
49 | * before the first active region line and for active lines, they are: | ||
50 | * 0x90 (Task 0 0 HorizontalBlank 0 0 0 0) | ||
51 | * 0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0) | ||
52 | * | ||
53 | * The user application DID bytes we care about are: | ||
54 | * 0x91 (1 0 010 0 !ActiveLine AncDataPresent) | ||
55 | * 0x55 (0 1 010 2ndField !ActiveLine AncDataPresent) | ||
56 | * | ||
57 | */ | ||
58 | static const u8 sliced_vbi_did[2] = { 0x91, 0x55 }; | ||
59 | |||
60 | struct vbi_anc_data { | ||
61 | /* u8 eav[4]; */ | ||
62 | /* u8 idle[]; Variable number of idle bytes */ | ||
63 | u8 preamble[3]; | ||
64 | u8 did; | ||
65 | u8 sdid; | ||
66 | u8 data_count; | ||
67 | u8 idid[2]; | ||
68 | u8 payload[1]; /* data_count of payload */ | ||
69 | /* u8 checksum; */ | ||
70 | /* u8 fill[]; Variable number of fill bytes */ | ||
71 | }; | ||
72 | |||
73 | static int odd_parity(u8 c) | ||
74 | { | ||
75 | c ^= (c >> 4); | ||
76 | c ^= (c >> 2); | ||
77 | c ^= (c >> 1); | ||
78 | |||
79 | return c & 1; | ||
80 | } | ||
81 | |||
82 | static int decode_vps(u8 *dst, u8 *p) | ||
83 | { | ||
84 | static const u8 biphase_tbl[] = { | ||
85 | 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, | ||
86 | 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, | ||
87 | 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, | ||
88 | 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, | ||
89 | 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, | ||
90 | 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, | ||
91 | 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, | ||
92 | 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, | ||
93 | 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, | ||
94 | 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, | ||
95 | 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, | ||
96 | 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, | ||
97 | 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, | ||
98 | 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, | ||
99 | 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, | ||
100 | 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, | ||
101 | 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, | ||
102 | 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, | ||
103 | 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, | ||
104 | 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, | ||
105 | 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, | ||
106 | 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, | ||
107 | 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, | ||
108 | 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, | ||
109 | 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, | ||
110 | 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, | ||
111 | 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, | ||
112 | 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, | ||
113 | 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, | ||
114 | 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, | ||
115 | 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, | ||
116 | 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, | ||
117 | }; | ||
118 | |||
119 | u8 c, err = 0; | ||
120 | int i; | ||
121 | |||
122 | for (i = 0; i < 2 * 13; i += 2) { | ||
123 | err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; | ||
124 | c = (biphase_tbl[p[i + 1]] & 0xf) | | ||
125 | ((biphase_tbl[p[i]] & 0xf) << 4); | ||
126 | dst[i / 2] = c; | ||
127 | } | ||
128 | |||
129 | return err & 0xf0; | ||
130 | } | ||
131 | |||
132 | int cx18_av_g_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) | ||
133 | { | ||
134 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
135 | struct cx18_av_state *state = &cx->av_state; | ||
136 | static const u16 lcr2vbi[] = { | ||
137 | 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ | ||
138 | 0, V4L2_SLICED_WSS_625, 0, /* 4 */ | ||
139 | V4L2_SLICED_CAPTION_525, /* 6 */ | ||
140 | 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ | ||
141 | 0, 0, 0, 0 | ||
142 | }; | ||
143 | int is_pal = !(state->std & V4L2_STD_525_60); | ||
144 | int i; | ||
145 | |||
146 | memset(svbi, 0, sizeof(*svbi)); | ||
147 | /* we're done if raw VBI is active */ | ||
148 | if ((cx18_av_read(cx, 0x404) & 0x10) == 0) | ||
149 | return 0; | ||
150 | |||
151 | if (is_pal) { | ||
152 | for (i = 7; i <= 23; i++) { | ||
153 | u8 v = cx18_av_read(cx, 0x424 + i - 7); | ||
154 | |||
155 | svbi->service_lines[0][i] = lcr2vbi[v >> 4]; | ||
156 | svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; | ||
157 | svbi->service_set |= svbi->service_lines[0][i] | | ||
158 | svbi->service_lines[1][i]; | ||
159 | } | ||
160 | } else { | ||
161 | for (i = 10; i <= 21; i++) { | ||
162 | u8 v = cx18_av_read(cx, 0x424 + i - 10); | ||
163 | |||
164 | svbi->service_lines[0][i] = lcr2vbi[v >> 4]; | ||
165 | svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; | ||
166 | svbi->service_set |= svbi->service_lines[0][i] | | ||
167 | svbi->service_lines[1][i]; | ||
168 | } | ||
169 | } | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | int cx18_av_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) | ||
174 | { | ||
175 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
176 | struct cx18_av_state *state = &cx->av_state; | ||
177 | |||
178 | /* Setup standard */ | ||
179 | cx18_av_std_setup(cx); | ||
180 | |||
181 | /* VBI Offset */ | ||
182 | cx18_av_write(cx, 0x47f, state->slicer_line_delay); | ||
183 | cx18_av_write(cx, 0x404, 0x2e); | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | int cx18_av_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) | ||
188 | { | ||
189 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
190 | struct cx18_av_state *state = &cx->av_state; | ||
191 | int is_pal = !(state->std & V4L2_STD_525_60); | ||
192 | int i, x; | ||
193 | u8 lcr[24]; | ||
194 | |||
195 | for (x = 0; x <= 23; x++) | ||
196 | lcr[x] = 0x00; | ||
197 | |||
198 | /* Setup standard */ | ||
199 | cx18_av_std_setup(cx); | ||
200 | |||
201 | /* Sliced VBI */ | ||
202 | cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */ | ||
203 | cx18_av_write(cx, 0x406, 0x13); | ||
204 | cx18_av_write(cx, 0x47f, state->slicer_line_delay); | ||
205 | |||
206 | /* Force impossible lines to 0 */ | ||
207 | if (is_pal) { | ||
208 | for (i = 0; i <= 6; i++) | ||
209 | svbi->service_lines[0][i] = | ||
210 | svbi->service_lines[1][i] = 0; | ||
211 | } else { | ||
212 | for (i = 0; i <= 9; i++) | ||
213 | svbi->service_lines[0][i] = | ||
214 | svbi->service_lines[1][i] = 0; | ||
215 | |||
216 | for (i = 22; i <= 23; i++) | ||
217 | svbi->service_lines[0][i] = | ||
218 | svbi->service_lines[1][i] = 0; | ||
219 | } | ||
220 | |||
221 | /* Build register values for requested service lines */ | ||
222 | for (i = 7; i <= 23; i++) { | ||
223 | for (x = 0; x <= 1; x++) { | ||
224 | switch (svbi->service_lines[1-x][i]) { | ||
225 | case V4L2_SLICED_TELETEXT_B: | ||
226 | lcr[i] |= 1 << (4 * x); | ||
227 | break; | ||
228 | case V4L2_SLICED_WSS_625: | ||
229 | lcr[i] |= 4 << (4 * x); | ||
230 | break; | ||
231 | case V4L2_SLICED_CAPTION_525: | ||
232 | lcr[i] |= 6 << (4 * x); | ||
233 | break; | ||
234 | case V4L2_SLICED_VPS: | ||
235 | lcr[i] |= 9 << (4 * x); | ||
236 | break; | ||
237 | } | ||
238 | } | ||
239 | } | ||
240 | |||
241 | if (is_pal) { | ||
242 | for (x = 1, i = 0x424; i <= 0x434; i++, x++) | ||
243 | cx18_av_write(cx, i, lcr[6 + x]); | ||
244 | } else { | ||
245 | for (x = 1, i = 0x424; i <= 0x430; i++, x++) | ||
246 | cx18_av_write(cx, i, lcr[9 + x]); | ||
247 | for (i = 0x431; i <= 0x434; i++) | ||
248 | cx18_av_write(cx, i, 0); | ||
249 | } | ||
250 | |||
251 | cx18_av_write(cx, 0x43c, 0x16); | ||
252 | /* Should match vblank set in cx18_av_std_setup() */ | ||
253 | cx18_av_write(cx, 0x474, is_pal ? 38 : 26); | ||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, | ||
258 | struct v4l2_decode_vbi_line *vbi) | ||
259 | { | ||
260 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
261 | struct cx18_av_state *state = &cx->av_state; | ||
262 | struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p; | ||
263 | u8 *p; | ||
264 | int did, sdid, l, err = 0; | ||
265 | |||
266 | /* | ||
267 | * Check for the ancillary data header for sliced VBI | ||
268 | */ | ||
269 | if (anc->preamble[0] || | ||
270 | anc->preamble[1] != 0xff || anc->preamble[2] != 0xff || | ||
271 | (anc->did != sliced_vbi_did[0] && | ||
272 | anc->did != sliced_vbi_did[1])) { | ||
273 | vbi->line = vbi->type = 0; | ||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | did = anc->did; | ||
278 | sdid = anc->sdid & 0xf; | ||
279 | l = anc->idid[0] & 0x3f; | ||
280 | l += state->slicer_line_offset; | ||
281 | p = anc->payload; | ||
282 | |||
283 | /* Decode the SDID set by the slicer */ | ||
284 | switch (sdid) { | ||
285 | case 1: | ||
286 | sdid = V4L2_SLICED_TELETEXT_B; | ||
287 | break; | ||
288 | case 4: | ||
289 | sdid = V4L2_SLICED_WSS_625; | ||
290 | break; | ||
291 | case 6: | ||
292 | sdid = V4L2_SLICED_CAPTION_525; | ||
293 | err = !odd_parity(p[0]) || !odd_parity(p[1]); | ||
294 | break; | ||
295 | case 9: | ||
296 | sdid = V4L2_SLICED_VPS; | ||
297 | if (decode_vps(p, p) != 0) | ||
298 | err = 1; | ||
299 | break; | ||
300 | default: | ||
301 | sdid = 0; | ||
302 | err = 1; | ||
303 | break; | ||
304 | } | ||
305 | |||
306 | vbi->type = err ? 0 : sdid; | ||
307 | vbi->line = err ? 0 : l; | ||
308 | vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]); | ||
309 | vbi->p = p; | ||
310 | return 0; | ||
311 | } | ||
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c new file mode 100644 index 00000000000..c07c849b1aa --- /dev/null +++ b/drivers/media/video/cx18/cx18-cards.c | |||
@@ -0,0 +1,638 @@ | |||
1 | /* | ||
2 | * cx18 functions to query card hardware | ||
3 | * | ||
4 | * Derived from ivtv-cards.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | #include "cx18-cards.h" | ||
27 | #include "cx18-av-core.h" | ||
28 | #include "cx18-i2c.h" | ||
29 | #include <media/cs5345.h> | ||
30 | |||
31 | #define V4L2_STD_PAL_SECAM (V4L2_STD_PAL|V4L2_STD_SECAM) | ||
32 | |||
33 | /********************** card configuration *******************************/ | ||
34 | |||
35 | /* usual i2c tuner addresses to probe */ | ||
36 | static struct cx18_card_tuner_i2c cx18_i2c_std = { | ||
37 | .radio = { I2C_CLIENT_END }, | ||
38 | .demod = { 0x43, I2C_CLIENT_END }, | ||
39 | .tv = { 0x61, 0x60, I2C_CLIENT_END }, | ||
40 | }; | ||
41 | |||
42 | /* | ||
43 | * usual i2c tuner addresses to probe with additional demod address for | ||
44 | * an NXP TDA8295 at 0x42 (N.B. it can possibly be at 0x4b or 0x4c too). | ||
45 | */ | ||
46 | static struct cx18_card_tuner_i2c cx18_i2c_nxp = { | ||
47 | .radio = { I2C_CLIENT_END }, | ||
48 | .demod = { 0x42, 0x43, I2C_CLIENT_END }, | ||
49 | .tv = { 0x61, 0x60, I2C_CLIENT_END }, | ||
50 | }; | ||
51 | |||
52 | /* Please add new PCI IDs to: http://pci-ids.ucw.cz/ | ||
53 | This keeps the PCI ID database up to date. Note that the entries | ||
54 | must be added under vendor 0x4444 (Conexant) as subsystem IDs. | ||
55 | New vendor IDs should still be added to the vendor ID list. */ | ||
56 | |||
57 | /* Hauppauge HVR-1600 cards */ | ||
58 | |||
59 | /* Note: for Hauppauge cards the tveeprom information is used instead | ||
60 | of PCI IDs */ | ||
61 | static const struct cx18_card cx18_card_hvr1600_esmt = { | ||
62 | .type = CX18_CARD_HVR_1600_ESMT, | ||
63 | .name = "Hauppauge HVR-1600", | ||
64 | .comment = "Simultaneous Digital and Analog TV capture supported\n", | ||
65 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
66 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
67 | .hw_muxer = CX18_HW_CS5345, | ||
68 | .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | | ||
69 | CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | | ||
70 | CX18_HW_Z8F0811_IR_HAUP, | ||
71 | .video_inputs = { | ||
72 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, | ||
73 | { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, | ||
74 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, | ||
75 | { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, | ||
76 | { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, | ||
77 | }, | ||
78 | .audio_inputs = { | ||
79 | { CX18_CARD_INPUT_AUD_TUNER, | ||
80 | CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, | ||
81 | { CX18_CARD_INPUT_LINE_IN1, | ||
82 | CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 }, | ||
83 | { CX18_CARD_INPUT_LINE_IN2, | ||
84 | CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 }, | ||
85 | }, | ||
86 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, | ||
87 | CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 }, | ||
88 | .ddr = { | ||
89 | /* ESMT M13S128324A-5B memory */ | ||
90 | .chip_config = 0x003, | ||
91 | .refresh = 0x30c, | ||
92 | .timing1 = 0x44220e82, | ||
93 | .timing2 = 0x08, | ||
94 | .tune_lane = 0, | ||
95 | .initial_emrs = 0, | ||
96 | }, | ||
97 | .gpio_init.initial_value = 0x3001, | ||
98 | .gpio_init.direction = 0x3001, | ||
99 | .gpio_i2c_slave_reset = { | ||
100 | .active_lo_mask = 0x3001, | ||
101 | .msecs_asserted = 10, | ||
102 | .msecs_recovery = 40, | ||
103 | .ir_reset_mask = 0x0001, | ||
104 | }, | ||
105 | .i2c = &cx18_i2c_std, | ||
106 | }; | ||
107 | |||
108 | static const struct cx18_card cx18_card_hvr1600_s5h1411 = { | ||
109 | .type = CX18_CARD_HVR_1600_S5H1411, | ||
110 | .name = "Hauppauge HVR-1600", | ||
111 | .comment = "Simultaneous Digital and Analog TV capture supported\n", | ||
112 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
113 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
114 | .hw_muxer = CX18_HW_CS5345, | ||
115 | .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | | ||
116 | CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | | ||
117 | CX18_HW_Z8F0811_IR_HAUP, | ||
118 | .video_inputs = { | ||
119 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, | ||
120 | { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, | ||
121 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, | ||
122 | { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, | ||
123 | { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, | ||
124 | }, | ||
125 | .audio_inputs = { | ||
126 | { CX18_CARD_INPUT_AUD_TUNER, | ||
127 | CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, | ||
128 | { CX18_CARD_INPUT_LINE_IN1, | ||
129 | CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 }, | ||
130 | { CX18_CARD_INPUT_LINE_IN2, | ||
131 | CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 }, | ||
132 | }, | ||
133 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, | ||
134 | CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 }, | ||
135 | .ddr = { | ||
136 | /* ESMT M13S128324A-5B memory */ | ||
137 | .chip_config = 0x003, | ||
138 | .refresh = 0x30c, | ||
139 | .timing1 = 0x44220e82, | ||
140 | .timing2 = 0x08, | ||
141 | .tune_lane = 0, | ||
142 | .initial_emrs = 0, | ||
143 | }, | ||
144 | .gpio_init.initial_value = 0x3801, | ||
145 | .gpio_init.direction = 0x3801, | ||
146 | .gpio_i2c_slave_reset = { | ||
147 | .active_lo_mask = 0x3801, | ||
148 | .msecs_asserted = 10, | ||
149 | .msecs_recovery = 40, | ||
150 | .ir_reset_mask = 0x0001, | ||
151 | }, | ||
152 | .i2c = &cx18_i2c_nxp, | ||
153 | }; | ||
154 | |||
155 | static const struct cx18_card cx18_card_hvr1600_samsung = { | ||
156 | .type = CX18_CARD_HVR_1600_SAMSUNG, | ||
157 | .name = "Hauppauge HVR-1600 (Preproduction)", | ||
158 | .comment = "Simultaneous Digital and Analog TV capture supported\n", | ||
159 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
160 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
161 | .hw_muxer = CX18_HW_CS5345, | ||
162 | .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | | ||
163 | CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL | | ||
164 | CX18_HW_Z8F0811_IR_HAUP, | ||
165 | .video_inputs = { | ||
166 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 }, | ||
167 | { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 }, | ||
168 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 }, | ||
169 | { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 }, | ||
170 | { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 }, | ||
171 | }, | ||
172 | .audio_inputs = { | ||
173 | { CX18_CARD_INPUT_AUD_TUNER, | ||
174 | CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 }, | ||
175 | { CX18_CARD_INPUT_LINE_IN1, | ||
176 | CX18_AV_AUDIO_SERIAL1, CS5345_IN_2 }, | ||
177 | { CX18_CARD_INPUT_LINE_IN2, | ||
178 | CX18_AV_AUDIO_SERIAL1, CS5345_IN_3 }, | ||
179 | }, | ||
180 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, | ||
181 | CX18_AV_AUDIO_SERIAL1, CS5345_IN_4 }, | ||
182 | .ddr = { | ||
183 | /* Samsung K4D263238G-VC33 memory */ | ||
184 | .chip_config = 0x003, | ||
185 | .refresh = 0x30c, | ||
186 | .timing1 = 0x23230b73, | ||
187 | .timing2 = 0x08, | ||
188 | .tune_lane = 0, | ||
189 | .initial_emrs = 2, | ||
190 | }, | ||
191 | .gpio_init.initial_value = 0x3001, | ||
192 | .gpio_init.direction = 0x3001, | ||
193 | .gpio_i2c_slave_reset = { | ||
194 | .active_lo_mask = 0x3001, | ||
195 | .msecs_asserted = 10, | ||
196 | .msecs_recovery = 40, | ||
197 | .ir_reset_mask = 0x0001, | ||
198 | }, | ||
199 | .i2c = &cx18_i2c_std, | ||
200 | }; | ||
201 | |||
202 | /* ------------------------------------------------------------------------- */ | ||
203 | |||
204 | /* Compro VideoMate H900: note that this card is analog only! */ | ||
205 | |||
206 | static const struct cx18_card_pci_info cx18_pci_h900[] = { | ||
207 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_COMPRO, 0xe100 }, | ||
208 | { 0, 0, 0 } | ||
209 | }; | ||
210 | |||
211 | static const struct cx18_card cx18_card_h900 = { | ||
212 | .type = CX18_CARD_COMPRO_H900, | ||
213 | .name = "Compro VideoMate H900", | ||
214 | .comment = "Analog TV capture supported\n", | ||
215 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
216 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
217 | .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL, | ||
218 | .video_inputs = { | ||
219 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, | ||
220 | { CX18_CARD_INPUT_SVIDEO1, 1, | ||
221 | CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, | ||
222 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, | ||
223 | }, | ||
224 | .audio_inputs = { | ||
225 | { CX18_CARD_INPUT_AUD_TUNER, | ||
226 | CX18_AV_AUDIO5, 0 }, | ||
227 | { CX18_CARD_INPUT_LINE_IN1, | ||
228 | CX18_AV_AUDIO_SERIAL1, 0 }, | ||
229 | }, | ||
230 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, | ||
231 | CX18_AV_AUDIO_SERIAL1, 0 }, | ||
232 | .tuners = { | ||
233 | { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, | ||
234 | }, | ||
235 | .ddr = { | ||
236 | /* EtronTech EM6A9160TS-5G memory */ | ||
237 | .chip_config = 0x50003, | ||
238 | .refresh = 0x753, | ||
239 | .timing1 = 0x24330e84, | ||
240 | .timing2 = 0x1f, | ||
241 | .tune_lane = 0, | ||
242 | .initial_emrs = 0, | ||
243 | }, | ||
244 | .xceive_pin = 15, | ||
245 | .pci_list = cx18_pci_h900, | ||
246 | .i2c = &cx18_i2c_std, | ||
247 | }; | ||
248 | |||
249 | /* ------------------------------------------------------------------------- */ | ||
250 | |||
251 | /* Yuan MPC718: not working at the moment! */ | ||
252 | |||
253 | static const struct cx18_card_pci_info cx18_pci_mpc718[] = { | ||
254 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_YUAN, 0x0718 }, | ||
255 | { 0, 0, 0 } | ||
256 | }; | ||
257 | |||
258 | static const struct cx18_card cx18_card_mpc718 = { | ||
259 | .type = CX18_CARD_YUAN_MPC718, | ||
260 | .name = "Yuan MPC718 MiniPCI DVB-T/Analog", | ||
261 | .comment = "Experimenters needed for device to work well.\n" | ||
262 | "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", | ||
263 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
264 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
265 | .hw_muxer = CX18_HW_GPIO_MUX, | ||
266 | .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | | ||
267 | CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, | ||
268 | .video_inputs = { | ||
269 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, | ||
270 | { CX18_CARD_INPUT_SVIDEO1, 1, | ||
271 | CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, | ||
272 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, | ||
273 | { CX18_CARD_INPUT_SVIDEO2, 2, | ||
274 | CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 }, | ||
275 | { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 }, | ||
276 | }, | ||
277 | .audio_inputs = { | ||
278 | { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, | ||
279 | { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, | ||
280 | { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 }, | ||
281 | }, | ||
282 | .tuners = { | ||
283 | /* XC3028 tuner */ | ||
284 | { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, | ||
285 | }, | ||
286 | /* FIXME - the FM radio is just a guess and driver doesn't use SIF */ | ||
287 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, | ||
288 | .ddr = { | ||
289 | /* Hynix HY5DU283222B DDR RAM */ | ||
290 | .chip_config = 0x303, | ||
291 | .refresh = 0x3bd, | ||
292 | .timing1 = 0x36320966, | ||
293 | .timing2 = 0x1f, | ||
294 | .tune_lane = 0, | ||
295 | .initial_emrs = 2, | ||
296 | }, | ||
297 | .gpio_init.initial_value = 0x1, | ||
298 | .gpio_init.direction = 0x3, | ||
299 | /* FIXME - these GPIO's are just guesses */ | ||
300 | .gpio_audio_input = { .mask = 0x3, | ||
301 | .tuner = 0x1, | ||
302 | .linein = 0x3, | ||
303 | .radio = 0x1 }, | ||
304 | .xceive_pin = 0, | ||
305 | .pci_list = cx18_pci_mpc718, | ||
306 | .i2c = &cx18_i2c_std, | ||
307 | }; | ||
308 | |||
309 | /* ------------------------------------------------------------------------- */ | ||
310 | |||
311 | /* GoTView PCI */ | ||
312 | |||
313 | static const struct cx18_card_pci_info cx18_pci_gotview_dvd3[] = { | ||
314 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_GOTVIEW, 0x3343 }, | ||
315 | { 0, 0, 0 } | ||
316 | }; | ||
317 | |||
318 | static const struct cx18_card cx18_card_gotview_dvd3 = { | ||
319 | .type = CX18_CARD_GOTVIEW_PCI_DVD3, | ||
320 | .name = "GoTView PCI DVD3 Hybrid", | ||
321 | .comment = "Experimenters needed for device to work well.\n" | ||
322 | "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", | ||
323 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
324 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
325 | .hw_muxer = CX18_HW_GPIO_MUX, | ||
326 | .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER | | ||
327 | CX18_HW_GPIO_MUX | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, | ||
328 | .video_inputs = { | ||
329 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, | ||
330 | { CX18_CARD_INPUT_SVIDEO1, 1, | ||
331 | CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, | ||
332 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, | ||
333 | { CX18_CARD_INPUT_SVIDEO2, 2, | ||
334 | CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 }, | ||
335 | { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 }, | ||
336 | }, | ||
337 | .audio_inputs = { | ||
338 | { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, | ||
339 | { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, | ||
340 | { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 }, | ||
341 | }, | ||
342 | .tuners = { | ||
343 | /* XC3028 tuner */ | ||
344 | { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, | ||
345 | }, | ||
346 | /* FIXME - the FM radio is just a guess and driver doesn't use SIF */ | ||
347 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, | ||
348 | .ddr = { | ||
349 | /* Hynix HY5DU283222B DDR RAM */ | ||
350 | .chip_config = 0x303, | ||
351 | .refresh = 0x3bd, | ||
352 | .timing1 = 0x36320966, | ||
353 | .timing2 = 0x1f, | ||
354 | .tune_lane = 0, | ||
355 | .initial_emrs = 2, | ||
356 | }, | ||
357 | .gpio_init.initial_value = 0x1, | ||
358 | .gpio_init.direction = 0x3, | ||
359 | |||
360 | .gpio_audio_input = { .mask = 0x3, | ||
361 | .tuner = 0x1, | ||
362 | .linein = 0x2, | ||
363 | .radio = 0x1 }, | ||
364 | .xceive_pin = 0, | ||
365 | .pci_list = cx18_pci_gotview_dvd3, | ||
366 | .i2c = &cx18_i2c_std, | ||
367 | }; | ||
368 | |||
369 | /* ------------------------------------------------------------------------- */ | ||
370 | |||
371 | /* Conexant Raptor PAL/SECAM: note that this card is analog only! */ | ||
372 | |||
373 | static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = { | ||
374 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_CONEXANT, 0x0009 }, | ||
375 | { 0, 0, 0 } | ||
376 | }; | ||
377 | |||
378 | static const struct cx18_card cx18_card_cnxt_raptor_pal = { | ||
379 | .type = CX18_CARD_CNXT_RAPTOR_PAL, | ||
380 | .name = "Conexant Raptor PAL/SECAM", | ||
381 | .comment = "Analog TV capture supported\n", | ||
382 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
383 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
384 | .hw_muxer = CX18_HW_GPIO_MUX, | ||
385 | .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX, | ||
386 | .video_inputs = { | ||
387 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, | ||
388 | { CX18_CARD_INPUT_SVIDEO1, 1, | ||
389 | CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, | ||
390 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, | ||
391 | { CX18_CARD_INPUT_SVIDEO2, 2, | ||
392 | CX18_AV_SVIDEO_LUMA7 | CX18_AV_SVIDEO_CHROMA8 }, | ||
393 | { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE6 }, | ||
394 | }, | ||
395 | .audio_inputs = { | ||
396 | { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, | ||
397 | { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, | ||
398 | { CX18_CARD_INPUT_LINE_IN2, CX18_AV_AUDIO_SERIAL2, 1 }, | ||
399 | }, | ||
400 | .tuners = { | ||
401 | { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, | ||
402 | }, | ||
403 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO_SERIAL1, 2 }, | ||
404 | .ddr = { | ||
405 | /* MT 46V16M16 memory */ | ||
406 | .chip_config = 0x50306, | ||
407 | .refresh = 0x753, | ||
408 | .timing1 = 0x33220953, | ||
409 | .timing2 = 0x09, | ||
410 | .tune_lane = 0, | ||
411 | .initial_emrs = 0, | ||
412 | }, | ||
413 | .gpio_init.initial_value = 0x1002, | ||
414 | .gpio_init.direction = 0xf002, | ||
415 | .gpio_audio_input = { .mask = 0xf002, | ||
416 | .tuner = 0x1002, /* LED D1 Tuner AF */ | ||
417 | .linein = 0x2000, /* LED D2 Line In 1 */ | ||
418 | .radio = 0x4002 }, /* LED D3 Tuner AF */ | ||
419 | .pci_list = cx18_pci_cnxt_raptor_pal, | ||
420 | .i2c = &cx18_i2c_std, | ||
421 | }; | ||
422 | |||
423 | /* ------------------------------------------------------------------------- */ | ||
424 | |||
425 | /* Toshiba Qosmio laptop internal DVB-T/Analog Hybrid Tuner */ | ||
426 | |||
427 | static const struct cx18_card_pci_info cx18_pci_toshiba_qosmio_dvbt[] = { | ||
428 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_TOSHIBA, 0x0110 }, | ||
429 | { 0, 0, 0 } | ||
430 | }; | ||
431 | |||
432 | static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = { | ||
433 | .type = CX18_CARD_TOSHIBA_QOSMIO_DVBT, | ||
434 | .name = "Toshiba Qosmio DVB-T/Analog", | ||
435 | .comment = "Experimenters and photos needed for device to work well.\n" | ||
436 | "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", | ||
437 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
438 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
439 | .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL, | ||
440 | .video_inputs = { | ||
441 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE6 }, | ||
442 | { CX18_CARD_INPUT_SVIDEO1, 1, | ||
443 | CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, | ||
444 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 }, | ||
445 | }, | ||
446 | .audio_inputs = { | ||
447 | { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, | ||
448 | { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, | ||
449 | }, | ||
450 | .tuners = { | ||
451 | { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, | ||
452 | }, | ||
453 | .ddr = { | ||
454 | .chip_config = 0x202, | ||
455 | .refresh = 0x3bb, | ||
456 | .timing1 = 0x33320a63, | ||
457 | .timing2 = 0x0a, | ||
458 | .tune_lane = 0, | ||
459 | .initial_emrs = 0x42, | ||
460 | }, | ||
461 | .xceive_pin = 15, | ||
462 | .pci_list = cx18_pci_toshiba_qosmio_dvbt, | ||
463 | .i2c = &cx18_i2c_std, | ||
464 | }; | ||
465 | |||
466 | /* ------------------------------------------------------------------------- */ | ||
467 | |||
468 | /* Leadtek WinFast PVR2100 */ | ||
469 | |||
470 | static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = { | ||
471 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, /* PVR2100 */ | ||
472 | { 0, 0, 0 } | ||
473 | }; | ||
474 | |||
475 | static const struct cx18_card cx18_card_leadtek_pvr2100 = { | ||
476 | .type = CX18_CARD_LEADTEK_PVR2100, | ||
477 | .name = "Leadtek WinFast PVR2100", | ||
478 | .comment = "Experimenters and photos needed for device to work well.\n" | ||
479 | "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", | ||
480 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
481 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
482 | .hw_muxer = CX18_HW_GPIO_MUX, | ||
483 | .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX | | ||
484 | CX18_HW_GPIO_RESET_CTRL, | ||
485 | .video_inputs = { | ||
486 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, | ||
487 | { CX18_CARD_INPUT_SVIDEO1, 1, | ||
488 | CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, | ||
489 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 }, | ||
490 | { CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 }, | ||
491 | }, | ||
492 | .audio_inputs = { | ||
493 | { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, | ||
494 | { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, | ||
495 | }, | ||
496 | .tuners = { | ||
497 | /* XC2028 tuner */ | ||
498 | { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, | ||
499 | }, | ||
500 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, | ||
501 | .ddr = { | ||
502 | /* Pointer to proper DDR config values provided by Terry Wu */ | ||
503 | .chip_config = 0x303, | ||
504 | .refresh = 0x3bb, | ||
505 | .timing1 = 0x24220e83, | ||
506 | .timing2 = 0x1f, | ||
507 | .tune_lane = 0, | ||
508 | .initial_emrs = 0x2, | ||
509 | }, | ||
510 | .gpio_init.initial_value = 0x6, | ||
511 | .gpio_init.direction = 0x7, | ||
512 | .gpio_audio_input = { .mask = 0x7, | ||
513 | .tuner = 0x6, .linein = 0x2, .radio = 0x2 }, | ||
514 | .xceive_pin = 1, | ||
515 | .pci_list = cx18_pci_leadtek_pvr2100, | ||
516 | .i2c = &cx18_i2c_std, | ||
517 | }; | ||
518 | |||
519 | /* ------------------------------------------------------------------------- */ | ||
520 | |||
521 | /* Leadtek WinFast DVR3100 H */ | ||
522 | |||
523 | static const struct cx18_card_pci_info cx18_pci_leadtek_dvr3100h[] = { | ||
524 | { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */ | ||
525 | { 0, 0, 0 } | ||
526 | }; | ||
527 | |||
528 | static const struct cx18_card cx18_card_leadtek_dvr3100h = { | ||
529 | .type = CX18_CARD_LEADTEK_DVR3100H, | ||
530 | .name = "Leadtek WinFast DVR3100 H", | ||
531 | .comment = "Simultaneous DVB-T and Analog capture supported,\n" | ||
532 | "\texcept when capturing Analog from the antenna input.\n", | ||
533 | .v4l2_capabilities = CX18_CAP_ENCODER, | ||
534 | .hw_audio_ctrl = CX18_HW_418_AV, | ||
535 | .hw_muxer = CX18_HW_GPIO_MUX, | ||
536 | .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX | | ||
537 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, | ||
538 | .video_inputs = { | ||
539 | { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, | ||
540 | { CX18_CARD_INPUT_SVIDEO1, 1, | ||
541 | CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, | ||
542 | { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 }, | ||
543 | { CX18_CARD_INPUT_COMPONENT1, 1, CX18_AV_COMPONENT1 }, | ||
544 | }, | ||
545 | .audio_inputs = { | ||
546 | { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, | ||
547 | { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, | ||
548 | }, | ||
549 | .tuners = { | ||
550 | /* XC3028 tuner */ | ||
551 | { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, | ||
552 | }, | ||
553 | .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, | ||
554 | .ddr = { | ||
555 | /* Pointer to proper DDR config values provided by Terry Wu */ | ||
556 | .chip_config = 0x303, | ||
557 | .refresh = 0x3bb, | ||
558 | .timing1 = 0x24220e83, | ||
559 | .timing2 = 0x1f, | ||
560 | .tune_lane = 0, | ||
561 | .initial_emrs = 0x2, | ||
562 | }, | ||
563 | .gpio_init.initial_value = 0x6, | ||
564 | .gpio_init.direction = 0x7, | ||
565 | .gpio_audio_input = { .mask = 0x7, | ||
566 | .tuner = 0x6, .linein = 0x2, .radio = 0x2 }, | ||
567 | .xceive_pin = 1, | ||
568 | .pci_list = cx18_pci_leadtek_dvr3100h, | ||
569 | .i2c = &cx18_i2c_std, | ||
570 | }; | ||
571 | |||
572 | /* ------------------------------------------------------------------------- */ | ||
573 | |||
574 | static const struct cx18_card *cx18_card_list[] = { | ||
575 | &cx18_card_hvr1600_esmt, | ||
576 | &cx18_card_hvr1600_samsung, | ||
577 | &cx18_card_h900, | ||
578 | &cx18_card_mpc718, | ||
579 | &cx18_card_cnxt_raptor_pal, | ||
580 | &cx18_card_toshiba_qosmio_dvbt, | ||
581 | &cx18_card_leadtek_pvr2100, | ||
582 | &cx18_card_leadtek_dvr3100h, | ||
583 | &cx18_card_gotview_dvd3, | ||
584 | &cx18_card_hvr1600_s5h1411 | ||
585 | }; | ||
586 | |||
587 | const struct cx18_card *cx18_get_card(u16 index) | ||
588 | { | ||
589 | if (index >= ARRAY_SIZE(cx18_card_list)) | ||
590 | return NULL; | ||
591 | return cx18_card_list[index]; | ||
592 | } | ||
593 | |||
594 | int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input) | ||
595 | { | ||
596 | const struct cx18_card_video_input *card_input = | ||
597 | cx->card->video_inputs + index; | ||
598 | static const char * const input_strs[] = { | ||
599 | "Tuner 1", | ||
600 | "S-Video 1", | ||
601 | "S-Video 2", | ||
602 | "Composite 1", | ||
603 | "Composite 2", | ||
604 | "Component 1" | ||
605 | }; | ||
606 | |||
607 | if (index >= cx->nof_inputs) | ||
608 | return -EINVAL; | ||
609 | input->index = index; | ||
610 | strlcpy(input->name, input_strs[card_input->video_type - 1], | ||
611 | sizeof(input->name)); | ||
612 | input->type = (card_input->video_type == CX18_CARD_INPUT_VID_TUNER ? | ||
613 | V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA); | ||
614 | input->audioset = (1 << cx->nof_audio_inputs) - 1; | ||
615 | input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ? | ||
616 | cx->tuner_std : V4L2_STD_ALL; | ||
617 | return 0; | ||
618 | } | ||
619 | |||
620 | int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *audio) | ||
621 | { | ||
622 | const struct cx18_card_audio_input *aud_input = | ||
623 | cx->card->audio_inputs + index; | ||
624 | static const char * const input_strs[] = { | ||
625 | "Tuner 1", | ||
626 | "Line In 1", | ||
627 | "Line In 2" | ||
628 | }; | ||
629 | |||
630 | memset(audio, 0, sizeof(*audio)); | ||
631 | if (index >= cx->nof_audio_inputs) | ||
632 | return -EINVAL; | ||
633 | strlcpy(audio->name, input_strs[aud_input->audio_type - 1], | ||
634 | sizeof(audio->name)); | ||
635 | audio->index = index; | ||
636 | audio->capability = V4L2_AUDCAP_STEREO; | ||
637 | return 0; | ||
638 | } | ||
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h new file mode 100644 index 00000000000..add7391ecab --- /dev/null +++ b/drivers/media/video/cx18/cx18-cards.h | |||
@@ -0,0 +1,157 @@ | |||
1 | /* | ||
2 | * cx18 functions to query card hardware | ||
3 | * | ||
4 | * Derived from ivtv-cards.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | /* hardware flags */ | ||
25 | #define CX18_HW_TUNER (1 << 0) | ||
26 | #define CX18_HW_TVEEPROM (1 << 1) | ||
27 | #define CX18_HW_CS5345 (1 << 2) | ||
28 | #define CX18_HW_DVB (1 << 3) | ||
29 | #define CX18_HW_418_AV (1 << 4) | ||
30 | #define CX18_HW_GPIO_MUX (1 << 5) | ||
31 | #define CX18_HW_GPIO_RESET_CTRL (1 << 6) | ||
32 | #define CX18_HW_Z8F0811_IR_TX_HAUP (1 << 7) | ||
33 | #define CX18_HW_Z8F0811_IR_RX_HAUP (1 << 8) | ||
34 | #define CX18_HW_Z8F0811_IR_HAUP (CX18_HW_Z8F0811_IR_RX_HAUP | \ | ||
35 | CX18_HW_Z8F0811_IR_TX_HAUP) | ||
36 | |||
37 | #define CX18_HW_IR_ANY (CX18_HW_Z8F0811_IR_RX_HAUP | \ | ||
38 | CX18_HW_Z8F0811_IR_TX_HAUP) | ||
39 | |||
40 | /* video inputs */ | ||
41 | #define CX18_CARD_INPUT_VID_TUNER 1 | ||
42 | #define CX18_CARD_INPUT_SVIDEO1 2 | ||
43 | #define CX18_CARD_INPUT_SVIDEO2 3 | ||
44 | #define CX18_CARD_INPUT_COMPOSITE1 4 | ||
45 | #define CX18_CARD_INPUT_COMPOSITE2 5 | ||
46 | #define CX18_CARD_INPUT_COMPONENT1 6 | ||
47 | |||
48 | /* audio inputs */ | ||
49 | #define CX18_CARD_INPUT_AUD_TUNER 1 | ||
50 | #define CX18_CARD_INPUT_LINE_IN1 2 | ||
51 | #define CX18_CARD_INPUT_LINE_IN2 3 | ||
52 | |||
53 | #define CX18_CARD_MAX_VIDEO_INPUTS 6 | ||
54 | #define CX18_CARD_MAX_AUDIO_INPUTS 3 | ||
55 | #define CX18_CARD_MAX_TUNERS 2 | ||
56 | |||
57 | /* V4L2 capability aliases */ | ||
58 | #define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ | ||
59 | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \ | ||
60 | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) | ||
61 | |||
62 | struct cx18_card_video_input { | ||
63 | u8 video_type; /* video input type */ | ||
64 | u8 audio_index; /* index in cx18_card_audio_input array */ | ||
65 | u32 video_input; /* hardware video input */ | ||
66 | }; | ||
67 | |||
68 | struct cx18_card_audio_input { | ||
69 | u8 audio_type; /* audio input type */ | ||
70 | u32 audio_input; /* hardware audio input */ | ||
71 | u16 muxer_input; /* hardware muxer input for boards with a | ||
72 | multiplexer chip */ | ||
73 | }; | ||
74 | |||
75 | struct cx18_card_pci_info { | ||
76 | u16 device; | ||
77 | u16 subsystem_vendor; | ||
78 | u16 subsystem_device; | ||
79 | }; | ||
80 | |||
81 | /* GPIO definitions */ | ||
82 | |||
83 | /* The mask is the set of bits used by the operation */ | ||
84 | |||
85 | struct cx18_gpio_init { /* set initial GPIO DIR and OUT values */ | ||
86 | u32 direction; /* DIR setting. Leave to 0 if no init is needed */ | ||
87 | u32 initial_value; | ||
88 | }; | ||
89 | |||
90 | struct cx18_gpio_i2c_slave_reset { | ||
91 | u32 active_lo_mask; /* GPIO outputs that reset i2c chips when low */ | ||
92 | u32 active_hi_mask; /* GPIO outputs that reset i2c chips when high */ | ||
93 | int msecs_asserted; /* time period reset must remain asserted */ | ||
94 | int msecs_recovery; /* time after deassert for chips to be ready */ | ||
95 | u32 ir_reset_mask; /* GPIO to reset the Zilog Z8F0811 IR contoller */ | ||
96 | }; | ||
97 | |||
98 | struct cx18_gpio_audio_input { /* select tuner/line in input */ | ||
99 | u32 mask; /* leave to 0 if not supported */ | ||
100 | u32 tuner; | ||
101 | u32 linein; | ||
102 | u32 radio; | ||
103 | }; | ||
104 | |||
105 | struct cx18_card_tuner { | ||
106 | v4l2_std_id std; /* standard for which the tuner is suitable */ | ||
107 | int tuner; /* tuner ID (from tuner.h) */ | ||
108 | }; | ||
109 | |||
110 | struct cx18_card_tuner_i2c { | ||
111 | unsigned short radio[2];/* radio tuner i2c address to probe */ | ||
112 | unsigned short demod[3];/* demodulator i2c address to probe */ | ||
113 | unsigned short tv[4]; /* tv tuner i2c addresses to probe */ | ||
114 | }; | ||
115 | |||
116 | struct cx18_ddr { /* DDR config data */ | ||
117 | u32 chip_config; | ||
118 | u32 refresh; | ||
119 | u32 timing1; | ||
120 | u32 timing2; | ||
121 | u32 tune_lane; | ||
122 | u32 initial_emrs; | ||
123 | }; | ||
124 | |||
125 | /* for card information/parameters */ | ||
126 | struct cx18_card { | ||
127 | int type; | ||
128 | char *name; | ||
129 | char *comment; | ||
130 | u32 v4l2_capabilities; | ||
131 | u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only | ||
132 | 1 dev allowed currently) */ | ||
133 | u32 hw_muxer; /* hardware used to multiplex audio input */ | ||
134 | u32 hw_all; /* all hardware used by the board */ | ||
135 | struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS]; | ||
136 | struct cx18_card_audio_input audio_inputs[CX18_CARD_MAX_AUDIO_INPUTS]; | ||
137 | struct cx18_card_audio_input radio_input; | ||
138 | |||
139 | /* GPIO card-specific settings */ | ||
140 | u8 xceive_pin; /* XCeive tuner GPIO reset pin */ | ||
141 | struct cx18_gpio_init gpio_init; | ||
142 | struct cx18_gpio_i2c_slave_reset gpio_i2c_slave_reset; | ||
143 | struct cx18_gpio_audio_input gpio_audio_input; | ||
144 | |||
145 | struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS]; | ||
146 | struct cx18_card_tuner_i2c *i2c; | ||
147 | |||
148 | struct cx18_ddr ddr; | ||
149 | |||
150 | /* list of device and subsystem vendor/devices that | ||
151 | correspond to this card type. */ | ||
152 | const struct cx18_card_pci_info *pci_list; | ||
153 | }; | ||
154 | |||
155 | int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input); | ||
156 | int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *input); | ||
157 | const struct cx18_card *cx18_get_card(u16 index); | ||
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c new file mode 100644 index 00000000000..282a3d29fda --- /dev/null +++ b/drivers/media/video/cx18/cx18-controls.c | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * cx18 ioctl control functions | ||
3 | * | ||
4 | * Derived from ivtv-controls.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
21 | * 02111-1307 USA | ||
22 | */ | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/slab.h> | ||
25 | |||
26 | #include "cx18-driver.h" | ||
27 | #include "cx18-cards.h" | ||
28 | #include "cx18-ioctl.h" | ||
29 | #include "cx18-audio.h" | ||
30 | #include "cx18-mailbox.h" | ||
31 | #include "cx18-controls.h" | ||
32 | |||
33 | static int cx18_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt) | ||
34 | { | ||
35 | struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); | ||
36 | int type = cxhdl->stream_type->val; | ||
37 | |||
38 | if (atomic_read(&cx->ana_capturing) > 0) | ||
39 | return -EBUSY; | ||
40 | |||
41 | if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV || | ||
42 | !(type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS || | ||
43 | type == V4L2_MPEG_STREAM_TYPE_MPEG2_DVD || | ||
44 | type == V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD)) { | ||
45 | /* Only IVTV fmt VBI insertion & only MPEG-2 PS type streams */ | ||
46 | cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE; | ||
47 | CX18_DEBUG_INFO("disabled insertion of sliced VBI data into " | ||
48 | "the MPEG stream\n"); | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | /* Allocate sliced VBI buffers if needed. */ | ||
53 | if (cx->vbi.sliced_mpeg_data[0] == NULL) { | ||
54 | int i; | ||
55 | |||
56 | for (i = 0; i < CX18_VBI_FRAMES; i++) { | ||
57 | cx->vbi.sliced_mpeg_data[i] = | ||
58 | kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ, GFP_KERNEL); | ||
59 | if (cx->vbi.sliced_mpeg_data[i] == NULL) { | ||
60 | while (--i >= 0) { | ||
61 | kfree(cx->vbi.sliced_mpeg_data[i]); | ||
62 | cx->vbi.sliced_mpeg_data[i] = NULL; | ||
63 | } | ||
64 | cx->vbi.insert_mpeg = | ||
65 | V4L2_MPEG_STREAM_VBI_FMT_NONE; | ||
66 | CX18_WARN("Unable to allocate buffers for " | ||
67 | "sliced VBI data insertion\n"); | ||
68 | return -ENOMEM; | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | |||
73 | cx->vbi.insert_mpeg = fmt; | ||
74 | CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS," | ||
75 | "when sliced VBI is enabled\n"); | ||
76 | |||
77 | /* | ||
78 | * If our current settings have no lines set for capture, store a valid, | ||
79 | * default set of service lines to capture, in our current settings. | ||
80 | */ | ||
81 | if (cx18_get_service_set(cx->vbi.sliced_in) == 0) { | ||
82 | if (cx->is_60hz) | ||
83 | cx->vbi.sliced_in->service_set = | ||
84 | V4L2_SLICED_CAPTION_525; | ||
85 | else | ||
86 | cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; | ||
87 | cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz); | ||
88 | } | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static int cx18_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val) | ||
93 | { | ||
94 | struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); | ||
95 | int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1; | ||
96 | struct v4l2_mbus_framefmt fmt; | ||
97 | |||
98 | /* fix videodecoder resolution */ | ||
99 | fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1); | ||
100 | fmt.height = cxhdl->height; | ||
101 | fmt.code = V4L2_MBUS_FMT_FIXED; | ||
102 | v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt); | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static int cx18_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx) | ||
107 | { | ||
108 | static const u32 freqs[3] = { 44100, 48000, 32000 }; | ||
109 | struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); | ||
110 | |||
111 | /* The audio clock of the digitizer must match the codec sample | ||
112 | rate otherwise you get some very strange effects. */ | ||
113 | if (idx < ARRAY_SIZE(freqs)) | ||
114 | cx18_call_all(cx, audio, s_clock_freq, freqs[idx]); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static int cx18_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val) | ||
119 | { | ||
120 | struct cx18 *cx = container_of(cxhdl, struct cx18, cxhdl); | ||
121 | |||
122 | cx->dualwatch_stereo_mode = val; | ||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | struct cx2341x_handler_ops cx18_cxhdl_ops = { | ||
127 | .s_audio_mode = cx18_s_audio_mode, | ||
128 | .s_audio_sampling_freq = cx18_s_audio_sampling_freq, | ||
129 | .s_video_encoding = cx18_s_video_encoding, | ||
130 | .s_stream_vbi_fmt = cx18_s_stream_vbi_fmt, | ||
131 | }; | ||
diff --git a/drivers/media/video/cx18/cx18-controls.h b/drivers/media/video/cx18/cx18-controls.h new file mode 100644 index 00000000000..cb5dfc7b205 --- /dev/null +++ b/drivers/media/video/cx18/cx18-controls.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * cx18 ioctl control functions | ||
3 | * | ||
4 | * Derived from ivtv-controls.h | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
21 | * 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | extern struct cx2341x_handler_ops cx18_cxhdl_ops; | ||
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c new file mode 100644 index 00000000000..9e2f870f425 --- /dev/null +++ b/drivers/media/video/cx18/cx18-driver.c | |||
@@ -0,0 +1,1357 @@ | |||
1 | /* | ||
2 | * cx18 driver initialization and card probing | ||
3 | * | ||
4 | * Derived from ivtv-driver.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | #include "cx18-io.h" | ||
27 | #include "cx18-version.h" | ||
28 | #include "cx18-cards.h" | ||
29 | #include "cx18-i2c.h" | ||
30 | #include "cx18-irq.h" | ||
31 | #include "cx18-gpio.h" | ||
32 | #include "cx18-firmware.h" | ||
33 | #include "cx18-queue.h" | ||
34 | #include "cx18-streams.h" | ||
35 | #include "cx18-av-core.h" | ||
36 | #include "cx18-scb.h" | ||
37 | #include "cx18-mailbox.h" | ||
38 | #include "cx18-ioctl.h" | ||
39 | #include "cx18-controls.h" | ||
40 | #include "tuner-xc2028.h" | ||
41 | |||
42 | #include <media/tveeprom.h> | ||
43 | |||
44 | /* If you have already X v4l cards, then set this to X. This way | ||
45 | the device numbers stay matched. Example: you have a WinTV card | ||
46 | without radio and a Compro H900 with. Normally this would give a | ||
47 | video1 device together with a radio0 device for the Compro. By | ||
48 | setting this to 1 you ensure that radio0 is now also radio1. */ | ||
49 | int cx18_first_minor; | ||
50 | |||
51 | /* Callback for registering extensions */ | ||
52 | int (*cx18_ext_init)(struct cx18 *); | ||
53 | EXPORT_SYMBOL(cx18_ext_init); | ||
54 | |||
55 | /* add your revision and whatnot here */ | ||
56 | static struct pci_device_id cx18_pci_tbl[] __devinitdata = { | ||
57 | {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418, | ||
58 | PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, | ||
59 | {0,} | ||
60 | }; | ||
61 | |||
62 | MODULE_DEVICE_TABLE(pci, cx18_pci_tbl); | ||
63 | |||
64 | static atomic_t cx18_instance = ATOMIC_INIT(0); | ||
65 | |||
66 | /* Parameter declarations */ | ||
67 | static int cardtype[CX18_MAX_CARDS]; | ||
68 | static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, | ||
69 | -1, -1, -1, -1, -1, -1, -1, -1, | ||
70 | -1, -1, -1, -1, -1, -1, -1, -1, | ||
71 | -1, -1, -1, -1, -1, -1, -1, -1 }; | ||
72 | static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, | ||
73 | -1, -1, -1, -1, -1, -1, -1, -1, | ||
74 | -1, -1, -1, -1, -1, -1, -1, -1, | ||
75 | -1, -1, -1, -1, -1, -1, -1, -1 }; | ||
76 | static unsigned cardtype_c = 1; | ||
77 | static unsigned tuner_c = 1; | ||
78 | static unsigned radio_c = 1; | ||
79 | static char pal[] = "--"; | ||
80 | static char secam[] = "--"; | ||
81 | static char ntsc[] = "-"; | ||
82 | |||
83 | /* Buffers */ | ||
84 | static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS; | ||
85 | static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS; | ||
86 | static int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS; | ||
87 | static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS; | ||
88 | static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS; | ||
89 | static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS; | ||
90 | |||
91 | static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE; | ||
92 | static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE; | ||
93 | static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE; | ||
94 | static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE; | ||
95 | static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE; | ||
96 | |||
97 | static int enc_ts_bufs = -1; | ||
98 | static int enc_mpg_bufs = -1; | ||
99 | static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM; | ||
100 | static int enc_yuv_bufs = -1; | ||
101 | static int enc_vbi_bufs = -1; | ||
102 | static int enc_pcm_bufs = -1; | ||
103 | |||
104 | |||
105 | static int cx18_pci_latency = 1; | ||
106 | |||
107 | static int mmio_ndelay; | ||
108 | static int retry_mmio = 1; | ||
109 | |||
110 | int cx18_debug; | ||
111 | |||
112 | module_param_array(tuner, int, &tuner_c, 0644); | ||
113 | module_param_array(radio, bool, &radio_c, 0644); | ||
114 | module_param_array(cardtype, int, &cardtype_c, 0644); | ||
115 | module_param_string(pal, pal, sizeof(pal), 0644); | ||
116 | module_param_string(secam, secam, sizeof(secam), 0644); | ||
117 | module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); | ||
118 | module_param_named(debug, cx18_debug, int, 0644); | ||
119 | module_param(mmio_ndelay, int, 0644); | ||
120 | module_param(retry_mmio, int, 0644); | ||
121 | module_param(cx18_pci_latency, int, 0644); | ||
122 | module_param(cx18_first_minor, int, 0644); | ||
123 | |||
124 | module_param(enc_ts_buffers, int, 0644); | ||
125 | module_param(enc_mpg_buffers, int, 0644); | ||
126 | module_param(enc_idx_buffers, int, 0644); | ||
127 | module_param(enc_yuv_buffers, int, 0644); | ||
128 | module_param(enc_vbi_buffers, int, 0644); | ||
129 | module_param(enc_pcm_buffers, int, 0644); | ||
130 | |||
131 | module_param(enc_ts_bufsize, int, 0644); | ||
132 | module_param(enc_mpg_bufsize, int, 0644); | ||
133 | module_param(enc_idx_bufsize, int, 0644); | ||
134 | module_param(enc_yuv_bufsize, int, 0644); | ||
135 | module_param(enc_pcm_bufsize, int, 0644); | ||
136 | |||
137 | module_param(enc_ts_bufs, int, 0644); | ||
138 | module_param(enc_mpg_bufs, int, 0644); | ||
139 | module_param(enc_idx_bufs, int, 0644); | ||
140 | module_param(enc_yuv_bufs, int, 0644); | ||
141 | module_param(enc_vbi_bufs, int, 0644); | ||
142 | module_param(enc_pcm_bufs, int, 0644); | ||
143 | |||
144 | MODULE_PARM_DESC(tuner, "Tuner type selection,\n" | ||
145 | "\t\t\tsee tuner.h for values"); | ||
146 | MODULE_PARM_DESC(radio, | ||
147 | "Enable or disable the radio. Use only if autodetection\n" | ||
148 | "\t\t\tfails. 0 = disable, 1 = enable"); | ||
149 | MODULE_PARM_DESC(cardtype, | ||
150 | "Only use this option if your card is not detected properly.\n" | ||
151 | "\t\tSpecify card type:\n" | ||
152 | "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n" | ||
153 | "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n" | ||
154 | "\t\t\t 3 = Compro VideoMate H900\n" | ||
155 | "\t\t\t 4 = Yuan MPC718\n" | ||
156 | "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" | ||
157 | "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n" | ||
158 | "\t\t\t 7 = Leadtek WinFast PVR2100\n" | ||
159 | "\t\t\t 8 = Leadtek WinFast DVR3100 H\n" | ||
160 | "\t\t\t 9 = GoTView PCI DVD3 Hybrid\n" | ||
161 | "\t\t\t 10 = Hauppauge HVR 1600 (S5H1411)\n" | ||
162 | "\t\t\t 0 = Autodetect (default)\n" | ||
163 | "\t\t\t-1 = Ignore this card\n\t\t"); | ||
164 | MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); | ||
165 | MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC"); | ||
166 | MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K"); | ||
167 | MODULE_PARM_DESC(debug, | ||
168 | "Debug level (bitmask). Default: 0\n" | ||
169 | "\t\t\t 1/0x0001: warning\n" | ||
170 | "\t\t\t 2/0x0002: info\n" | ||
171 | "\t\t\t 4/0x0004: mailbox\n" | ||
172 | "\t\t\t 8/0x0008: dma\n" | ||
173 | "\t\t\t 16/0x0010: ioctl\n" | ||
174 | "\t\t\t 32/0x0020: file\n" | ||
175 | "\t\t\t 64/0x0040: i2c\n" | ||
176 | "\t\t\t128/0x0080: irq\n" | ||
177 | "\t\t\t256/0x0100: high volume\n"); | ||
178 | MODULE_PARM_DESC(cx18_pci_latency, | ||
179 | "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" | ||
180 | "\t\t\tDefault: Yes"); | ||
181 | MODULE_PARM_DESC(retry_mmio, | ||
182 | "(Deprecated) MMIO writes are now always checked and retried\n" | ||
183 | "\t\t\tEffectively: 1 [Yes]"); | ||
184 | MODULE_PARM_DESC(mmio_ndelay, | ||
185 | "(Deprecated) MMIO accesses are now never purposely delayed\n" | ||
186 | "\t\t\tEffectively: 0 ns"); | ||
187 | MODULE_PARM_DESC(enc_ts_buffers, | ||
188 | "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n" | ||
189 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS)); | ||
190 | MODULE_PARM_DESC(enc_ts_bufsize, | ||
191 | "Size of an encoder TS buffer (kB)\n" | ||
192 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE)); | ||
193 | MODULE_PARM_DESC(enc_ts_bufs, | ||
194 | "Number of encoder TS buffers\n" | ||
195 | "\t\t\tDefault is computed from other enc_ts_* parameters"); | ||
196 | MODULE_PARM_DESC(enc_mpg_buffers, | ||
197 | "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n" | ||
198 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS)); | ||
199 | MODULE_PARM_DESC(enc_mpg_bufsize, | ||
200 | "Size of an encoder MPG buffer (kB)\n" | ||
201 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE)); | ||
202 | MODULE_PARM_DESC(enc_mpg_bufs, | ||
203 | "Number of encoder MPG buffers\n" | ||
204 | "\t\t\tDefault is computed from other enc_mpg_* parameters"); | ||
205 | MODULE_PARM_DESC(enc_idx_buffers, | ||
206 | "(Deprecated) Encoder IDX buffer memory (MB)\n" | ||
207 | "\t\t\tIgnored, except 0 disables IDX buffer allocations\n" | ||
208 | "\t\t\tDefault: 1 [Enabled]"); | ||
209 | MODULE_PARM_DESC(enc_idx_bufsize, | ||
210 | "Size of an encoder IDX buffer (kB)\n" | ||
211 | "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n" | ||
212 | "\t\t\t(multiples of size required for 64 index entries)\n" | ||
213 | "\t\t\tDefault: 2"); | ||
214 | MODULE_PARM_DESC(enc_idx_bufs, | ||
215 | "Number of encoder IDX buffers\n" | ||
216 | "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM)); | ||
217 | MODULE_PARM_DESC(enc_yuv_buffers, | ||
218 | "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n" | ||
219 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); | ||
220 | MODULE_PARM_DESC(enc_yuv_bufsize, | ||
221 | "Size of an encoder YUV buffer (kB)\n" | ||
222 | "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n" | ||
223 | "\t\t\t(multiples of size required for 32 screen lines)\n" | ||
224 | "\t\t\tDefault: 102"); | ||
225 | MODULE_PARM_DESC(enc_yuv_bufs, | ||
226 | "Number of encoder YUV buffers\n" | ||
227 | "\t\t\tDefault is computed from other enc_yuv_* parameters"); | ||
228 | MODULE_PARM_DESC(enc_vbi_buffers, | ||
229 | "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n" | ||
230 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS)); | ||
231 | MODULE_PARM_DESC(enc_vbi_bufs, | ||
232 | "Number of encoder VBI buffers\n" | ||
233 | "\t\t\tDefault is computed from enc_vbi_buffers"); | ||
234 | MODULE_PARM_DESC(enc_pcm_buffers, | ||
235 | "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n" | ||
236 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS)); | ||
237 | MODULE_PARM_DESC(enc_pcm_bufsize, | ||
238 | "Size of an encoder PCM buffer (kB)\n" | ||
239 | "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE)); | ||
240 | MODULE_PARM_DESC(enc_pcm_bufs, | ||
241 | "Number of encoder PCM buffers\n" | ||
242 | "\t\t\tDefault is computed from other enc_pcm_* parameters"); | ||
243 | |||
244 | MODULE_PARM_DESC(cx18_first_minor, | ||
245 | "Set device node number assigned to first card"); | ||
246 | |||
247 | MODULE_AUTHOR("Hans Verkuil"); | ||
248 | MODULE_DESCRIPTION("CX23418 driver"); | ||
249 | MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder"); | ||
250 | MODULE_LICENSE("GPL"); | ||
251 | |||
252 | MODULE_VERSION(CX18_VERSION); | ||
253 | |||
254 | #if defined(CONFIG_MODULES) && defined(MODULE) | ||
255 | static void request_module_async(struct work_struct *work) | ||
256 | { | ||
257 | struct cx18 *dev = container_of(work, struct cx18, request_module_wk); | ||
258 | |||
259 | /* Make sure cx18-alsa module is loaded */ | ||
260 | request_module("cx18-alsa"); | ||
261 | |||
262 | /* Initialize cx18-alsa for this instance of the cx18 device */ | ||
263 | if (cx18_ext_init != NULL) | ||
264 | cx18_ext_init(dev); | ||
265 | } | ||
266 | |||
267 | static void request_modules(struct cx18 *dev) | ||
268 | { | ||
269 | INIT_WORK(&dev->request_module_wk, request_module_async); | ||
270 | schedule_work(&dev->request_module_wk); | ||
271 | } | ||
272 | |||
273 | static void flush_request_modules(struct cx18 *dev) | ||
274 | { | ||
275 | flush_work_sync(&dev->request_module_wk); | ||
276 | } | ||
277 | #else | ||
278 | #define request_modules(dev) | ||
279 | #define flush_request_modules(dev) | ||
280 | #endif /* CONFIG_MODULES */ | ||
281 | |||
282 | /* Generic utility functions */ | ||
283 | int cx18_msleep_timeout(unsigned int msecs, int intr) | ||
284 | { | ||
285 | long int timeout = msecs_to_jiffies(msecs); | ||
286 | int sig; | ||
287 | |||
288 | do { | ||
289 | set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); | ||
290 | timeout = schedule_timeout(timeout); | ||
291 | sig = intr ? signal_pending(current) : 0; | ||
292 | } while (!sig && timeout); | ||
293 | return sig; | ||
294 | } | ||
295 | |||
296 | /* Release ioremapped memory */ | ||
297 | static void cx18_iounmap(struct cx18 *cx) | ||
298 | { | ||
299 | if (cx == NULL) | ||
300 | return; | ||
301 | |||
302 | /* Release io memory */ | ||
303 | if (cx->enc_mem != NULL) { | ||
304 | CX18_DEBUG_INFO("releasing enc_mem\n"); | ||
305 | iounmap(cx->enc_mem); | ||
306 | cx->enc_mem = NULL; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | static void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len) | ||
311 | { | ||
312 | int i; | ||
313 | |||
314 | CX18_INFO("eeprom dump:\n"); | ||
315 | for (i = 0; i < len; i++) { | ||
316 | if (0 == (i % 16)) | ||
317 | CX18_INFO("eeprom %02x:", i); | ||
318 | printk(KERN_CONT " %02x", eedata[i]); | ||
319 | if (15 == (i % 16)) | ||
320 | printk(KERN_CONT "\n"); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | /* Hauppauge card? get values from tveeprom */ | ||
325 | void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv) | ||
326 | { | ||
327 | struct i2c_client c; | ||
328 | u8 eedata[256]; | ||
329 | |||
330 | memset(&c, 0, sizeof(c)); | ||
331 | strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name)); | ||
332 | c.adapter = &cx->i2c_adap[0]; | ||
333 | c.addr = 0xA0 >> 1; | ||
334 | |||
335 | memset(tv, 0, sizeof(*tv)); | ||
336 | if (tveeprom_read(&c, eedata, sizeof(eedata))) | ||
337 | return; | ||
338 | |||
339 | switch (cx->card->type) { | ||
340 | case CX18_CARD_HVR_1600_ESMT: | ||
341 | case CX18_CARD_HVR_1600_SAMSUNG: | ||
342 | case CX18_CARD_HVR_1600_S5H1411: | ||
343 | tveeprom_hauppauge_analog(&c, tv, eedata); | ||
344 | break; | ||
345 | case CX18_CARD_YUAN_MPC718: | ||
346 | case CX18_CARD_GOTVIEW_PCI_DVD3: | ||
347 | tv->model = 0x718; | ||
348 | cx18_eeprom_dump(cx, eedata, sizeof(eedata)); | ||
349 | CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n", | ||
350 | eedata[2], eedata[1], eedata[4], eedata[3]); | ||
351 | break; | ||
352 | default: | ||
353 | tv->model = 0xffffffff; | ||
354 | cx18_eeprom_dump(cx, eedata, sizeof(eedata)); | ||
355 | break; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | static void cx18_process_eeprom(struct cx18 *cx) | ||
360 | { | ||
361 | struct tveeprom tv; | ||
362 | |||
363 | cx18_read_eeprom(cx, &tv); | ||
364 | |||
365 | /* Many thanks to Steven Toth from Hauppauge for providing the | ||
366 | model numbers */ | ||
367 | /* Note: the Samsung memory models cannot be reliably determined | ||
368 | from the model number. Use the cardtype module option if you | ||
369 | have one of these preproduction models. */ | ||
370 | switch (tv.model) { | ||
371 | case 74301: /* Retail models */ | ||
372 | case 74321: | ||
373 | case 74351: /* OEM models */ | ||
374 | case 74361: | ||
375 | /* Digital side is s5h1411/tda18271 */ | ||
376 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_S5H1411); | ||
377 | break; | ||
378 | case 74021: /* Retail models */ | ||
379 | case 74031: | ||
380 | case 74041: | ||
381 | case 74141: | ||
382 | case 74541: /* OEM models */ | ||
383 | case 74551: | ||
384 | case 74591: | ||
385 | case 74651: | ||
386 | case 74691: | ||
387 | case 74751: | ||
388 | case 74891: | ||
389 | /* Digital side is s5h1409/mxl5005s */ | ||
390 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); | ||
391 | break; | ||
392 | case 0x718: | ||
393 | return; | ||
394 | case 0xffffffff: | ||
395 | CX18_INFO("Unknown EEPROM encoding\n"); | ||
396 | return; | ||
397 | case 0: | ||
398 | CX18_ERR("Invalid EEPROM\n"); | ||
399 | return; | ||
400 | default: | ||
401 | CX18_ERR("Unknown model %d, defaulting to original HVR-1600 " | ||
402 | "(cardtype=1)\n", tv.model); | ||
403 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); | ||
404 | break; | ||
405 | } | ||
406 | |||
407 | cx->v4l2_cap = cx->card->v4l2_capabilities; | ||
408 | cx->card_name = cx->card->name; | ||
409 | cx->card_i2c = cx->card->i2c; | ||
410 | |||
411 | CX18_INFO("Autodetected %s\n", cx->card_name); | ||
412 | |||
413 | if (tv.tuner_type == TUNER_ABSENT) | ||
414 | CX18_ERR("tveeprom cannot autodetect tuner!\n"); | ||
415 | |||
416 | if (cx->options.tuner == -1) | ||
417 | cx->options.tuner = tv.tuner_type; | ||
418 | if (cx->options.radio == -1) | ||
419 | cx->options.radio = (tv.has_radio != 0); | ||
420 | |||
421 | if (cx->std != 0) | ||
422 | /* user specified tuner standard */ | ||
423 | return; | ||
424 | |||
425 | /* autodetect tuner standard */ | ||
426 | #define TVEEPROM_TUNER_FORMAT_ALL (V4L2_STD_B | V4L2_STD_GH | \ | ||
427 | V4L2_STD_MN | \ | ||
428 | V4L2_STD_PAL_I | \ | ||
429 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC | \ | ||
430 | V4L2_STD_DK) | ||
431 | if ((tv.tuner_formats & TVEEPROM_TUNER_FORMAT_ALL) | ||
432 | == TVEEPROM_TUNER_FORMAT_ALL) { | ||
433 | CX18_DEBUG_INFO("Worldwide tuner detected\n"); | ||
434 | cx->std = V4L2_STD_ALL; | ||
435 | } else if (tv.tuner_formats & V4L2_STD_PAL) { | ||
436 | CX18_DEBUG_INFO("PAL tuner detected\n"); | ||
437 | cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H; | ||
438 | } else if (tv.tuner_formats & V4L2_STD_NTSC) { | ||
439 | CX18_DEBUG_INFO("NTSC tuner detected\n"); | ||
440 | cx->std |= V4L2_STD_NTSC_M; | ||
441 | } else if (tv.tuner_formats & V4L2_STD_SECAM) { | ||
442 | CX18_DEBUG_INFO("SECAM tuner detected\n"); | ||
443 | cx->std |= V4L2_STD_SECAM_L; | ||
444 | } else { | ||
445 | CX18_INFO("No tuner detected, default to NTSC-M\n"); | ||
446 | cx->std |= V4L2_STD_NTSC_M; | ||
447 | } | ||
448 | } | ||
449 | |||
450 | static v4l2_std_id cx18_parse_std(struct cx18 *cx) | ||
451 | { | ||
452 | switch (pal[0]) { | ||
453 | case '6': | ||
454 | return V4L2_STD_PAL_60; | ||
455 | case 'b': | ||
456 | case 'B': | ||
457 | case 'g': | ||
458 | case 'G': | ||
459 | return V4L2_STD_PAL_BG; | ||
460 | case 'h': | ||
461 | case 'H': | ||
462 | return V4L2_STD_PAL_H; | ||
463 | case 'n': | ||
464 | case 'N': | ||
465 | if (pal[1] == 'c' || pal[1] == 'C') | ||
466 | return V4L2_STD_PAL_Nc; | ||
467 | return V4L2_STD_PAL_N; | ||
468 | case 'i': | ||
469 | case 'I': | ||
470 | return V4L2_STD_PAL_I; | ||
471 | case 'd': | ||
472 | case 'D': | ||
473 | case 'k': | ||
474 | case 'K': | ||
475 | return V4L2_STD_PAL_DK; | ||
476 | case 'M': | ||
477 | case 'm': | ||
478 | return V4L2_STD_PAL_M; | ||
479 | case '-': | ||
480 | break; | ||
481 | default: | ||
482 | CX18_WARN("pal= argument not recognised\n"); | ||
483 | return 0; | ||
484 | } | ||
485 | |||
486 | switch (secam[0]) { | ||
487 | case 'b': | ||
488 | case 'B': | ||
489 | case 'g': | ||
490 | case 'G': | ||
491 | case 'h': | ||
492 | case 'H': | ||
493 | return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; | ||
494 | case 'd': | ||
495 | case 'D': | ||
496 | case 'k': | ||
497 | case 'K': | ||
498 | return V4L2_STD_SECAM_DK; | ||
499 | case 'l': | ||
500 | case 'L': | ||
501 | if (secam[1] == 'C' || secam[1] == 'c') | ||
502 | return V4L2_STD_SECAM_LC; | ||
503 | return V4L2_STD_SECAM_L; | ||
504 | case '-': | ||
505 | break; | ||
506 | default: | ||
507 | CX18_WARN("secam= argument not recognised\n"); | ||
508 | return 0; | ||
509 | } | ||
510 | |||
511 | switch (ntsc[0]) { | ||
512 | case 'm': | ||
513 | case 'M': | ||
514 | return V4L2_STD_NTSC_M; | ||
515 | case 'j': | ||
516 | case 'J': | ||
517 | return V4L2_STD_NTSC_M_JP; | ||
518 | case 'k': | ||
519 | case 'K': | ||
520 | return V4L2_STD_NTSC_M_KR; | ||
521 | case '-': | ||
522 | break; | ||
523 | default: | ||
524 | CX18_WARN("ntsc= argument not recognised\n"); | ||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | /* no match found */ | ||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | static void cx18_process_options(struct cx18 *cx) | ||
533 | { | ||
534 | int i, j; | ||
535 | |||
536 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers; | ||
537 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers; | ||
538 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers; | ||
539 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers; | ||
540 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers; | ||
541 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers; | ||
542 | cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */ | ||
543 | |||
544 | cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs; | ||
545 | cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs; | ||
546 | cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs; | ||
547 | cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs; | ||
548 | cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs; | ||
549 | cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs; | ||
550 | cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */ | ||
551 | |||
552 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize; | ||
553 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize; | ||
554 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize; | ||
555 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize; | ||
556 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36; | ||
557 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize; | ||
558 | cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */ | ||
559 | |||
560 | /* Ensure stream_buffers & stream_buf_size are valid */ | ||
561 | for (i = 0; i < CX18_MAX_STREAMS; i++) { | ||
562 | if (cx->stream_buffers[i] == 0 || /* User said 0 buffers */ | ||
563 | cx->options.megabytes[i] <= 0 || /* User said 0 MB total */ | ||
564 | cx->stream_buf_size[i] <= 0) { /* User said buf size 0 */ | ||
565 | cx->options.megabytes[i] = 0; | ||
566 | cx->stream_buffers[i] = 0; | ||
567 | cx->stream_buf_size[i] = 0; | ||
568 | continue; | ||
569 | } | ||
570 | /* | ||
571 | * YUV is a special case where the stream_buf_size needs to be | ||
572 | * an integral multiple of 33.75 kB (storage for 32 screens | ||
573 | * lines to maintain alignment in case of lost buffers). | ||
574 | * | ||
575 | * IDX is a special case where the stream_buf_size should be | ||
576 | * an integral multiple of 1.5 kB (storage for 64 index entries | ||
577 | * to maintain alignment in case of lost buffers). | ||
578 | * | ||
579 | */ | ||
580 | if (i == CX18_ENC_STREAM_TYPE_YUV) { | ||
581 | cx->stream_buf_size[i] *= 1024; | ||
582 | cx->stream_buf_size[i] -= | ||
583 | (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE); | ||
584 | |||
585 | if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE) | ||
586 | cx->stream_buf_size[i] = | ||
587 | CX18_UNIT_ENC_YUV_BUFSIZE; | ||
588 | } else if (i == CX18_ENC_STREAM_TYPE_IDX) { | ||
589 | cx->stream_buf_size[i] *= 1024; | ||
590 | cx->stream_buf_size[i] -= | ||
591 | (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE); | ||
592 | |||
593 | if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE) | ||
594 | cx->stream_buf_size[i] = | ||
595 | CX18_UNIT_ENC_IDX_BUFSIZE; | ||
596 | } | ||
597 | /* | ||
598 | * YUV and IDX are special cases where the stream_buf_size is | ||
599 | * now in bytes. | ||
600 | * VBI is a special case where the stream_buf_size is fixed | ||
601 | * and already in bytes | ||
602 | */ | ||
603 | if (i == CX18_ENC_STREAM_TYPE_VBI || | ||
604 | i == CX18_ENC_STREAM_TYPE_YUV || | ||
605 | i == CX18_ENC_STREAM_TYPE_IDX) { | ||
606 | if (cx->stream_buffers[i] < 0) { | ||
607 | cx->stream_buffers[i] = | ||
608 | cx->options.megabytes[i] * 1024 * 1024 | ||
609 | / cx->stream_buf_size[i]; | ||
610 | } else { | ||
611 | /* N.B. This might round down to 0 */ | ||
612 | cx->options.megabytes[i] = | ||
613 | cx->stream_buffers[i] | ||
614 | * cx->stream_buf_size[i]/(1024 * 1024); | ||
615 | } | ||
616 | } else { | ||
617 | /* All other streams have stream_buf_size in kB here */ | ||
618 | if (cx->stream_buffers[i] < 0) { | ||
619 | cx->stream_buffers[i] = | ||
620 | cx->options.megabytes[i] * 1024 | ||
621 | / cx->stream_buf_size[i]; | ||
622 | } else { | ||
623 | /* N.B. This might round down to 0 */ | ||
624 | cx->options.megabytes[i] = | ||
625 | cx->stream_buffers[i] | ||
626 | * cx->stream_buf_size[i] / 1024; | ||
627 | } | ||
628 | /* convert from kB to bytes */ | ||
629 | cx->stream_buf_size[i] *= 1024; | ||
630 | } | ||
631 | CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, " | ||
632 | "%d bytes\n", i, cx->options.megabytes[i], | ||
633 | cx->stream_buffers[i], cx->stream_buf_size[i]); | ||
634 | } | ||
635 | |||
636 | cx->options.cardtype = cardtype[cx->instance]; | ||
637 | cx->options.tuner = tuner[cx->instance]; | ||
638 | cx->options.radio = radio[cx->instance]; | ||
639 | |||
640 | cx->std = cx18_parse_std(cx); | ||
641 | if (cx->options.cardtype == -1) { | ||
642 | CX18_INFO("Ignore card\n"); | ||
643 | return; | ||
644 | } | ||
645 | cx->card = cx18_get_card(cx->options.cardtype - 1); | ||
646 | if (cx->card) | ||
647 | CX18_INFO("User specified %s card\n", cx->card->name); | ||
648 | else if (cx->options.cardtype != 0) | ||
649 | CX18_ERR("Unknown user specified type, trying to autodetect card\n"); | ||
650 | if (cx->card == NULL) { | ||
651 | if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) { | ||
652 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); | ||
653 | CX18_INFO("Autodetected Hauppauge card\n"); | ||
654 | } | ||
655 | } | ||
656 | if (cx->card == NULL) { | ||
657 | for (i = 0; (cx->card = cx18_get_card(i)); i++) { | ||
658 | if (cx->card->pci_list == NULL) | ||
659 | continue; | ||
660 | for (j = 0; cx->card->pci_list[j].device; j++) { | ||
661 | if (cx->pci_dev->device != | ||
662 | cx->card->pci_list[j].device) | ||
663 | continue; | ||
664 | if (cx->pci_dev->subsystem_vendor != | ||
665 | cx->card->pci_list[j].subsystem_vendor) | ||
666 | continue; | ||
667 | if (cx->pci_dev->subsystem_device != | ||
668 | cx->card->pci_list[j].subsystem_device) | ||
669 | continue; | ||
670 | CX18_INFO("Autodetected %s card\n", cx->card->name); | ||
671 | goto done; | ||
672 | } | ||
673 | } | ||
674 | } | ||
675 | done: | ||
676 | |||
677 | if (cx->card == NULL) { | ||
678 | cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT); | ||
679 | CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n", | ||
680 | cx->pci_dev->vendor, cx->pci_dev->device); | ||
681 | CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n", | ||
682 | cx->pci_dev->subsystem_vendor, | ||
683 | cx->pci_dev->subsystem_device); | ||
684 | CX18_ERR("Defaulting to %s card\n", cx->card->name); | ||
685 | CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); | ||
686 | CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n"); | ||
687 | CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n"); | ||
688 | } | ||
689 | cx->v4l2_cap = cx->card->v4l2_capabilities; | ||
690 | cx->card_name = cx->card->name; | ||
691 | cx->card_i2c = cx->card->i2c; | ||
692 | } | ||
693 | |||
694 | static int __devinit cx18_create_in_workq(struct cx18 *cx) | ||
695 | { | ||
696 | snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in", | ||
697 | cx->v4l2_dev.name); | ||
698 | cx->in_work_queue = alloc_ordered_workqueue(cx->in_workq_name, 0); | ||
699 | if (cx->in_work_queue == NULL) { | ||
700 | CX18_ERR("Unable to create incoming mailbox handler thread\n"); | ||
701 | return -ENOMEM; | ||
702 | } | ||
703 | return 0; | ||
704 | } | ||
705 | |||
706 | static void __devinit cx18_init_in_work_orders(struct cx18 *cx) | ||
707 | { | ||
708 | int i; | ||
709 | for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) { | ||
710 | cx->in_work_order[i].cx = cx; | ||
711 | cx->in_work_order[i].str = cx->epu_debug_str; | ||
712 | INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler); | ||
713 | } | ||
714 | } | ||
715 | |||
716 | /* Precondition: the cx18 structure has been memset to 0. Only | ||
717 | the dev and instance fields have been filled in. | ||
718 | No assumptions on the card type may be made here (see cx18_init_struct2 | ||
719 | for that). | ||
720 | */ | ||
721 | static int __devinit cx18_init_struct1(struct cx18 *cx) | ||
722 | { | ||
723 | int ret; | ||
724 | |||
725 | cx->base_addr = pci_resource_start(cx->pci_dev, 0); | ||
726 | |||
727 | mutex_init(&cx->serialize_lock); | ||
728 | mutex_init(&cx->gpio_lock); | ||
729 | mutex_init(&cx->epu2apu_mb_lock); | ||
730 | mutex_init(&cx->epu2cpu_mb_lock); | ||
731 | |||
732 | ret = cx18_create_in_workq(cx); | ||
733 | if (ret) | ||
734 | return ret; | ||
735 | |||
736 | cx18_init_in_work_orders(cx); | ||
737 | |||
738 | /* start counting open_id at 1 */ | ||
739 | cx->open_id = 1; | ||
740 | |||
741 | /* Initial settings */ | ||
742 | cx->cxhdl.port = CX2341X_PORT_MEMORY; | ||
743 | cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI; | ||
744 | cx->cxhdl.ops = &cx18_cxhdl_ops; | ||
745 | cx->cxhdl.func = cx18_api_func; | ||
746 | cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; | ||
747 | ret = cx2341x_handler_init(&cx->cxhdl, 50); | ||
748 | if (ret) | ||
749 | return ret; | ||
750 | cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl; | ||
751 | |||
752 | cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val; | ||
753 | cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val; | ||
754 | cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val | | ||
755 | (cx->cxhdl.video_temporal_filter_mode->cur.val << 1) | | ||
756 | (cx->cxhdl.video_median_filter_type->cur.val << 2); | ||
757 | |||
758 | init_waitqueue_head(&cx->cap_w); | ||
759 | init_waitqueue_head(&cx->mb_apu_waitq); | ||
760 | init_waitqueue_head(&cx->mb_cpu_waitq); | ||
761 | init_waitqueue_head(&cx->dma_waitq); | ||
762 | |||
763 | /* VBI */ | ||
764 | cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; | ||
765 | cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; | ||
766 | |||
767 | /* IVTV style VBI insertion into MPEG streams */ | ||
768 | INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list); | ||
769 | INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list); | ||
770 | INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list); | ||
771 | list_add(&cx->vbi.sliced_mpeg_buf.list, | ||
772 | &cx->vbi.sliced_mpeg_mdl.buf_list); | ||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | /* Second initialization part. Here the card type has been | ||
777 | autodetected. */ | ||
778 | static void __devinit cx18_init_struct2(struct cx18 *cx) | ||
779 | { | ||
780 | int i; | ||
781 | |||
782 | for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++) | ||
783 | if (cx->card->video_inputs[i].video_type == 0) | ||
784 | break; | ||
785 | cx->nof_inputs = i; | ||
786 | for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++) | ||
787 | if (cx->card->audio_inputs[i].audio_type == 0) | ||
788 | break; | ||
789 | cx->nof_audio_inputs = i; | ||
790 | |||
791 | /* Find tuner input */ | ||
792 | for (i = 0; i < cx->nof_inputs; i++) { | ||
793 | if (cx->card->video_inputs[i].video_type == | ||
794 | CX18_CARD_INPUT_VID_TUNER) | ||
795 | break; | ||
796 | } | ||
797 | if (i == cx->nof_inputs) | ||
798 | i = 0; | ||
799 | cx->active_input = i; | ||
800 | cx->audio_input = cx->card->video_inputs[i].audio_index; | ||
801 | } | ||
802 | |||
803 | static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev, | ||
804 | const struct pci_device_id *pci_id) | ||
805 | { | ||
806 | u16 cmd; | ||
807 | unsigned char pci_latency; | ||
808 | |||
809 | CX18_DEBUG_INFO("Enabling pci device\n"); | ||
810 | |||
811 | if (pci_enable_device(pci_dev)) { | ||
812 | CX18_ERR("Can't enable device %d!\n", cx->instance); | ||
813 | return -EIO; | ||
814 | } | ||
815 | if (pci_set_dma_mask(pci_dev, 0xffffffff)) { | ||
816 | CX18_ERR("No suitable DMA available, card %d\n", cx->instance); | ||
817 | return -EIO; | ||
818 | } | ||
819 | if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) { | ||
820 | CX18_ERR("Cannot request encoder memory region, card %d\n", | ||
821 | cx->instance); | ||
822 | return -EIO; | ||
823 | } | ||
824 | |||
825 | /* Enable bus mastering and memory mapped IO for the CX23418 */ | ||
826 | pci_read_config_word(pci_dev, PCI_COMMAND, &cmd); | ||
827 | cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; | ||
828 | pci_write_config_word(pci_dev, PCI_COMMAND, cmd); | ||
829 | |||
830 | cx->card_rev = pci_dev->revision; | ||
831 | pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency); | ||
832 | |||
833 | if (pci_latency < 64 && cx18_pci_latency) { | ||
834 | CX18_INFO("Unreasonably low latency timer, " | ||
835 | "setting to 64 (was %d)\n", pci_latency); | ||
836 | pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64); | ||
837 | pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency); | ||
838 | } | ||
839 | |||
840 | CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, " | ||
841 | "irq: %d, latency: %d, memory: 0x%lx\n", | ||
842 | cx->pci_dev->device, cx->card_rev, pci_dev->bus->number, | ||
843 | PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn), | ||
844 | cx->pci_dev->irq, pci_latency, (unsigned long)cx->base_addr); | ||
845 | |||
846 | return 0; | ||
847 | } | ||
848 | |||
849 | static void cx18_init_subdevs(struct cx18 *cx) | ||
850 | { | ||
851 | u32 hw = cx->card->hw_all; | ||
852 | u32 device; | ||
853 | int i; | ||
854 | |||
855 | for (i = 0, device = 1; i < 32; i++, device <<= 1) { | ||
856 | |||
857 | if (!(device & hw)) | ||
858 | continue; | ||
859 | |||
860 | switch (device) { | ||
861 | case CX18_HW_DVB: | ||
862 | case CX18_HW_TVEEPROM: | ||
863 | /* These subordinate devices do not use probing */ | ||
864 | cx->hw_flags |= device; | ||
865 | break; | ||
866 | case CX18_HW_418_AV: | ||
867 | /* The A/V decoder gets probed earlier to set PLLs */ | ||
868 | /* Just note that the card uses it (i.e. has analog) */ | ||
869 | cx->hw_flags |= device; | ||
870 | break; | ||
871 | case CX18_HW_GPIO_RESET_CTRL: | ||
872 | /* | ||
873 | * The Reset Controller gets probed and added to | ||
874 | * hw_flags earlier for i2c adapter/bus initialization | ||
875 | */ | ||
876 | break; | ||
877 | case CX18_HW_GPIO_MUX: | ||
878 | if (cx18_gpio_register(cx, device) == 0) | ||
879 | cx->hw_flags |= device; | ||
880 | break; | ||
881 | default: | ||
882 | if (cx18_i2c_register(cx, i) == 0) | ||
883 | cx->hw_flags |= device; | ||
884 | break; | ||
885 | } | ||
886 | } | ||
887 | |||
888 | if (cx->hw_flags & CX18_HW_418_AV) | ||
889 | cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV); | ||
890 | |||
891 | if (cx->card->hw_muxer != 0) | ||
892 | cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer); | ||
893 | } | ||
894 | |||
895 | static int __devinit cx18_probe(struct pci_dev *pci_dev, | ||
896 | const struct pci_device_id *pci_id) | ||
897 | { | ||
898 | int retval = 0; | ||
899 | int i; | ||
900 | u32 devtype; | ||
901 | struct cx18 *cx; | ||
902 | |||
903 | /* FIXME - module parameter arrays constrain max instances */ | ||
904 | i = atomic_inc_return(&cx18_instance) - 1; | ||
905 | if (i >= CX18_MAX_CARDS) { | ||
906 | printk(KERN_ERR "cx18: cannot manage card %d, driver has a " | ||
907 | "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1); | ||
908 | return -ENOMEM; | ||
909 | } | ||
910 | |||
911 | cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC); | ||
912 | if (cx == NULL) { | ||
913 | printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n", | ||
914 | i); | ||
915 | return -ENOMEM; | ||
916 | } | ||
917 | cx->pci_dev = pci_dev; | ||
918 | cx->instance = i; | ||
919 | |||
920 | retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev); | ||
921 | if (retval) { | ||
922 | printk(KERN_ERR "cx18: v4l2_device_register of card %d failed" | ||
923 | "\n", cx->instance); | ||
924 | kfree(cx); | ||
925 | return retval; | ||
926 | } | ||
927 | snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d", | ||
928 | cx->instance); | ||
929 | CX18_INFO("Initializing card %d\n", cx->instance); | ||
930 | |||
931 | cx18_process_options(cx); | ||
932 | if (cx->options.cardtype == -1) { | ||
933 | retval = -ENODEV; | ||
934 | goto err; | ||
935 | } | ||
936 | |||
937 | retval = cx18_init_struct1(cx); | ||
938 | if (retval) | ||
939 | goto err; | ||
940 | |||
941 | CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); | ||
942 | |||
943 | /* PCI Device Setup */ | ||
944 | retval = cx18_setup_pci(cx, pci_dev, pci_id); | ||
945 | if (retval != 0) | ||
946 | goto free_workqueues; | ||
947 | |||
948 | /* map io memory */ | ||
949 | CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", | ||
950 | cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE); | ||
951 | cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET, | ||
952 | CX18_MEM_SIZE); | ||
953 | if (!cx->enc_mem) { | ||
954 | CX18_ERR("ioremap failed. Can't get a window into CX23418 " | ||
955 | "memory and register space\n"); | ||
956 | CX18_ERR("Each capture card with a CX23418 needs 64 MB of " | ||
957 | "vmalloc address space for the window\n"); | ||
958 | CX18_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n"); | ||
959 | CX18_ERR("Use the vmalloc= kernel command line option to set " | ||
960 | "VmallocTotal to a larger value\n"); | ||
961 | retval = -ENOMEM; | ||
962 | goto free_mem; | ||
963 | } | ||
964 | cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET; | ||
965 | devtype = cx18_read_reg(cx, 0xC72028); | ||
966 | switch (devtype & 0xff000000) { | ||
967 | case 0xff000000: | ||
968 | CX18_INFO("cx23418 revision %08x (A)\n", devtype); | ||
969 | break; | ||
970 | case 0x01000000: | ||
971 | CX18_INFO("cx23418 revision %08x (B)\n", devtype); | ||
972 | break; | ||
973 | default: | ||
974 | CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype); | ||
975 | break; | ||
976 | } | ||
977 | |||
978 | cx18_init_power(cx, 1); | ||
979 | cx18_init_memory(cx); | ||
980 | |||
981 | cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET); | ||
982 | cx18_init_scb(cx); | ||
983 | |||
984 | cx18_gpio_init(cx); | ||
985 | |||
986 | /* Initialize integrated A/V decoder early to set PLLs, just in case */ | ||
987 | retval = cx18_av_probe(cx); | ||
988 | if (retval) { | ||
989 | CX18_ERR("Could not register A/V decoder subdevice\n"); | ||
990 | goto free_map; | ||
991 | } | ||
992 | |||
993 | /* Initialize GPIO Reset Controller to do chip resets during i2c init */ | ||
994 | if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) { | ||
995 | if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0) | ||
996 | CX18_WARN("Could not register GPIO reset controller" | ||
997 | "subdevice; proceeding anyway.\n"); | ||
998 | else | ||
999 | cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL; | ||
1000 | } | ||
1001 | |||
1002 | /* active i2c */ | ||
1003 | CX18_DEBUG_INFO("activating i2c...\n"); | ||
1004 | retval = init_cx18_i2c(cx); | ||
1005 | if (retval) { | ||
1006 | CX18_ERR("Could not initialize i2c\n"); | ||
1007 | goto free_map; | ||
1008 | } | ||
1009 | |||
1010 | if (cx->card->hw_all & CX18_HW_TVEEPROM) { | ||
1011 | /* Based on the model number the cardtype may be changed. | ||
1012 | The PCI IDs are not always reliable. */ | ||
1013 | const struct cx18_card *orig_card = cx->card; | ||
1014 | cx18_process_eeprom(cx); | ||
1015 | |||
1016 | if (cx->card != orig_card) { | ||
1017 | /* Changed the cardtype; re-reset the I2C chips */ | ||
1018 | cx18_gpio_init(cx); | ||
1019 | cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, | ||
1020 | core, reset, (u32) CX18_GPIO_RESET_I2C); | ||
1021 | } | ||
1022 | } | ||
1023 | if (cx->card->comment) | ||
1024 | CX18_INFO("%s", cx->card->comment); | ||
1025 | if (cx->card->v4l2_capabilities == 0) { | ||
1026 | retval = -ENODEV; | ||
1027 | goto free_i2c; | ||
1028 | } | ||
1029 | cx18_init_memory(cx); | ||
1030 | cx18_init_scb(cx); | ||
1031 | |||
1032 | /* Register IRQ */ | ||
1033 | retval = request_irq(cx->pci_dev->irq, cx18_irq_handler, | ||
1034 | IRQF_SHARED | IRQF_DISABLED, | ||
1035 | cx->v4l2_dev.name, (void *)cx); | ||
1036 | if (retval) { | ||
1037 | CX18_ERR("Failed to register irq %d\n", retval); | ||
1038 | goto free_i2c; | ||
1039 | } | ||
1040 | |||
1041 | if (cx->std == 0) | ||
1042 | cx->std = V4L2_STD_NTSC_M; | ||
1043 | |||
1044 | if (cx->options.tuner == -1) { | ||
1045 | for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) { | ||
1046 | if ((cx->std & cx->card->tuners[i].std) == 0) | ||
1047 | continue; | ||
1048 | cx->options.tuner = cx->card->tuners[i].tuner; | ||
1049 | break; | ||
1050 | } | ||
1051 | } | ||
1052 | /* if no tuner was found, then pick the first tuner in the card list */ | ||
1053 | if (cx->options.tuner == -1 && cx->card->tuners[0].std) { | ||
1054 | cx->std = cx->card->tuners[0].std; | ||
1055 | if (cx->std & V4L2_STD_PAL) | ||
1056 | cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H; | ||
1057 | else if (cx->std & V4L2_STD_NTSC) | ||
1058 | cx->std = V4L2_STD_NTSC_M; | ||
1059 | else if (cx->std & V4L2_STD_SECAM) | ||
1060 | cx->std = V4L2_STD_SECAM_L; | ||
1061 | cx->options.tuner = cx->card->tuners[0].tuner; | ||
1062 | } | ||
1063 | if (cx->options.radio == -1) | ||
1064 | cx->options.radio = (cx->card->radio_input.audio_type != 0); | ||
1065 | |||
1066 | /* The card is now fully identified, continue with card-specific | ||
1067 | initialization. */ | ||
1068 | cx18_init_struct2(cx); | ||
1069 | |||
1070 | cx18_init_subdevs(cx); | ||
1071 | |||
1072 | if (cx->std & V4L2_STD_525_60) | ||
1073 | cx->is_60hz = 1; | ||
1074 | else | ||
1075 | cx->is_50hz = 1; | ||
1076 | |||
1077 | cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz); | ||
1078 | |||
1079 | if (cx->options.radio > 0) | ||
1080 | cx->v4l2_cap |= V4L2_CAP_RADIO; | ||
1081 | |||
1082 | if (cx->options.tuner > -1) { | ||
1083 | struct tuner_setup setup; | ||
1084 | |||
1085 | setup.addr = ADDR_UNSET; | ||
1086 | setup.type = cx->options.tuner; | ||
1087 | setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ | ||
1088 | setup.tuner_callback = (setup.type == TUNER_XC2028) ? | ||
1089 | cx18_reset_tuner_gpio : NULL; | ||
1090 | cx18_call_all(cx, tuner, s_type_addr, &setup); | ||
1091 | if (setup.type == TUNER_XC2028) { | ||
1092 | static struct xc2028_ctrl ctrl = { | ||
1093 | .fname = XC2028_DEFAULT_FIRMWARE, | ||
1094 | .max_len = 64, | ||
1095 | }; | ||
1096 | struct v4l2_priv_tun_config cfg = { | ||
1097 | .tuner = cx->options.tuner, | ||
1098 | .priv = &ctrl, | ||
1099 | }; | ||
1100 | cx18_call_all(cx, tuner, s_config, &cfg); | ||
1101 | } | ||
1102 | } | ||
1103 | |||
1104 | /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) | ||
1105 | are not. */ | ||
1106 | cx->tuner_std = cx->std; | ||
1107 | if (cx->std == V4L2_STD_ALL) | ||
1108 | cx->std = V4L2_STD_NTSC_M; | ||
1109 | |||
1110 | retval = cx18_streams_setup(cx); | ||
1111 | if (retval) { | ||
1112 | CX18_ERR("Error %d setting up streams\n", retval); | ||
1113 | goto free_irq; | ||
1114 | } | ||
1115 | retval = cx18_streams_register(cx); | ||
1116 | if (retval) { | ||
1117 | CX18_ERR("Error %d registering devices\n", retval); | ||
1118 | goto free_streams; | ||
1119 | } | ||
1120 | |||
1121 | CX18_INFO("Initialized card: %s\n", cx->card_name); | ||
1122 | |||
1123 | /* Load cx18 submodules (cx18-alsa) */ | ||
1124 | request_modules(cx); | ||
1125 | return 0; | ||
1126 | |||
1127 | free_streams: | ||
1128 | cx18_streams_cleanup(cx, 1); | ||
1129 | free_irq: | ||
1130 | free_irq(cx->pci_dev->irq, (void *)cx); | ||
1131 | free_i2c: | ||
1132 | exit_cx18_i2c(cx); | ||
1133 | free_map: | ||
1134 | cx18_iounmap(cx); | ||
1135 | free_mem: | ||
1136 | release_mem_region(cx->base_addr, CX18_MEM_SIZE); | ||
1137 | free_workqueues: | ||
1138 | destroy_workqueue(cx->in_work_queue); | ||
1139 | err: | ||
1140 | if (retval == 0) | ||
1141 | retval = -ENODEV; | ||
1142 | CX18_ERR("Error %d on initialization\n", retval); | ||
1143 | |||
1144 | v4l2_device_unregister(&cx->v4l2_dev); | ||
1145 | kfree(cx); | ||
1146 | return retval; | ||
1147 | } | ||
1148 | |||
1149 | int cx18_init_on_first_open(struct cx18 *cx) | ||
1150 | { | ||
1151 | int video_input; | ||
1152 | int fw_retry_count = 3; | ||
1153 | struct v4l2_frequency vf; | ||
1154 | struct cx18_open_id fh; | ||
1155 | v4l2_std_id std; | ||
1156 | |||
1157 | fh.cx = cx; | ||
1158 | |||
1159 | if (test_bit(CX18_F_I_FAILED, &cx->i_flags)) | ||
1160 | return -ENXIO; | ||
1161 | |||
1162 | if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags)) | ||
1163 | return 0; | ||
1164 | |||
1165 | while (--fw_retry_count > 0) { | ||
1166 | /* load firmware */ | ||
1167 | if (cx18_firmware_init(cx) == 0) | ||
1168 | break; | ||
1169 | if (fw_retry_count > 1) | ||
1170 | CX18_WARN("Retry loading firmware\n"); | ||
1171 | } | ||
1172 | |||
1173 | if (fw_retry_count == 0) { | ||
1174 | set_bit(CX18_F_I_FAILED, &cx->i_flags); | ||
1175 | return -ENXIO; | ||
1176 | } | ||
1177 | set_bit(CX18_F_I_LOADED_FW, &cx->i_flags); | ||
1178 | |||
1179 | /* | ||
1180 | * Init the firmware twice to work around a silicon bug | ||
1181 | * with the digital TS. | ||
1182 | * | ||
1183 | * The second firmware load requires us to normalize the APU state, | ||
1184 | * or the audio for the first analog capture will be badly incorrect. | ||
1185 | * | ||
1186 | * I can't seem to call APU_RESETAI and have it succeed without the | ||
1187 | * APU capturing audio, so we start and stop it here to do the reset | ||
1188 | */ | ||
1189 | |||
1190 | /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */ | ||
1191 | cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); | ||
1192 | cx18_vapi(cx, CX18_APU_RESETAI, 0); | ||
1193 | cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); | ||
1194 | |||
1195 | fw_retry_count = 3; | ||
1196 | while (--fw_retry_count > 0) { | ||
1197 | /* load firmware */ | ||
1198 | if (cx18_firmware_init(cx) == 0) | ||
1199 | break; | ||
1200 | if (fw_retry_count > 1) | ||
1201 | CX18_WARN("Retry loading firmware\n"); | ||
1202 | } | ||
1203 | |||
1204 | if (fw_retry_count == 0) { | ||
1205 | set_bit(CX18_F_I_FAILED, &cx->i_flags); | ||
1206 | return -ENXIO; | ||
1207 | } | ||
1208 | |||
1209 | /* | ||
1210 | * The second firmware load requires us to normalize the APU state, | ||
1211 | * or the audio for the first analog capture will be badly incorrect. | ||
1212 | * | ||
1213 | * I can't seem to call APU_RESETAI and have it succeed without the | ||
1214 | * APU capturing audio, so we start and stop it here to do the reset | ||
1215 | */ | ||
1216 | |||
1217 | /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */ | ||
1218 | cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0); | ||
1219 | cx18_vapi(cx, CX18_APU_RESETAI, 0); | ||
1220 | cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); | ||
1221 | |||
1222 | /* Init the A/V decoder, if it hasn't been already */ | ||
1223 | v4l2_subdev_call(cx->sd_av, core, load_fw); | ||
1224 | |||
1225 | vf.tuner = 0; | ||
1226 | vf.type = V4L2_TUNER_ANALOG_TV; | ||
1227 | vf.frequency = 6400; /* the tuner 'baseline' frequency */ | ||
1228 | |||
1229 | /* Set initial frequency. For PAL/SECAM broadcasts no | ||
1230 | 'default' channel exists AFAIK. */ | ||
1231 | if (cx->std == V4L2_STD_NTSC_M_JP) | ||
1232 | vf.frequency = 1460; /* ch. 1 91250*16/1000 */ | ||
1233 | else if (cx->std & V4L2_STD_NTSC_M) | ||
1234 | vf.frequency = 1076; /* ch. 4 67250*16/1000 */ | ||
1235 | |||
1236 | video_input = cx->active_input; | ||
1237 | cx->active_input++; /* Force update of input */ | ||
1238 | cx18_s_input(NULL, &fh, video_input); | ||
1239 | |||
1240 | /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code | ||
1241 | in one place. */ | ||
1242 | cx->std++; /* Force full standard initialization */ | ||
1243 | std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std; | ||
1244 | cx18_s_std(NULL, &fh, &std); | ||
1245 | cx18_s_frequency(NULL, &fh, &vf); | ||
1246 | return 0; | ||
1247 | } | ||
1248 | |||
1249 | static void cx18_cancel_in_work_orders(struct cx18 *cx) | ||
1250 | { | ||
1251 | int i; | ||
1252 | for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) | ||
1253 | cancel_work_sync(&cx->in_work_order[i].work); | ||
1254 | } | ||
1255 | |||
1256 | static void cx18_cancel_out_work_orders(struct cx18 *cx) | ||
1257 | { | ||
1258 | int i; | ||
1259 | for (i = 0; i < CX18_MAX_STREAMS; i++) | ||
1260 | if (&cx->streams[i].video_dev != NULL) | ||
1261 | cancel_work_sync(&cx->streams[i].out_work_order); | ||
1262 | } | ||
1263 | |||
1264 | static void cx18_remove(struct pci_dev *pci_dev) | ||
1265 | { | ||
1266 | struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); | ||
1267 | struct cx18 *cx = to_cx18(v4l2_dev); | ||
1268 | int i; | ||
1269 | |||
1270 | CX18_DEBUG_INFO("Removing Card\n"); | ||
1271 | |||
1272 | flush_request_modules(cx); | ||
1273 | |||
1274 | /* Stop all captures */ | ||
1275 | CX18_DEBUG_INFO("Stopping all streams\n"); | ||
1276 | if (atomic_read(&cx->tot_capturing) > 0) | ||
1277 | cx18_stop_all_captures(cx); | ||
1278 | |||
1279 | /* Stop interrupts that cause incoming work to be queued */ | ||
1280 | cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); | ||
1281 | |||
1282 | /* Incoming work can cause outgoing work, so clean up incoming first */ | ||
1283 | cx18_cancel_in_work_orders(cx); | ||
1284 | cx18_cancel_out_work_orders(cx); | ||
1285 | |||
1286 | /* Stop ack interrupts that may have been needed for work to finish */ | ||
1287 | cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | ||
1288 | |||
1289 | cx18_halt_firmware(cx); | ||
1290 | |||
1291 | destroy_workqueue(cx->in_work_queue); | ||
1292 | |||
1293 | cx18_streams_cleanup(cx, 1); | ||
1294 | |||
1295 | exit_cx18_i2c(cx); | ||
1296 | |||
1297 | free_irq(cx->pci_dev->irq, (void *)cx); | ||
1298 | |||
1299 | cx18_iounmap(cx); | ||
1300 | |||
1301 | release_mem_region(cx->base_addr, CX18_MEM_SIZE); | ||
1302 | |||
1303 | pci_disable_device(cx->pci_dev); | ||
1304 | |||
1305 | if (cx->vbi.sliced_mpeg_data[0] != NULL) | ||
1306 | for (i = 0; i < CX18_VBI_FRAMES; i++) | ||
1307 | kfree(cx->vbi.sliced_mpeg_data[i]); | ||
1308 | |||
1309 | v4l2_ctrl_handler_free(&cx->av_state.hdl); | ||
1310 | |||
1311 | CX18_INFO("Removed %s\n", cx->card_name); | ||
1312 | |||
1313 | v4l2_device_unregister(v4l2_dev); | ||
1314 | kfree(cx); | ||
1315 | } | ||
1316 | |||
1317 | |||
1318 | /* define a pci_driver for card detection */ | ||
1319 | static struct pci_driver cx18_pci_driver = { | ||
1320 | .name = "cx18", | ||
1321 | .id_table = cx18_pci_tbl, | ||
1322 | .probe = cx18_probe, | ||
1323 | .remove = cx18_remove, | ||
1324 | }; | ||
1325 | |||
1326 | static int __init module_start(void) | ||
1327 | { | ||
1328 | printk(KERN_INFO "cx18: Start initialization, version %s\n", | ||
1329 | CX18_VERSION); | ||
1330 | |||
1331 | /* Validate parameters */ | ||
1332 | if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) { | ||
1333 | printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n", | ||
1334 | CX18_MAX_CARDS - 1); | ||
1335 | return -1; | ||
1336 | } | ||
1337 | |||
1338 | if (cx18_debug < 0 || cx18_debug > 511) { | ||
1339 | cx18_debug = 0; | ||
1340 | printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n"); | ||
1341 | } | ||
1342 | |||
1343 | if (pci_register_driver(&cx18_pci_driver)) { | ||
1344 | printk(KERN_ERR "cx18: Error detecting PCI card\n"); | ||
1345 | return -ENODEV; | ||
1346 | } | ||
1347 | printk(KERN_INFO "cx18: End initialization\n"); | ||
1348 | return 0; | ||
1349 | } | ||
1350 | |||
1351 | static void __exit module_cleanup(void) | ||
1352 | { | ||
1353 | pci_unregister_driver(&cx18_pci_driver); | ||
1354 | } | ||
1355 | |||
1356 | module_init(module_start); | ||
1357 | module_exit(module_cleanup); | ||
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h new file mode 100644 index 00000000000..18342072306 --- /dev/null +++ b/drivers/media/video/cx18/cx18-driver.h | |||
@@ -0,0 +1,735 @@ | |||
1 | /* | ||
2 | * cx18 driver internal defines and structures | ||
3 | * | ||
4 | * Derived from ivtv-driver.h | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #ifndef CX18_DRIVER_H | ||
26 | #define CX18_DRIVER_H | ||
27 | |||
28 | #include <linux/module.h> | ||
29 | #include <linux/moduleparam.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/delay.h> | ||
32 | #include <linux/sched.h> | ||
33 | #include <linux/fs.h> | ||
34 | #include <linux/pci.h> | ||
35 | #include <linux/interrupt.h> | ||
36 | #include <linux/spinlock.h> | ||
37 | #include <linux/i2c.h> | ||
38 | #include <linux/i2c-algo-bit.h> | ||
39 | #include <linux/list.h> | ||
40 | #include <linux/unistd.h> | ||
41 | #include <linux/pagemap.h> | ||
42 | #include <linux/workqueue.h> | ||
43 | #include <linux/mutex.h> | ||
44 | #include <linux/slab.h> | ||
45 | #include <asm/byteorder.h> | ||
46 | |||
47 | #include <linux/dvb/video.h> | ||
48 | #include <linux/dvb/audio.h> | ||
49 | #include <media/v4l2-common.h> | ||
50 | #include <media/v4l2-ioctl.h> | ||
51 | #include <media/v4l2-device.h> | ||
52 | #include <media/v4l2-fh.h> | ||
53 | #include <media/tuner.h> | ||
54 | #include <media/ir-kbd-i2c.h> | ||
55 | #include "cx18-mailbox.h" | ||
56 | #include "cx18-av-core.h" | ||
57 | #include "cx23418.h" | ||
58 | |||
59 | /* DVB */ | ||
60 | #include "demux.h" | ||
61 | #include "dmxdev.h" | ||
62 | #include "dvb_demux.h" | ||
63 | #include "dvb_frontend.h" | ||
64 | #include "dvb_net.h" | ||
65 | #include "dvbdev.h" | ||
66 | |||
67 | /* Videobuf / YUV support */ | ||
68 | #include <media/videobuf-core.h> | ||
69 | #include <media/videobuf-vmalloc.h> | ||
70 | |||
71 | #ifndef CONFIG_PCI | ||
72 | # error "This driver requires kernel PCI support." | ||
73 | #endif | ||
74 | |||
75 | #define CX18_MEM_OFFSET 0x00000000 | ||
76 | #define CX18_MEM_SIZE 0x04000000 | ||
77 | #define CX18_REG_OFFSET 0x02000000 | ||
78 | |||
79 | /* Maximum cx18 driver instances. */ | ||
80 | #define CX18_MAX_CARDS 32 | ||
81 | |||
82 | /* Supported cards */ | ||
83 | #define CX18_CARD_HVR_1600_ESMT 0 /* Hauppauge HVR 1600 (ESMT memory) */ | ||
84 | #define CX18_CARD_HVR_1600_SAMSUNG 1 /* Hauppauge HVR 1600 (Samsung memory) */ | ||
85 | #define CX18_CARD_COMPRO_H900 2 /* Compro VideoMate H900 */ | ||
86 | #define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */ | ||
87 | #define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */ | ||
88 | #define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/ | ||
89 | #define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100 */ | ||
90 | #define CX18_CARD_LEADTEK_DVR3100H 7 /* Leadtek WinFast DVR3100 H */ | ||
91 | #define CX18_CARD_GOTVIEW_PCI_DVD3 8 /* GoTView PCI DVD3 Hybrid */ | ||
92 | #define CX18_CARD_HVR_1600_S5H1411 9 /* Hauppauge HVR 1600 s5h1411/tda18271*/ | ||
93 | #define CX18_CARD_LAST 9 | ||
94 | |||
95 | #define CX18_ENC_STREAM_TYPE_MPG 0 | ||
96 | #define CX18_ENC_STREAM_TYPE_TS 1 | ||
97 | #define CX18_ENC_STREAM_TYPE_YUV 2 | ||
98 | #define CX18_ENC_STREAM_TYPE_VBI 3 | ||
99 | #define CX18_ENC_STREAM_TYPE_PCM 4 | ||
100 | #define CX18_ENC_STREAM_TYPE_IDX 5 | ||
101 | #define CX18_ENC_STREAM_TYPE_RAD 6 | ||
102 | #define CX18_MAX_STREAMS 7 | ||
103 | |||
104 | /* system vendor and device IDs */ | ||
105 | #define PCI_VENDOR_ID_CX 0x14f1 | ||
106 | #define PCI_DEVICE_ID_CX23418 0x5b7a | ||
107 | |||
108 | /* subsystem vendor ID */ | ||
109 | #define CX18_PCI_ID_HAUPPAUGE 0x0070 | ||
110 | #define CX18_PCI_ID_COMPRO 0x185b | ||
111 | #define CX18_PCI_ID_YUAN 0x12ab | ||
112 | #define CX18_PCI_ID_CONEXANT 0x14f1 | ||
113 | #define CX18_PCI_ID_TOSHIBA 0x1179 | ||
114 | #define CX18_PCI_ID_LEADTEK 0x107D | ||
115 | #define CX18_PCI_ID_GOTVIEW 0x5854 | ||
116 | |||
117 | /* ======================================================================== */ | ||
118 | /* ========================== START USER SETTABLE DMA VARIABLES =========== */ | ||
119 | /* ======================================================================== */ | ||
120 | |||
121 | /* DMA Buffers, Default size in MB allocated */ | ||
122 | #define CX18_DEFAULT_ENC_TS_BUFFERS 1 | ||
123 | #define CX18_DEFAULT_ENC_MPG_BUFFERS 2 | ||
124 | #define CX18_DEFAULT_ENC_IDX_BUFFERS 1 | ||
125 | #define CX18_DEFAULT_ENC_YUV_BUFFERS 2 | ||
126 | #define CX18_DEFAULT_ENC_VBI_BUFFERS 1 | ||
127 | #define CX18_DEFAULT_ENC_PCM_BUFFERS 1 | ||
128 | |||
129 | /* Maximum firmware DMA buffers per stream */ | ||
130 | #define CX18_MAX_FW_MDLS_PER_STREAM 63 | ||
131 | |||
132 | /* YUV buffer sizes in bytes to ensure integer # of frames per buffer */ | ||
133 | #define CX18_UNIT_ENC_YUV_BUFSIZE (720 * 32 * 3 / 2) /* bytes */ | ||
134 | #define CX18_625_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 576/32) | ||
135 | #define CX18_525_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 480/32) | ||
136 | |||
137 | /* IDX buffer size should be a multiple of the index entry size from the chip */ | ||
138 | struct cx18_enc_idx_entry { | ||
139 | __le32 length; | ||
140 | __le32 offset_low; | ||
141 | __le32 offset_high; | ||
142 | __le32 flags; | ||
143 | __le32 pts_low; | ||
144 | __le32 pts_high; | ||
145 | } __attribute__ ((packed)); | ||
146 | #define CX18_UNIT_ENC_IDX_BUFSIZE \ | ||
147 | (sizeof(struct cx18_enc_idx_entry) * V4L2_ENC_IDX_ENTRIES) | ||
148 | |||
149 | /* DMA buffer, default size in kB allocated */ | ||
150 | #define CX18_DEFAULT_ENC_TS_BUFSIZE 32 | ||
151 | #define CX18_DEFAULT_ENC_MPG_BUFSIZE 32 | ||
152 | #define CX18_DEFAULT_ENC_IDX_BUFSIZE (CX18_UNIT_ENC_IDX_BUFSIZE * 1 / 1024 + 1) | ||
153 | #define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1) | ||
154 | #define CX18_DEFAULT_ENC_PCM_BUFSIZE 4 | ||
155 | |||
156 | /* i2c stuff */ | ||
157 | #define I2C_CLIENTS_MAX 16 | ||
158 | |||
159 | /* debugging */ | ||
160 | |||
161 | /* Flag to turn on high volume debugging */ | ||
162 | #define CX18_DBGFLG_WARN (1 << 0) | ||
163 | #define CX18_DBGFLG_INFO (1 << 1) | ||
164 | #define CX18_DBGFLG_API (1 << 2) | ||
165 | #define CX18_DBGFLG_DMA (1 << 3) | ||
166 | #define CX18_DBGFLG_IOCTL (1 << 4) | ||
167 | #define CX18_DBGFLG_FILE (1 << 5) | ||
168 | #define CX18_DBGFLG_I2C (1 << 6) | ||
169 | #define CX18_DBGFLG_IRQ (1 << 7) | ||
170 | /* Flag to turn on high volume debugging */ | ||
171 | #define CX18_DBGFLG_HIGHVOL (1 << 8) | ||
172 | |||
173 | /* NOTE: extra space before comma in 'fmt , ## args' is required for | ||
174 | gcc-2.95, otherwise it won't compile. */ | ||
175 | #define CX18_DEBUG(x, type, fmt, args...) \ | ||
176 | do { \ | ||
177 | if ((x) & cx18_debug) \ | ||
178 | v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \ | ||
179 | } while (0) | ||
180 | #define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args) | ||
181 | #define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args) | ||
182 | #define CX18_DEBUG_API(fmt, args...) CX18_DEBUG(CX18_DBGFLG_API, "api", fmt , ## args) | ||
183 | #define CX18_DEBUG_DMA(fmt, args...) CX18_DEBUG(CX18_DBGFLG_DMA, "dma", fmt , ## args) | ||
184 | #define CX18_DEBUG_IOCTL(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args) | ||
185 | #define CX18_DEBUG_FILE(fmt, args...) CX18_DEBUG(CX18_DBGFLG_FILE, "file", fmt , ## args) | ||
186 | #define CX18_DEBUG_I2C(fmt, args...) CX18_DEBUG(CX18_DBGFLG_I2C, "i2c", fmt , ## args) | ||
187 | #define CX18_DEBUG_IRQ(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IRQ, "irq", fmt , ## args) | ||
188 | |||
189 | #define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \ | ||
190 | do { \ | ||
191 | if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \ | ||
192 | v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \ | ||
193 | } while (0) | ||
194 | #define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args) | ||
195 | #define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args) | ||
196 | #define CX18_DEBUG_HI_API(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_API, "api", fmt , ## args) | ||
197 | #define CX18_DEBUG_HI_DMA(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_DMA, "dma", fmt , ## args) | ||
198 | #define CX18_DEBUG_HI_IOCTL(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args) | ||
199 | #define CX18_DEBUG_HI_FILE(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_FILE, "file", fmt , ## args) | ||
200 | #define CX18_DEBUG_HI_I2C(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_I2C, "i2c", fmt , ## args) | ||
201 | #define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args) | ||
202 | |||
203 | /* Standard kernel messages */ | ||
204 | #define CX18_ERR(fmt, args...) v4l2_err(&cx->v4l2_dev, fmt , ## args) | ||
205 | #define CX18_WARN(fmt, args...) v4l2_warn(&cx->v4l2_dev, fmt , ## args) | ||
206 | #define CX18_INFO(fmt, args...) v4l2_info(&cx->v4l2_dev, fmt , ## args) | ||
207 | |||
208 | /* Messages for internal subdevs to use */ | ||
209 | #define CX18_DEBUG_DEV(x, dev, type, fmt, args...) \ | ||
210 | do { \ | ||
211 | if ((x) & cx18_debug) \ | ||
212 | v4l2_info(dev, " " type ": " fmt , ## args); \ | ||
213 | } while (0) | ||
214 | #define CX18_DEBUG_WARN_DEV(dev, fmt, args...) \ | ||
215 | CX18_DEBUG_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args) | ||
216 | #define CX18_DEBUG_INFO_DEV(dev, fmt, args...) \ | ||
217 | CX18_DEBUG_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args) | ||
218 | #define CX18_DEBUG_API_DEV(dev, fmt, args...) \ | ||
219 | CX18_DEBUG_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args) | ||
220 | #define CX18_DEBUG_DMA_DEV(dev, fmt, args...) \ | ||
221 | CX18_DEBUG_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args) | ||
222 | #define CX18_DEBUG_IOCTL_DEV(dev, fmt, args...) \ | ||
223 | CX18_DEBUG_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args) | ||
224 | #define CX18_DEBUG_FILE_DEV(dev, fmt, args...) \ | ||
225 | CX18_DEBUG_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args) | ||
226 | #define CX18_DEBUG_I2C_DEV(dev, fmt, args...) \ | ||
227 | CX18_DEBUG_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args) | ||
228 | #define CX18_DEBUG_IRQ_DEV(dev, fmt, args...) \ | ||
229 | CX18_DEBUG_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args) | ||
230 | |||
231 | #define CX18_DEBUG_HIGH_VOL_DEV(x, dev, type, fmt, args...) \ | ||
232 | do { \ | ||
233 | if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \ | ||
234 | v4l2_info(dev, " " type ": " fmt , ## args); \ | ||
235 | } while (0) | ||
236 | #define CX18_DEBUG_HI_WARN_DEV(dev, fmt, args...) \ | ||
237 | CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args) | ||
238 | #define CX18_DEBUG_HI_INFO_DEV(dev, fmt, args...) \ | ||
239 | CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args) | ||
240 | #define CX18_DEBUG_HI_API_DEV(dev, fmt, args...) \ | ||
241 | CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args) | ||
242 | #define CX18_DEBUG_HI_DMA_DEV(dev, fmt, args...) \ | ||
243 | CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args) | ||
244 | #define CX18_DEBUG_HI_IOCTL_DEV(dev, fmt, args...) \ | ||
245 | CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args) | ||
246 | #define CX18_DEBUG_HI_FILE_DEV(dev, fmt, args...) \ | ||
247 | CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args) | ||
248 | #define CX18_DEBUG_HI_I2C_DEV(dev, fmt, args...) \ | ||
249 | CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args) | ||
250 | #define CX18_DEBUG_HI_IRQ_DEV(dev, fmt, args...) \ | ||
251 | CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args) | ||
252 | |||
253 | #define CX18_ERR_DEV(dev, fmt, args...) v4l2_err(dev, fmt , ## args) | ||
254 | #define CX18_WARN_DEV(dev, fmt, args...) v4l2_warn(dev, fmt , ## args) | ||
255 | #define CX18_INFO_DEV(dev, fmt, args...) v4l2_info(dev, fmt , ## args) | ||
256 | |||
257 | extern int cx18_debug; | ||
258 | |||
259 | struct cx18_options { | ||
260 | int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */ | ||
261 | int cardtype; /* force card type on load */ | ||
262 | int tuner; /* set tuner on load */ | ||
263 | int radio; /* enable/disable radio */ | ||
264 | }; | ||
265 | |||
266 | /* per-mdl bit flags */ | ||
267 | #define CX18_F_M_NEED_SWAP 0 /* mdl buffer data must be endianess swapped */ | ||
268 | |||
269 | /* per-stream, s_flags */ | ||
270 | #define CX18_F_S_CLAIMED 3 /* this stream is claimed */ | ||
271 | #define CX18_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */ | ||
272 | #define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ | ||
273 | #define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */ | ||
274 | #define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */ | ||
275 | #define CX18_F_S_STOPPING 9 /* telling the fw to stop capturing */ | ||
276 | |||
277 | /* per-cx18, i_flags */ | ||
278 | #define CX18_F_I_LOADED_FW 0 /* Loaded firmware 1st time */ | ||
279 | #define CX18_F_I_EOS 4 /* End of encoder stream */ | ||
280 | #define CX18_F_I_RADIO_USER 5 /* radio tuner is selected */ | ||
281 | #define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */ | ||
282 | #define CX18_F_I_INITED 21 /* set after first open */ | ||
283 | #define CX18_F_I_FAILED 22 /* set if first open failed */ | ||
284 | |||
285 | /* These are the VBI types as they appear in the embedded VBI private packets. */ | ||
286 | #define CX18_SLICED_TYPE_TELETEXT_B (1) | ||
287 | #define CX18_SLICED_TYPE_CAPTION_525 (4) | ||
288 | #define CX18_SLICED_TYPE_WSS_625 (5) | ||
289 | #define CX18_SLICED_TYPE_VPS (7) | ||
290 | |||
291 | /** | ||
292 | * list_entry_is_past_end - check if a previous loop cursor is off list end | ||
293 | * @pos: the type * previously used as a loop cursor. | ||
294 | * @head: the head for your list. | ||
295 | * @member: the name of the list_struct within the struct. | ||
296 | * | ||
297 | * Check if the entry's list_head is the head of the list, thus it's not a | ||
298 | * real entry but was the loop cursor that walked past the end | ||
299 | */ | ||
300 | #define list_entry_is_past_end(pos, head, member) \ | ||
301 | (&pos->member == (head)) | ||
302 | |||
303 | struct cx18_buffer { | ||
304 | struct list_head list; | ||
305 | dma_addr_t dma_handle; | ||
306 | char *buf; | ||
307 | |||
308 | u32 bytesused; | ||
309 | u32 readpos; | ||
310 | }; | ||
311 | |||
312 | struct cx18_mdl { | ||
313 | struct list_head list; | ||
314 | u32 id; /* index into cx->scb->cpu_mdl[] of 1st cx18_mdl_ent */ | ||
315 | |||
316 | unsigned int skipped; | ||
317 | unsigned long m_flags; | ||
318 | |||
319 | struct list_head buf_list; | ||
320 | struct cx18_buffer *curr_buf; /* current buffer in list for reading */ | ||
321 | |||
322 | u32 bytesused; | ||
323 | u32 readpos; | ||
324 | }; | ||
325 | |||
326 | struct cx18_queue { | ||
327 | struct list_head list; | ||
328 | atomic_t depth; | ||
329 | u32 bytesused; | ||
330 | spinlock_t lock; | ||
331 | }; | ||
332 | |||
333 | struct cx18_stream; /* forward reference */ | ||
334 | |||
335 | struct cx18_dvb { | ||
336 | struct cx18_stream *stream; | ||
337 | struct dmx_frontend hw_frontend; | ||
338 | struct dmx_frontend mem_frontend; | ||
339 | struct dmxdev dmxdev; | ||
340 | struct dvb_adapter dvb_adapter; | ||
341 | struct dvb_demux demux; | ||
342 | struct dvb_frontend *fe; | ||
343 | struct dvb_net dvbnet; | ||
344 | int enabled; | ||
345 | int feeding; | ||
346 | struct mutex feedlock; | ||
347 | }; | ||
348 | |||
349 | struct cx18; /* forward reference */ | ||
350 | struct cx18_scb; /* forward reference */ | ||
351 | |||
352 | |||
353 | #define CX18_MAX_MDL_ACKS 2 | ||
354 | #define CX18_MAX_IN_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7) | ||
355 | /* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */ | ||
356 | |||
357 | #define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1 | ||
358 | #define CX18_F_EWO_MB_STALE_WHILE_PROC 0x2 | ||
359 | #define CX18_F_EWO_MB_STALE \ | ||
360 | (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC) | ||
361 | |||
362 | struct cx18_in_work_order { | ||
363 | struct work_struct work; | ||
364 | atomic_t pending; | ||
365 | struct cx18 *cx; | ||
366 | unsigned long flags; | ||
367 | int rpu; | ||
368 | struct cx18_mailbox mb; | ||
369 | struct cx18_mdl_ack mdl_ack[CX18_MAX_MDL_ACKS]; | ||
370 | char *str; | ||
371 | }; | ||
372 | |||
373 | #define CX18_INVALID_TASK_HANDLE 0xffffffff | ||
374 | |||
375 | struct cx18_stream { | ||
376 | /* These first five fields are always set, even if the stream | ||
377 | is not actually created. */ | ||
378 | struct video_device *video_dev; /* NULL when stream not created */ | ||
379 | struct cx18_dvb *dvb; /* DVB / Digital Transport */ | ||
380 | struct cx18 *cx; /* for ease of use */ | ||
381 | const char *name; /* name of the stream */ | ||
382 | int type; /* stream type */ | ||
383 | u32 handle; /* task handle */ | ||
384 | unsigned int mdl_base_idx; | ||
385 | |||
386 | u32 id; | ||
387 | unsigned long s_flags; /* status flags, see above */ | ||
388 | int dma; /* can be PCI_DMA_TODEVICE, | ||
389 | PCI_DMA_FROMDEVICE or | ||
390 | PCI_DMA_NONE */ | ||
391 | wait_queue_head_t waitq; | ||
392 | |||
393 | /* Buffers */ | ||
394 | struct list_head buf_pool; /* buffers not attached to an MDL */ | ||
395 | u32 buffers; /* total buffers owned by this stream */ | ||
396 | u32 buf_size; /* size in bytes of a single buffer */ | ||
397 | |||
398 | /* MDL sizes - all stream MDLs are the same size */ | ||
399 | u32 bufs_per_mdl; | ||
400 | u32 mdl_size; /* total bytes in all buffers in a mdl */ | ||
401 | |||
402 | /* MDL Queues */ | ||
403 | struct cx18_queue q_free; /* free - in rotation, not committed */ | ||
404 | struct cx18_queue q_busy; /* busy - in use by firmware */ | ||
405 | struct cx18_queue q_full; /* full - data for user apps */ | ||
406 | struct cx18_queue q_idle; /* idle - not in rotation */ | ||
407 | |||
408 | struct work_struct out_work_order; | ||
409 | |||
410 | /* Videobuf for YUV video */ | ||
411 | u32 pixelformat; | ||
412 | struct list_head vb_capture; /* video capture queue */ | ||
413 | spinlock_t vb_lock; | ||
414 | struct timer_list vb_timeout; | ||
415 | |||
416 | struct videobuf_queue vbuf_q; | ||
417 | spinlock_t vbuf_q_lock; /* Protect vbuf_q */ | ||
418 | enum v4l2_buf_type vb_type; | ||
419 | }; | ||
420 | |||
421 | struct cx18_videobuf_buffer { | ||
422 | /* Common video buffer sub-system struct */ | ||
423 | struct videobuf_buffer vb; | ||
424 | v4l2_std_id tvnorm; /* selected tv norm */ | ||
425 | u32 bytes_used; | ||
426 | }; | ||
427 | |||
428 | struct cx18_open_id { | ||
429 | struct v4l2_fh fh; | ||
430 | u32 open_id; | ||
431 | int type; | ||
432 | struct cx18 *cx; | ||
433 | |||
434 | struct videobuf_queue vbuf_q; | ||
435 | spinlock_t s_lock; /* Protect vbuf_q */ | ||
436 | enum v4l2_buf_type vb_type; | ||
437 | }; | ||
438 | |||
439 | static inline struct cx18_open_id *fh2id(struct v4l2_fh *fh) | ||
440 | { | ||
441 | return container_of(fh, struct cx18_open_id, fh); | ||
442 | } | ||
443 | |||
444 | static inline struct cx18_open_id *file2id(struct file *file) | ||
445 | { | ||
446 | return fh2id(file->private_data); | ||
447 | } | ||
448 | |||
449 | /* forward declaration of struct defined in cx18-cards.h */ | ||
450 | struct cx18_card; | ||
451 | |||
452 | /* | ||
453 | * A note about "sliced" VBI data as implemented in this driver: | ||
454 | * | ||
455 | * Currently we collect the sliced VBI in the form of Ancillary Data | ||
456 | * packets, inserted by the AV core decoder/digitizer/slicer in the | ||
457 | * horizontal blanking region of the VBI lines, in "raw" mode as far as | ||
458 | * the Encoder is concerned. We don't ever tell the Encoder itself | ||
459 | * to provide sliced VBI. (AV Core: sliced mode - Encoder: raw mode) | ||
460 | * | ||
461 | * We then process the ancillary data ourselves to send the sliced data | ||
462 | * to the user application directly or build up MPEG-2 private stream 1 | ||
463 | * packets to splice into (only!) MPEG-2 PS streams for the user app. | ||
464 | * | ||
465 | * (That's how ivtv essentially does it.) | ||
466 | * | ||
467 | * The Encoder should be able to extract certain sliced VBI data for | ||
468 | * us and provide it in a separate stream or splice it into any type of | ||
469 | * MPEG PS or TS stream, but this isn't implemented yet. | ||
470 | */ | ||
471 | |||
472 | /* | ||
473 | * Number of "raw" VBI samples per horizontal line we tell the Encoder to | ||
474 | * grab from the decoder/digitizer/slicer output for raw or sliced VBI. | ||
475 | * It depends on the pixel clock and the horiz rate: | ||
476 | * | ||
477 | * (1/Fh)*(2*Fp) = Samples/line | ||
478 | * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples | ||
479 | * | ||
480 | * Sliced VBI data is sent as ancillary data during horizontal blanking | ||
481 | * Raw VBI is sent as active video samples during vertcal blanking | ||
482 | * | ||
483 | * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line | ||
484 | * length of 720 pixels @ 4:2:2 sampling. Thus... | ||
485 | * | ||
486 | * For systems that use a 15.734 kHz horizontal rate, such as | ||
487 | * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have: | ||
488 | * | ||
489 | * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line = | ||
490 | * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples | ||
491 | * | ||
492 | * For systems that use a 15.625 kHz horizontal rate, such as | ||
493 | * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have: | ||
494 | * | ||
495 | * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line = | ||
496 | * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples | ||
497 | */ | ||
498 | static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */ | ||
499 | static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */ | ||
500 | static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */ | ||
501 | |||
502 | #define CX18_VBI_FRAMES 32 | ||
503 | |||
504 | struct vbi_info { | ||
505 | /* Current state of v4l2 VBI settings for this device */ | ||
506 | struct v4l2_format in; | ||
507 | struct v4l2_sliced_vbi_format *sliced_in; /* pointer to in.fmt.sliced */ | ||
508 | u32 count; /* Count of VBI data lines: 60 Hz: 12 or 50 Hz: 18 */ | ||
509 | u32 start[2]; /* First VBI data line per field: 10 & 273 or 6 & 318 */ | ||
510 | |||
511 | u32 frame; /* Count of VBI buffers/frames received from Encoder */ | ||
512 | |||
513 | /* | ||
514 | * Vars for creation and insertion of MPEG Private Stream 1 packets | ||
515 | * of sliced VBI data into an MPEG PS | ||
516 | */ | ||
517 | |||
518 | /* Boolean: create and insert Private Stream 1 packets into the PS */ | ||
519 | int insert_mpeg; | ||
520 | |||
521 | /* | ||
522 | * Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. | ||
523 | * Used in cx18-vbi.c only for collecting sliced data, and as a source | ||
524 | * during conversion of sliced VBI data into MPEG Priv Stream 1 packets. | ||
525 | * We don't need to save state here, but the array may have been a bit | ||
526 | * too big (2304 bytes) to alloc from the stack. | ||
527 | */ | ||
528 | struct v4l2_sliced_vbi_data sliced_data[36]; | ||
529 | |||
530 | /* | ||
531 | * A ring buffer of driver-generated MPEG-2 PS | ||
532 | * Program Pack/Private Stream 1 packets for sliced VBI data insertion | ||
533 | * into the MPEG PS stream. | ||
534 | * | ||
535 | * In each sliced_mpeg_data[] buffer is: | ||
536 | * 16 byte MPEG-2 PS Program Pack Header | ||
537 | * 16 byte MPEG-2 Private Stream 1 PES Header | ||
538 | * 4 byte magic number: "itv0" or "ITV0" | ||
539 | * 4 byte first field line mask, if "itv0" | ||
540 | * 4 byte second field line mask, if "itv0" | ||
541 | * 36 lines, if "ITV0"; or <36 lines, if "itv0"; of sliced VBI data | ||
542 | * | ||
543 | * Each line in the payload is | ||
544 | * 1 byte line header derived from the SDID (WSS, CC, VPS, etc.) | ||
545 | * 42 bytes of line data | ||
546 | * | ||
547 | * That's a maximum 1552 bytes of payload in the Private Stream 1 packet | ||
548 | * which is the payload size a PVR-350 (CX23415) MPEG decoder will | ||
549 | * accept for VBI data. So, including the headers, it's a maximum 1584 | ||
550 | * bytes total. | ||
551 | */ | ||
552 | #define CX18_SLICED_MPEG_DATA_MAXSZ 1584 | ||
553 | /* copy_vbi_buf() needs 8 temp bytes on the end for the worst case */ | ||
554 | #define CX18_SLICED_MPEG_DATA_BUFSZ (CX18_SLICED_MPEG_DATA_MAXSZ+8) | ||
555 | u8 *sliced_mpeg_data[CX18_VBI_FRAMES]; | ||
556 | u32 sliced_mpeg_size[CX18_VBI_FRAMES]; | ||
557 | |||
558 | /* Count of Program Pack/Program Stream 1 packets inserted into PS */ | ||
559 | u32 inserted_frame; | ||
560 | |||
561 | /* | ||
562 | * A dummy driver stream transfer mdl & buffer with a copy of the next | ||
563 | * sliced_mpeg_data[] buffer for output to userland apps. | ||
564 | * Only used in cx18-fileops.c, but its state needs to persist at times. | ||
565 | */ | ||
566 | struct cx18_mdl sliced_mpeg_mdl; | ||
567 | struct cx18_buffer sliced_mpeg_buf; | ||
568 | }; | ||
569 | |||
570 | /* Per cx23418, per I2C bus private algo callback data */ | ||
571 | struct cx18_i2c_algo_callback_data { | ||
572 | struct cx18 *cx; | ||
573 | int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */ | ||
574 | }; | ||
575 | |||
576 | #define CX18_MAX_MMIO_WR_RETRIES 10 | ||
577 | |||
578 | /* Struct to hold info about cx18 cards */ | ||
579 | struct cx18 { | ||
580 | int instance; | ||
581 | struct pci_dev *pci_dev; | ||
582 | struct v4l2_device v4l2_dev; | ||
583 | struct v4l2_subdev *sd_av; /* A/V decoder/digitizer sub-device */ | ||
584 | struct v4l2_subdev *sd_extmux; /* External multiplexer sub-dev */ | ||
585 | |||
586 | const struct cx18_card *card; /* card information */ | ||
587 | const char *card_name; /* full name of the card */ | ||
588 | const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */ | ||
589 | u8 is_50hz; | ||
590 | u8 is_60hz; | ||
591 | u8 nof_inputs; /* number of video inputs */ | ||
592 | u8 nof_audio_inputs; /* number of audio inputs */ | ||
593 | u32 v4l2_cap; /* V4L2 capabilities of card */ | ||
594 | u32 hw_flags; /* Hardware description of the board */ | ||
595 | unsigned int free_mdl_idx; | ||
596 | struct cx18_scb __iomem *scb; /* pointer to SCB */ | ||
597 | struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/ | ||
598 | struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/ | ||
599 | |||
600 | struct cx18_av_state av_state; | ||
601 | |||
602 | /* codec settings */ | ||
603 | struct cx2341x_handler cxhdl; | ||
604 | u32 filter_mode; | ||
605 | u32 temporal_strength; | ||
606 | u32 spatial_strength; | ||
607 | |||
608 | /* dualwatch */ | ||
609 | unsigned long dualwatch_jiffies; | ||
610 | u32 dualwatch_stereo_mode; | ||
611 | |||
612 | struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */ | ||
613 | struct cx18_options options; /* User options */ | ||
614 | int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */ | ||
615 | int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */ | ||
616 | struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */ | ||
617 | struct snd_cx18_card *alsa; /* ALSA interface for PCM capture stream */ | ||
618 | void (*pcm_announce_callback)(struct snd_cx18_card *card, u8 *pcm_data, | ||
619 | size_t num_bytes); | ||
620 | |||
621 | unsigned long i_flags; /* global cx18 flags */ | ||
622 | atomic_t ana_capturing; /* count number of active analog capture streams */ | ||
623 | atomic_t tot_capturing; /* total count number of active capture streams */ | ||
624 | int search_pack_header; | ||
625 | |||
626 | int open_id; /* incremented each time an open occurs, used as | ||
627 | unique ID. Starts at 1, so 0 can be used as | ||
628 | uninitialized value in the stream->id. */ | ||
629 | |||
630 | u32 base_addr; | ||
631 | |||
632 | u8 card_rev; | ||
633 | void __iomem *enc_mem, *reg_mem; | ||
634 | |||
635 | struct vbi_info vbi; | ||
636 | |||
637 | u64 mpg_data_received; | ||
638 | u64 vbi_data_inserted; | ||
639 | |||
640 | wait_queue_head_t mb_apu_waitq; | ||
641 | wait_queue_head_t mb_cpu_waitq; | ||
642 | wait_queue_head_t cap_w; | ||
643 | /* when the current DMA is finished this queue is woken up */ | ||
644 | wait_queue_head_t dma_waitq; | ||
645 | |||
646 | u32 sw1_irq_mask; | ||
647 | u32 sw2_irq_mask; | ||
648 | u32 hw2_irq_mask; | ||
649 | |||
650 | struct workqueue_struct *in_work_queue; | ||
651 | char in_workq_name[11]; /* "cx18-NN-in" */ | ||
652 | struct cx18_in_work_order in_work_order[CX18_MAX_IN_WORK_ORDERS]; | ||
653 | char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */ | ||
654 | |||
655 | /* i2c */ | ||
656 | struct i2c_adapter i2c_adap[2]; | ||
657 | struct i2c_algo_bit_data i2c_algo[2]; | ||
658 | struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2]; | ||
659 | |||
660 | struct IR_i2c_init_data ir_i2c_init_data; | ||
661 | |||
662 | /* gpio */ | ||
663 | u32 gpio_dir; | ||
664 | u32 gpio_val; | ||
665 | struct mutex gpio_lock; | ||
666 | struct v4l2_subdev sd_gpiomux; | ||
667 | struct v4l2_subdev sd_resetctrl; | ||
668 | |||
669 | /* v4l2 and User settings */ | ||
670 | |||
671 | /* codec settings */ | ||
672 | u32 audio_input; | ||
673 | u32 active_input; | ||
674 | v4l2_std_id std; | ||
675 | v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ | ||
676 | |||
677 | /* Used for cx18-alsa module loading */ | ||
678 | struct work_struct request_module_wk; | ||
679 | }; | ||
680 | |||
681 | static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev) | ||
682 | { | ||
683 | return container_of(v4l2_dev, struct cx18, v4l2_dev); | ||
684 | } | ||
685 | |||
686 | /* cx18 extensions to be loaded */ | ||
687 | extern int (*cx18_ext_init)(struct cx18 *); | ||
688 | |||
689 | /* Globals */ | ||
690 | extern int cx18_first_minor; | ||
691 | |||
692 | /*==============Prototypes==================*/ | ||
693 | |||
694 | /* Return non-zero if a signal is pending */ | ||
695 | int cx18_msleep_timeout(unsigned int msecs, int intr); | ||
696 | |||
697 | /* Read Hauppauge eeprom */ | ||
698 | struct tveeprom; /* forward reference */ | ||
699 | void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv); | ||
700 | |||
701 | /* First-open initialization: load firmware, etc. */ | ||
702 | int cx18_init_on_first_open(struct cx18 *cx); | ||
703 | |||
704 | /* Test if the current VBI mode is raw (1) or sliced (0) */ | ||
705 | static inline int cx18_raw_vbi(const struct cx18 *cx) | ||
706 | { | ||
707 | return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE; | ||
708 | } | ||
709 | |||
710 | /* Call the specified callback for all subdevs with a grp_id bit matching the | ||
711 | * mask in hw (if 0, then match them all). Ignore any errors. */ | ||
712 | #define cx18_call_hw(cx, hw, o, f, args...) \ | ||
713 | do { \ | ||
714 | struct v4l2_subdev *__sd; \ | ||
715 | __v4l2_device_call_subdevs_p(&(cx)->v4l2_dev, __sd, \ | ||
716 | !(hw) || (__sd->grp_id & (hw)), o, f , ##args); \ | ||
717 | } while (0) | ||
718 | |||
719 | #define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args) | ||
720 | |||
721 | /* Call the specified callback for all subdevs with a grp_id bit matching the | ||
722 | * mask in hw (if 0, then match them all). If the callback returns an error | ||
723 | * other than 0 or -ENOIOCTLCMD, then return with that error code. */ | ||
724 | #define cx18_call_hw_err(cx, hw, o, f, args...) \ | ||
725 | ({ \ | ||
726 | struct v4l2_subdev *__sd; \ | ||
727 | __v4l2_device_call_subdevs_until_err_p(&(cx)->v4l2_dev, \ | ||
728 | __sd, !(hw) || (__sd->grp_id & (hw)), o, f, \ | ||
729 | ##args); \ | ||
730 | }) | ||
731 | |||
732 | #define cx18_call_all_err(cx, o, f, args...) \ | ||
733 | cx18_call_hw_err(cx, 0, o, f , ##args) | ||
734 | |||
735 | #endif /* CX18_DRIVER_H */ | ||
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c new file mode 100644 index 00000000000..f41922bd402 --- /dev/null +++ b/drivers/media/video/cx18/cx18-dvb.c | |||
@@ -0,0 +1,605 @@ | |||
1 | /* | ||
2 | * cx18 functions for DVB support | ||
3 | * | ||
4 | * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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 | * | ||
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 "cx18-version.h" | ||
24 | #include "cx18-dvb.h" | ||
25 | #include "cx18-io.h" | ||
26 | #include "cx18-queue.h" | ||
27 | #include "cx18-streams.h" | ||
28 | #include "cx18-cards.h" | ||
29 | #include "cx18-gpio.h" | ||
30 | #include "s5h1409.h" | ||
31 | #include "mxl5005s.h" | ||
32 | #include "s5h1411.h" | ||
33 | #include "tda18271.h" | ||
34 | #include "zl10353.h" | ||
35 | |||
36 | #include <linux/firmware.h> | ||
37 | #include "mt352.h" | ||
38 | #include "mt352_priv.h" | ||
39 | #include "tuner-xc2028.h" | ||
40 | |||
41 | DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); | ||
42 | |||
43 | #define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 | ||
44 | #define CX18_CLOCK_ENABLE2 0xc71024 | ||
45 | #define CX18_DMUX_CLK_MASK 0x0080 | ||
46 | |||
47 | /* | ||
48 | * CX18_CARD_HVR_1600_ESMT | ||
49 | * CX18_CARD_HVR_1600_SAMSUNG | ||
50 | */ | ||
51 | |||
52 | static struct mxl5005s_config hauppauge_hvr1600_tuner = { | ||
53 | .i2c_address = 0xC6 >> 1, | ||
54 | .if_freq = IF_FREQ_5380000HZ, | ||
55 | .xtal_freq = CRYSTAL_FREQ_16000000HZ, | ||
56 | .agc_mode = MXL_SINGLE_AGC, | ||
57 | .tracking_filter = MXL_TF_C_H, | ||
58 | .rssi_enable = MXL_RSSI_ENABLE, | ||
59 | .cap_select = MXL_CAP_SEL_ENABLE, | ||
60 | .div_out = MXL_DIV_OUT_4, | ||
61 | .clock_out = MXL_CLOCK_OUT_DISABLE, | ||
62 | .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, | ||
63 | .top = MXL5005S_TOP_25P2, | ||
64 | .mod_mode = MXL_DIGITAL_MODE, | ||
65 | .if_mode = MXL_ZERO_IF, | ||
66 | .qam_gain = 0x02, | ||
67 | .AgcMasterByte = 0x00, | ||
68 | }; | ||
69 | |||
70 | static struct s5h1409_config hauppauge_hvr1600_config = { | ||
71 | .demod_address = 0x32 >> 1, | ||
72 | .output_mode = S5H1409_SERIAL_OUTPUT, | ||
73 | .gpio = S5H1409_GPIO_ON, | ||
74 | .qam_if = 44000, | ||
75 | .inversion = S5H1409_INVERSION_OFF, | ||
76 | .status_mode = S5H1409_DEMODLOCKING, | ||
77 | .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, | ||
78 | .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE | ||
79 | }; | ||
80 | |||
81 | /* | ||
82 | * CX18_CARD_HVR_1600_S5H1411 | ||
83 | */ | ||
84 | static struct s5h1411_config hcw_s5h1411_config = { | ||
85 | .output_mode = S5H1411_SERIAL_OUTPUT, | ||
86 | .gpio = S5H1411_GPIO_OFF, | ||
87 | .vsb_if = S5H1411_IF_44000, | ||
88 | .qam_if = S5H1411_IF_4000, | ||
89 | .inversion = S5H1411_INVERSION_ON, | ||
90 | .status_mode = S5H1411_DEMODLOCKING, | ||
91 | .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, | ||
92 | }; | ||
93 | |||
94 | static struct tda18271_std_map hauppauge_tda18271_std_map = { | ||
95 | .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3, | ||
96 | .if_lvl = 6, .rfagc_top = 0x37 }, | ||
97 | .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, | ||
98 | .if_lvl = 6, .rfagc_top = 0x37 }, | ||
99 | }; | ||
100 | |||
101 | static struct tda18271_config hauppauge_tda18271_config = { | ||
102 | .std_map = &hauppauge_tda18271_std_map, | ||
103 | .gate = TDA18271_GATE_DIGITAL, | ||
104 | .output_opt = TDA18271_OUTPUT_LT_OFF, | ||
105 | }; | ||
106 | |||
107 | /* | ||
108 | * CX18_CARD_LEADTEK_DVR3100H | ||
109 | */ | ||
110 | /* Information/confirmation of proper config values provided by Terry Wu */ | ||
111 | static struct zl10353_config leadtek_dvr3100h_demod = { | ||
112 | .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ | ||
113 | .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ | ||
114 | .parallel_ts = 1, /* Not a serial TS */ | ||
115 | .no_tuner = 1, /* XC3028 is not behind the gate */ | ||
116 | .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ | ||
117 | }; | ||
118 | |||
119 | /* | ||
120 | * CX18_CARD_YUAN_MPC718 | ||
121 | */ | ||
122 | /* | ||
123 | * Due to | ||
124 | * | ||
125 | * 1. an absence of information on how to prgram the MT352 | ||
126 | * 2. the Linux mt352 module pushing MT352 initialzation off onto us here | ||
127 | * | ||
128 | * We have to use an init sequence that *you* must extract from the Windows | ||
129 | * driver (yuanrap.sys) and which we load as a firmware. | ||
130 | * | ||
131 | * If someone can provide me with a Zarlink MT352 (Intel CE6352?) Design Manual | ||
132 | * with chip programming details, then I can remove this annoyance. | ||
133 | */ | ||
134 | static int yuan_mpc718_mt352_reqfw(struct cx18_stream *stream, | ||
135 | const struct firmware **fw) | ||
136 | { | ||
137 | struct cx18 *cx = stream->cx; | ||
138 | const char *fn = "dvb-cx18-mpc718-mt352.fw"; | ||
139 | int ret; | ||
140 | |||
141 | ret = request_firmware(fw, fn, &cx->pci_dev->dev); | ||
142 | if (ret) | ||
143 | CX18_ERR("Unable to open firmware file %s\n", fn); | ||
144 | else { | ||
145 | size_t sz = (*fw)->size; | ||
146 | if (sz < 2 || sz > 64 || (sz % 2) != 0) { | ||
147 | CX18_ERR("Firmware %s has a bad size: %lu bytes\n", | ||
148 | fn, (unsigned long) sz); | ||
149 | ret = -EILSEQ; | ||
150 | release_firmware(*fw); | ||
151 | *fw = NULL; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | if (ret) { | ||
156 | CX18_ERR("The MPC718 board variant with the MT352 DVB-T" | ||
157 | "demodualtor will not work without it\n"); | ||
158 | CX18_ERR("Run 'linux/Documentation/dvb/get_dvb_firmware " | ||
159 | "mpc718' if you need the firmware\n"); | ||
160 | } | ||
161 | return ret; | ||
162 | } | ||
163 | |||
164 | static int yuan_mpc718_mt352_init(struct dvb_frontend *fe) | ||
165 | { | ||
166 | struct cx18_dvb *dvb = container_of(fe->dvb, | ||
167 | struct cx18_dvb, dvb_adapter); | ||
168 | struct cx18_stream *stream = dvb->stream; | ||
169 | const struct firmware *fw = NULL; | ||
170 | int ret; | ||
171 | int i; | ||
172 | u8 buf[3]; | ||
173 | |||
174 | ret = yuan_mpc718_mt352_reqfw(stream, &fw); | ||
175 | if (ret) | ||
176 | return ret; | ||
177 | |||
178 | /* Loop through all the register-value pairs in the firmware file */ | ||
179 | for (i = 0; i < fw->size; i += 2) { | ||
180 | buf[0] = fw->data[i]; | ||
181 | /* Intercept a few registers we want to set ourselves */ | ||
182 | switch (buf[0]) { | ||
183 | case TRL_NOMINAL_RATE_0: | ||
184 | /* Set our custom OFDM bandwidth in the case below */ | ||
185 | break; | ||
186 | case TRL_NOMINAL_RATE_1: | ||
187 | /* 6 MHz: 64/7 * 6/8 / 20.48 * 2^16 = 0x55b6.db6 */ | ||
188 | /* 7 MHz: 64/7 * 7/8 / 20.48 * 2^16 = 0x6400 */ | ||
189 | /* 8 MHz: 64/7 * 8/8 / 20.48 * 2^16 = 0x7249.249 */ | ||
190 | buf[1] = 0x72; | ||
191 | buf[2] = 0x49; | ||
192 | mt352_write(fe, buf, 3); | ||
193 | break; | ||
194 | case INPUT_FREQ_0: | ||
195 | /* Set our custom IF in the case below */ | ||
196 | break; | ||
197 | case INPUT_FREQ_1: | ||
198 | /* 4.56 MHz IF: (20.48 - 4.56)/20.48 * 2^14 = 0x31c0 */ | ||
199 | buf[1] = 0x31; | ||
200 | buf[2] = 0xc0; | ||
201 | mt352_write(fe, buf, 3); | ||
202 | break; | ||
203 | default: | ||
204 | /* Pass through the register-value pair from the fw */ | ||
205 | buf[1] = fw->data[i+1]; | ||
206 | mt352_write(fe, buf, 2); | ||
207 | break; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | buf[0] = (u8) TUNER_GO; | ||
212 | buf[1] = 0x01; /* Go */ | ||
213 | mt352_write(fe, buf, 2); | ||
214 | release_firmware(fw); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static struct mt352_config yuan_mpc718_mt352_demod = { | ||
219 | .demod_address = 0x1e >> 1, | ||
220 | .adc_clock = 20480, /* 20.480 MHz */ | ||
221 | .if2 = 4560, /* 4.560 MHz */ | ||
222 | .no_tuner = 1, /* XC3028 is not behind the gate */ | ||
223 | .demod_init = yuan_mpc718_mt352_init, | ||
224 | }; | ||
225 | |||
226 | static struct zl10353_config yuan_mpc718_zl10353_demod = { | ||
227 | .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ | ||
228 | .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ | ||
229 | .parallel_ts = 1, /* Not a serial TS */ | ||
230 | .no_tuner = 1, /* XC3028 is not behind the gate */ | ||
231 | .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ | ||
232 | }; | ||
233 | |||
234 | static struct zl10353_config gotview_dvd3_zl10353_demod = { | ||
235 | .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ | ||
236 | .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ | ||
237 | .parallel_ts = 1, /* Not a serial TS */ | ||
238 | .no_tuner = 1, /* XC3028 is not behind the gate */ | ||
239 | .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ | ||
240 | }; | ||
241 | |||
242 | static int dvb_register(struct cx18_stream *stream); | ||
243 | |||
244 | /* Kernel DVB framework calls this when the feed needs to start. | ||
245 | * The CX18 framework should enable the transport DMA handling | ||
246 | * and queue processing. | ||
247 | */ | ||
248 | static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) | ||
249 | { | ||
250 | struct dvb_demux *demux = feed->demux; | ||
251 | struct cx18_stream *stream = (struct cx18_stream *) demux->priv; | ||
252 | struct cx18 *cx; | ||
253 | int ret; | ||
254 | u32 v; | ||
255 | |||
256 | if (!stream) | ||
257 | return -EINVAL; | ||
258 | |||
259 | cx = stream->cx; | ||
260 | CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", | ||
261 | feed->pid, feed->index); | ||
262 | |||
263 | mutex_lock(&cx->serialize_lock); | ||
264 | ret = cx18_init_on_first_open(cx); | ||
265 | mutex_unlock(&cx->serialize_lock); | ||
266 | if (ret) { | ||
267 | CX18_ERR("Failed to initialize firmware starting DVB feed\n"); | ||
268 | return ret; | ||
269 | } | ||
270 | ret = -EINVAL; | ||
271 | |||
272 | switch (cx->card->type) { | ||
273 | case CX18_CARD_HVR_1600_ESMT: | ||
274 | case CX18_CARD_HVR_1600_SAMSUNG: | ||
275 | case CX18_CARD_HVR_1600_S5H1411: | ||
276 | v = cx18_read_reg(cx, CX18_REG_DMUX_NUM_PORT_0_CONTROL); | ||
277 | v |= 0x00400000; /* Serial Mode */ | ||
278 | v |= 0x00002000; /* Data Length - Byte */ | ||
279 | v |= 0x00010000; /* Error - Polarity */ | ||
280 | v |= 0x00020000; /* Error - Passthru */ | ||
281 | v |= 0x000c0000; /* Error - Ignore */ | ||
282 | cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); | ||
283 | break; | ||
284 | |||
285 | case CX18_CARD_LEADTEK_DVR3100H: | ||
286 | case CX18_CARD_YUAN_MPC718: | ||
287 | case CX18_CARD_GOTVIEW_PCI_DVD3: | ||
288 | default: | ||
289 | /* Assumption - Parallel transport - Signalling | ||
290 | * undefined or default. | ||
291 | */ | ||
292 | break; | ||
293 | } | ||
294 | |||
295 | if (!demux->dmx.frontend) | ||
296 | return -EINVAL; | ||
297 | |||
298 | mutex_lock(&stream->dvb->feedlock); | ||
299 | if (stream->dvb->feeding++ == 0) { | ||
300 | CX18_DEBUG_INFO("Starting Transport DMA\n"); | ||
301 | mutex_lock(&cx->serialize_lock); | ||
302 | set_bit(CX18_F_S_STREAMING, &stream->s_flags); | ||
303 | ret = cx18_start_v4l2_encode_stream(stream); | ||
304 | if (ret < 0) { | ||
305 | CX18_DEBUG_INFO("Failed to start Transport DMA\n"); | ||
306 | stream->dvb->feeding--; | ||
307 | if (stream->dvb->feeding == 0) | ||
308 | clear_bit(CX18_F_S_STREAMING, &stream->s_flags); | ||
309 | } | ||
310 | mutex_unlock(&cx->serialize_lock); | ||
311 | } else | ||
312 | ret = 0; | ||
313 | mutex_unlock(&stream->dvb->feedlock); | ||
314 | |||
315 | return ret; | ||
316 | } | ||
317 | |||
318 | /* Kernel DVB framework calls this when the feed needs to stop. */ | ||
319 | static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed) | ||
320 | { | ||
321 | struct dvb_demux *demux = feed->demux; | ||
322 | struct cx18_stream *stream = (struct cx18_stream *)demux->priv; | ||
323 | struct cx18 *cx; | ||
324 | int ret = -EINVAL; | ||
325 | |||
326 | if (stream) { | ||
327 | cx = stream->cx; | ||
328 | CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n", | ||
329 | feed->pid, feed->index); | ||
330 | |||
331 | mutex_lock(&stream->dvb->feedlock); | ||
332 | if (--stream->dvb->feeding == 0) { | ||
333 | CX18_DEBUG_INFO("Stopping Transport DMA\n"); | ||
334 | mutex_lock(&cx->serialize_lock); | ||
335 | ret = cx18_stop_v4l2_encode_stream(stream, 0); | ||
336 | mutex_unlock(&cx->serialize_lock); | ||
337 | } else | ||
338 | ret = 0; | ||
339 | mutex_unlock(&stream->dvb->feedlock); | ||
340 | } | ||
341 | |||
342 | return ret; | ||
343 | } | ||
344 | |||
345 | int cx18_dvb_register(struct cx18_stream *stream) | ||
346 | { | ||
347 | struct cx18 *cx = stream->cx; | ||
348 | struct cx18_dvb *dvb = stream->dvb; | ||
349 | struct dvb_adapter *dvb_adapter; | ||
350 | struct dvb_demux *dvbdemux; | ||
351 | struct dmx_demux *dmx; | ||
352 | int ret; | ||
353 | |||
354 | if (!dvb) | ||
355 | return -EINVAL; | ||
356 | |||
357 | dvb->enabled = 0; | ||
358 | dvb->stream = stream; | ||
359 | |||
360 | ret = dvb_register_adapter(&dvb->dvb_adapter, | ||
361 | CX18_DRIVER_NAME, | ||
362 | THIS_MODULE, &cx->pci_dev->dev, adapter_nr); | ||
363 | if (ret < 0) | ||
364 | goto err_out; | ||
365 | |||
366 | dvb_adapter = &dvb->dvb_adapter; | ||
367 | |||
368 | dvbdemux = &dvb->demux; | ||
369 | |||
370 | dvbdemux->priv = (void *)stream; | ||
371 | |||
372 | dvbdemux->filternum = 256; | ||
373 | dvbdemux->feednum = 256; | ||
374 | dvbdemux->start_feed = cx18_dvb_start_feed; | ||
375 | dvbdemux->stop_feed = cx18_dvb_stop_feed; | ||
376 | dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | | ||
377 | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); | ||
378 | ret = dvb_dmx_init(dvbdemux); | ||
379 | if (ret < 0) | ||
380 | goto err_dvb_unregister_adapter; | ||
381 | |||
382 | dmx = &dvbdemux->dmx; | ||
383 | |||
384 | dvb->hw_frontend.source = DMX_FRONTEND_0; | ||
385 | dvb->mem_frontend.source = DMX_MEMORY_FE; | ||
386 | dvb->dmxdev.filternum = 256; | ||
387 | dvb->dmxdev.demux = dmx; | ||
388 | |||
389 | ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter); | ||
390 | if (ret < 0) | ||
391 | goto err_dvb_dmx_release; | ||
392 | |||
393 | ret = dmx->add_frontend(dmx, &dvb->hw_frontend); | ||
394 | if (ret < 0) | ||
395 | goto err_dvb_dmxdev_release; | ||
396 | |||
397 | ret = dmx->add_frontend(dmx, &dvb->mem_frontend); | ||
398 | if (ret < 0) | ||
399 | goto err_remove_hw_frontend; | ||
400 | |||
401 | ret = dmx->connect_frontend(dmx, &dvb->hw_frontend); | ||
402 | if (ret < 0) | ||
403 | goto err_remove_mem_frontend; | ||
404 | |||
405 | ret = dvb_register(stream); | ||
406 | if (ret < 0) | ||
407 | goto err_disconnect_frontend; | ||
408 | |||
409 | dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); | ||
410 | |||
411 | CX18_INFO("DVB Frontend registered\n"); | ||
412 | CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n", | ||
413 | stream->dvb->dvb_adapter.num, stream->name, | ||
414 | stream->buffers, stream->buf_size/1024, | ||
415 | (stream->buf_size * 100 / 1024) % 100); | ||
416 | |||
417 | mutex_init(&dvb->feedlock); | ||
418 | dvb->enabled = 1; | ||
419 | return ret; | ||
420 | |||
421 | err_disconnect_frontend: | ||
422 | dmx->disconnect_frontend(dmx); | ||
423 | err_remove_mem_frontend: | ||
424 | dmx->remove_frontend(dmx, &dvb->mem_frontend); | ||
425 | err_remove_hw_frontend: | ||
426 | dmx->remove_frontend(dmx, &dvb->hw_frontend); | ||
427 | err_dvb_dmxdev_release: | ||
428 | dvb_dmxdev_release(&dvb->dmxdev); | ||
429 | err_dvb_dmx_release: | ||
430 | dvb_dmx_release(dvbdemux); | ||
431 | err_dvb_unregister_adapter: | ||
432 | dvb_unregister_adapter(dvb_adapter); | ||
433 | err_out: | ||
434 | return ret; | ||
435 | } | ||
436 | |||
437 | void cx18_dvb_unregister(struct cx18_stream *stream) | ||
438 | { | ||
439 | struct cx18 *cx = stream->cx; | ||
440 | struct cx18_dvb *dvb = stream->dvb; | ||
441 | struct dvb_adapter *dvb_adapter; | ||
442 | struct dvb_demux *dvbdemux; | ||
443 | struct dmx_demux *dmx; | ||
444 | |||
445 | CX18_INFO("unregister DVB\n"); | ||
446 | |||
447 | if (dvb == NULL || !dvb->enabled) | ||
448 | return; | ||
449 | |||
450 | dvb_adapter = &dvb->dvb_adapter; | ||
451 | dvbdemux = &dvb->demux; | ||
452 | dmx = &dvbdemux->dmx; | ||
453 | |||
454 | dmx->close(dmx); | ||
455 | dvb_net_release(&dvb->dvbnet); | ||
456 | dmx->remove_frontend(dmx, &dvb->mem_frontend); | ||
457 | dmx->remove_frontend(dmx, &dvb->hw_frontend); | ||
458 | dvb_dmxdev_release(&dvb->dmxdev); | ||
459 | dvb_dmx_release(dvbdemux); | ||
460 | dvb_unregister_frontend(dvb->fe); | ||
461 | dvb_frontend_detach(dvb->fe); | ||
462 | dvb_unregister_adapter(dvb_adapter); | ||
463 | } | ||
464 | |||
465 | /* All the DVB attach calls go here, this function get's modified | ||
466 | * for each new card. cx18_dvb_start_feed() will also need changes. | ||
467 | */ | ||
468 | static int dvb_register(struct cx18_stream *stream) | ||
469 | { | ||
470 | struct cx18_dvb *dvb = stream->dvb; | ||
471 | struct cx18 *cx = stream->cx; | ||
472 | int ret = 0; | ||
473 | |||
474 | switch (cx->card->type) { | ||
475 | case CX18_CARD_HVR_1600_ESMT: | ||
476 | case CX18_CARD_HVR_1600_SAMSUNG: | ||
477 | dvb->fe = dvb_attach(s5h1409_attach, | ||
478 | &hauppauge_hvr1600_config, | ||
479 | &cx->i2c_adap[0]); | ||
480 | if (dvb->fe != NULL) { | ||
481 | dvb_attach(mxl5005s_attach, dvb->fe, | ||
482 | &cx->i2c_adap[0], | ||
483 | &hauppauge_hvr1600_tuner); | ||
484 | ret = 0; | ||
485 | } | ||
486 | break; | ||
487 | case CX18_CARD_HVR_1600_S5H1411: | ||
488 | dvb->fe = dvb_attach(s5h1411_attach, | ||
489 | &hcw_s5h1411_config, | ||
490 | &cx->i2c_adap[0]); | ||
491 | if (dvb->fe != NULL) | ||
492 | dvb_attach(tda18271_attach, dvb->fe, | ||
493 | 0x60, &cx->i2c_adap[0], | ||
494 | &hauppauge_tda18271_config); | ||
495 | break; | ||
496 | case CX18_CARD_LEADTEK_DVR3100H: | ||
497 | dvb->fe = dvb_attach(zl10353_attach, | ||
498 | &leadtek_dvr3100h_demod, | ||
499 | &cx->i2c_adap[1]); | ||
500 | if (dvb->fe != NULL) { | ||
501 | struct dvb_frontend *fe; | ||
502 | struct xc2028_config cfg = { | ||
503 | .i2c_adap = &cx->i2c_adap[1], | ||
504 | .i2c_addr = 0xc2 >> 1, | ||
505 | .ctrl = NULL, | ||
506 | }; | ||
507 | static struct xc2028_ctrl ctrl = { | ||
508 | .fname = XC2028_DEFAULT_FIRMWARE, | ||
509 | .max_len = 64, | ||
510 | .demod = XC3028_FE_ZARLINK456, | ||
511 | .type = XC2028_AUTO, | ||
512 | }; | ||
513 | |||
514 | fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); | ||
515 | if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) | ||
516 | fe->ops.tuner_ops.set_config(fe, &ctrl); | ||
517 | } | ||
518 | break; | ||
519 | case CX18_CARD_YUAN_MPC718: | ||
520 | /* | ||
521 | * TODO | ||
522 | * Apparently, these cards also could instead have a | ||
523 | * DiBcom demod supported by one of the db7000 drivers | ||
524 | */ | ||
525 | dvb->fe = dvb_attach(mt352_attach, | ||
526 | &yuan_mpc718_mt352_demod, | ||
527 | &cx->i2c_adap[1]); | ||
528 | if (dvb->fe == NULL) | ||
529 | dvb->fe = dvb_attach(zl10353_attach, | ||
530 | &yuan_mpc718_zl10353_demod, | ||
531 | &cx->i2c_adap[1]); | ||
532 | if (dvb->fe != NULL) { | ||
533 | struct dvb_frontend *fe; | ||
534 | struct xc2028_config cfg = { | ||
535 | .i2c_adap = &cx->i2c_adap[1], | ||
536 | .i2c_addr = 0xc2 >> 1, | ||
537 | .ctrl = NULL, | ||
538 | }; | ||
539 | static struct xc2028_ctrl ctrl = { | ||
540 | .fname = XC2028_DEFAULT_FIRMWARE, | ||
541 | .max_len = 64, | ||
542 | .demod = XC3028_FE_ZARLINK456, | ||
543 | .type = XC2028_AUTO, | ||
544 | }; | ||
545 | |||
546 | fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); | ||
547 | if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) | ||
548 | fe->ops.tuner_ops.set_config(fe, &ctrl); | ||
549 | } | ||
550 | break; | ||
551 | case CX18_CARD_GOTVIEW_PCI_DVD3: | ||
552 | dvb->fe = dvb_attach(zl10353_attach, | ||
553 | &gotview_dvd3_zl10353_demod, | ||
554 | &cx->i2c_adap[1]); | ||
555 | if (dvb->fe != NULL) { | ||
556 | struct dvb_frontend *fe; | ||
557 | struct xc2028_config cfg = { | ||
558 | .i2c_adap = &cx->i2c_adap[1], | ||
559 | .i2c_addr = 0xc2 >> 1, | ||
560 | .ctrl = NULL, | ||
561 | }; | ||
562 | static struct xc2028_ctrl ctrl = { | ||
563 | .fname = XC2028_DEFAULT_FIRMWARE, | ||
564 | .max_len = 64, | ||
565 | .demod = XC3028_FE_ZARLINK456, | ||
566 | .type = XC2028_AUTO, | ||
567 | }; | ||
568 | |||
569 | fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); | ||
570 | if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) | ||
571 | fe->ops.tuner_ops.set_config(fe, &ctrl); | ||
572 | } | ||
573 | break; | ||
574 | default: | ||
575 | /* No Digital Tv Support */ | ||
576 | break; | ||
577 | } | ||
578 | |||
579 | if (dvb->fe == NULL) { | ||
580 | CX18_ERR("frontend initialization failed\n"); | ||
581 | return -1; | ||
582 | } | ||
583 | |||
584 | dvb->fe->callback = cx18_reset_tuner_gpio; | ||
585 | |||
586 | ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); | ||
587 | if (ret < 0) { | ||
588 | if (dvb->fe->ops.release) | ||
589 | dvb->fe->ops.release(dvb->fe); | ||
590 | return ret; | ||
591 | } | ||
592 | |||
593 | /* | ||
594 | * The firmware seems to enable the TS DMUX clock | ||
595 | * under various circumstances. However, since we know we | ||
596 | * might use it, let's just turn it on ourselves here. | ||
597 | */ | ||
598 | cx18_write_reg_expect(cx, | ||
599 | (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK, | ||
600 | CX18_CLOCK_ENABLE2, | ||
601 | CX18_DMUX_CLK_MASK, | ||
602 | (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK); | ||
603 | |||
604 | return ret; | ||
605 | } | ||
diff --git a/drivers/media/video/cx18/cx18-dvb.h b/drivers/media/video/cx18/cx18-dvb.h new file mode 100644 index 00000000000..bf8d8f6f545 --- /dev/null +++ b/drivers/media/video/cx18/cx18-dvb.h | |||
@@ -0,0 +1,25 @@ | |||
1 | /* | ||
2 | * cx18 functions for DVB support | ||
3 | * | ||
4 | * Copyright (c) 2008 Steven Toth <stoth@linuxtv.org> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * | ||
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 "cx18-driver.h" | ||
23 | |||
24 | int cx18_dvb_register(struct cx18_stream *stream); | ||
25 | void cx18_dvb_unregister(struct cx18_stream *stream); | ||
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c new file mode 100644 index 00000000000..07411f34885 --- /dev/null +++ b/drivers/media/video/cx18/cx18-fileops.c | |||
@@ -0,0 +1,896 @@ | |||
1 | /* | ||
2 | * cx18 file operation functions | ||
3 | * | ||
4 | * Derived from ivtv-fileops.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | #include "cx18-fileops.h" | ||
27 | #include "cx18-i2c.h" | ||
28 | #include "cx18-queue.h" | ||
29 | #include "cx18-vbi.h" | ||
30 | #include "cx18-audio.h" | ||
31 | #include "cx18-mailbox.h" | ||
32 | #include "cx18-scb.h" | ||
33 | #include "cx18-streams.h" | ||
34 | #include "cx18-controls.h" | ||
35 | #include "cx18-ioctl.h" | ||
36 | #include "cx18-cards.h" | ||
37 | |||
38 | /* This function tries to claim the stream for a specific file descriptor. | ||
39 | If no one else is using this stream then the stream is claimed and | ||
40 | associated VBI and IDX streams are also automatically claimed. | ||
41 | Possible error returns: -EBUSY if someone else has claimed | ||
42 | the stream or 0 on success. */ | ||
43 | int cx18_claim_stream(struct cx18_open_id *id, int type) | ||
44 | { | ||
45 | struct cx18 *cx = id->cx; | ||
46 | struct cx18_stream *s = &cx->streams[type]; | ||
47 | struct cx18_stream *s_assoc; | ||
48 | |||
49 | /* Nothing should ever try to directly claim the IDX stream */ | ||
50 | if (type == CX18_ENC_STREAM_TYPE_IDX) { | ||
51 | CX18_WARN("MPEG Index stream cannot be claimed " | ||
52 | "directly, but something tried.\n"); | ||
53 | return -EINVAL; | ||
54 | } | ||
55 | |||
56 | if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) { | ||
57 | /* someone already claimed this stream */ | ||
58 | if (s->id == id->open_id) { | ||
59 | /* yes, this file descriptor did. So that's OK. */ | ||
60 | return 0; | ||
61 | } | ||
62 | if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) { | ||
63 | /* VBI is handled already internally, now also assign | ||
64 | the file descriptor to this stream for external | ||
65 | reading of the stream. */ | ||
66 | s->id = id->open_id; | ||
67 | CX18_DEBUG_INFO("Start Read VBI\n"); | ||
68 | return 0; | ||
69 | } | ||
70 | /* someone else is using this stream already */ | ||
71 | CX18_DEBUG_INFO("Stream %d is busy\n", type); | ||
72 | return -EBUSY; | ||
73 | } | ||
74 | s->id = id->open_id; | ||
75 | |||
76 | /* | ||
77 | * CX18_ENC_STREAM_TYPE_MPG needs to claim: | ||
78 | * CX18_ENC_STREAM_TYPE_VBI, if VBI insertion is on for sliced VBI, or | ||
79 | * CX18_ENC_STREAM_TYPE_IDX, if VBI insertion is off for sliced VBI | ||
80 | * (We don't yet fix up MPEG Index entries for our inserted packets). | ||
81 | * | ||
82 | * For all other streams we're done. | ||
83 | */ | ||
84 | if (type != CX18_ENC_STREAM_TYPE_MPG) | ||
85 | return 0; | ||
86 | |||
87 | s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; | ||
88 | if (cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) | ||
89 | s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; | ||
90 | else if (!cx18_stream_enabled(s_assoc)) | ||
91 | return 0; | ||
92 | |||
93 | set_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags); | ||
94 | |||
95 | /* mark that it is used internally */ | ||
96 | set_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags); | ||
97 | return 0; | ||
98 | } | ||
99 | EXPORT_SYMBOL(cx18_claim_stream); | ||
100 | |||
101 | /* This function releases a previously claimed stream. It will take into | ||
102 | account associated VBI streams. */ | ||
103 | void cx18_release_stream(struct cx18_stream *s) | ||
104 | { | ||
105 | struct cx18 *cx = s->cx; | ||
106 | struct cx18_stream *s_assoc; | ||
107 | |||
108 | s->id = -1; | ||
109 | if (s->type == CX18_ENC_STREAM_TYPE_IDX) { | ||
110 | /* | ||
111 | * The IDX stream is only used internally, and can | ||
112 | * only be indirectly unclaimed by unclaiming the MPG stream. | ||
113 | */ | ||
114 | return; | ||
115 | } | ||
116 | |||
117 | if (s->type == CX18_ENC_STREAM_TYPE_VBI && | ||
118 | test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) { | ||
119 | /* this stream is still in use internally */ | ||
120 | return; | ||
121 | } | ||
122 | if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) { | ||
123 | CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name); | ||
124 | return; | ||
125 | } | ||
126 | |||
127 | cx18_flush_queues(s); | ||
128 | |||
129 | /* | ||
130 | * CX18_ENC_STREAM_TYPE_MPG needs to release the | ||
131 | * CX18_ENC_STREAM_TYPE_VBI and/or CX18_ENC_STREAM_TYPE_IDX streams. | ||
132 | * | ||
133 | * For all other streams we're done. | ||
134 | */ | ||
135 | if (s->type != CX18_ENC_STREAM_TYPE_MPG) | ||
136 | return; | ||
137 | |||
138 | /* Unclaim the associated MPEG Index stream */ | ||
139 | s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; | ||
140 | if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) { | ||
141 | clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags); | ||
142 | cx18_flush_queues(s_assoc); | ||
143 | } | ||
144 | |||
145 | /* Unclaim the associated VBI stream */ | ||
146 | s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; | ||
147 | if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) { | ||
148 | if (s_assoc->id == -1) { | ||
149 | /* | ||
150 | * The VBI stream is not still claimed by a file | ||
151 | * descriptor, so completely unclaim it. | ||
152 | */ | ||
153 | clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags); | ||
154 | cx18_flush_queues(s_assoc); | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | EXPORT_SYMBOL(cx18_release_stream); | ||
159 | |||
160 | static void cx18_dualwatch(struct cx18 *cx) | ||
161 | { | ||
162 | struct v4l2_tuner vt; | ||
163 | u32 new_stereo_mode; | ||
164 | const u32 dual = 0x0200; | ||
165 | |||
166 | new_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode); | ||
167 | memset(&vt, 0, sizeof(vt)); | ||
168 | cx18_call_all(cx, tuner, g_tuner, &vt); | ||
169 | if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && | ||
170 | (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) | ||
171 | new_stereo_mode = dual; | ||
172 | |||
173 | if (new_stereo_mode == cx->dualwatch_stereo_mode) | ||
174 | return; | ||
175 | |||
176 | CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n", | ||
177 | cx->dualwatch_stereo_mode, new_stereo_mode); | ||
178 | if (v4l2_ctrl_s_ctrl(cx->cxhdl.audio_mode, new_stereo_mode)) | ||
179 | CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); | ||
180 | } | ||
181 | |||
182 | |||
183 | static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block, | ||
184 | int *err) | ||
185 | { | ||
186 | struct cx18 *cx = s->cx; | ||
187 | struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; | ||
188 | struct cx18_mdl *mdl; | ||
189 | DEFINE_WAIT(wait); | ||
190 | |||
191 | *err = 0; | ||
192 | while (1) { | ||
193 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) { | ||
194 | /* Process pending program updates and VBI data */ | ||
195 | if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) { | ||
196 | cx->dualwatch_jiffies = jiffies; | ||
197 | cx18_dualwatch(cx); | ||
198 | } | ||
199 | if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && | ||
200 | !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { | ||
201 | while ((mdl = cx18_dequeue(s_vbi, | ||
202 | &s_vbi->q_full))) { | ||
203 | /* byteswap and process VBI data */ | ||
204 | cx18_process_vbi_data(cx, mdl, | ||
205 | s_vbi->type); | ||
206 | cx18_stream_put_mdl_fw(s_vbi, mdl); | ||
207 | } | ||
208 | } | ||
209 | mdl = &cx->vbi.sliced_mpeg_mdl; | ||
210 | if (mdl->readpos != mdl->bytesused) | ||
211 | return mdl; | ||
212 | } | ||
213 | |||
214 | /* do we have new data? */ | ||
215 | mdl = cx18_dequeue(s, &s->q_full); | ||
216 | if (mdl) { | ||
217 | if (!test_and_clear_bit(CX18_F_M_NEED_SWAP, | ||
218 | &mdl->m_flags)) | ||
219 | return mdl; | ||
220 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) | ||
221 | /* byteswap MPG data */ | ||
222 | cx18_mdl_swap(mdl); | ||
223 | else { | ||
224 | /* byteswap and process VBI data */ | ||
225 | cx18_process_vbi_data(cx, mdl, s->type); | ||
226 | } | ||
227 | return mdl; | ||
228 | } | ||
229 | |||
230 | /* return if end of stream */ | ||
231 | if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) { | ||
232 | CX18_DEBUG_INFO("EOS %s\n", s->name); | ||
233 | return NULL; | ||
234 | } | ||
235 | |||
236 | /* return if file was opened with O_NONBLOCK */ | ||
237 | if (non_block) { | ||
238 | *err = -EAGAIN; | ||
239 | return NULL; | ||
240 | } | ||
241 | |||
242 | /* wait for more data to arrive */ | ||
243 | prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); | ||
244 | /* New buffers might have become available before we were added | ||
245 | to the waitqueue */ | ||
246 | if (!atomic_read(&s->q_full.depth)) | ||
247 | schedule(); | ||
248 | finish_wait(&s->waitq, &wait); | ||
249 | if (signal_pending(current)) { | ||
250 | /* return if a signal was received */ | ||
251 | CX18_DEBUG_INFO("User stopped %s\n", s->name); | ||
252 | *err = -EINTR; | ||
253 | return NULL; | ||
254 | } | ||
255 | } | ||
256 | } | ||
257 | |||
258 | static void cx18_setup_sliced_vbi_mdl(struct cx18 *cx) | ||
259 | { | ||
260 | struct cx18_mdl *mdl = &cx->vbi.sliced_mpeg_mdl; | ||
261 | struct cx18_buffer *buf = &cx->vbi.sliced_mpeg_buf; | ||
262 | int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; | ||
263 | |||
264 | buf->buf = cx->vbi.sliced_mpeg_data[idx]; | ||
265 | buf->bytesused = cx->vbi.sliced_mpeg_size[idx]; | ||
266 | buf->readpos = 0; | ||
267 | |||
268 | mdl->curr_buf = NULL; | ||
269 | mdl->bytesused = cx->vbi.sliced_mpeg_size[idx]; | ||
270 | mdl->readpos = 0; | ||
271 | } | ||
272 | |||
273 | static size_t cx18_copy_buf_to_user(struct cx18_stream *s, | ||
274 | struct cx18_buffer *buf, char __user *ubuf, size_t ucount, bool *stop) | ||
275 | { | ||
276 | struct cx18 *cx = s->cx; | ||
277 | size_t len = buf->bytesused - buf->readpos; | ||
278 | |||
279 | *stop = false; | ||
280 | if (len > ucount) | ||
281 | len = ucount; | ||
282 | if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && | ||
283 | !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) { | ||
284 | /* | ||
285 | * Try to find a good splice point in the PS, just before | ||
286 | * an MPEG-2 Program Pack start code, and provide only | ||
287 | * up to that point to the user, so it's easy to insert VBI data | ||
288 | * the next time around. | ||
289 | * | ||
290 | * This will not work for an MPEG-2 TS and has only been | ||
291 | * verified by analysis to work for an MPEG-2 PS. Helen Buus | ||
292 | * pointed out this works for the CX23416 MPEG-2 DVD compatible | ||
293 | * stream, and research indicates both the MPEG 2 SVCD and DVD | ||
294 | * stream types use an MPEG-2 PS container. | ||
295 | */ | ||
296 | /* | ||
297 | * An MPEG-2 Program Stream (PS) is a series of | ||
298 | * MPEG-2 Program Packs terminated by an | ||
299 | * MPEG Program End Code after the last Program Pack. | ||
300 | * A Program Pack may hold a PS System Header packet and any | ||
301 | * number of Program Elementary Stream (PES) Packets | ||
302 | */ | ||
303 | const char *start = buf->buf + buf->readpos; | ||
304 | const char *p = start + 1; | ||
305 | const u8 *q; | ||
306 | u8 ch = cx->search_pack_header ? 0xba : 0xe0; | ||
307 | int stuffing, i; | ||
308 | |||
309 | while (start + len > p) { | ||
310 | /* Scan for a 0 to find a potential MPEG-2 start code */ | ||
311 | q = memchr(p, 0, start + len - p); | ||
312 | if (q == NULL) | ||
313 | break; | ||
314 | p = q + 1; | ||
315 | /* | ||
316 | * Keep looking if not a | ||
317 | * MPEG-2 Pack header start code: 0x00 0x00 0x01 0xba | ||
318 | * or MPEG-2 video PES start code: 0x00 0x00 0x01 0xe0 | ||
319 | */ | ||
320 | if ((char *)q + 15 >= buf->buf + buf->bytesused || | ||
321 | q[1] != 0 || q[2] != 1 || q[3] != ch) | ||
322 | continue; | ||
323 | |||
324 | /* If expecting the primary video PES */ | ||
325 | if (!cx->search_pack_header) { | ||
326 | /* Continue if it couldn't be a PES packet */ | ||
327 | if ((q[6] & 0xc0) != 0x80) | ||
328 | continue; | ||
329 | /* Check if a PTS or PTS & DTS follow */ | ||
330 | if (((q[7] & 0xc0) == 0x80 && /* PTS only */ | ||
331 | (q[9] & 0xf0) == 0x20) || /* PTS only */ | ||
332 | ((q[7] & 0xc0) == 0xc0 && /* PTS & DTS */ | ||
333 | (q[9] & 0xf0) == 0x30)) { /* DTS follows */ | ||
334 | /* Assume we found the video PES hdr */ | ||
335 | ch = 0xba; /* next want a Program Pack*/ | ||
336 | cx->search_pack_header = 1; | ||
337 | p = q + 9; /* Skip this video PES hdr */ | ||
338 | } | ||
339 | continue; | ||
340 | } | ||
341 | |||
342 | /* We may have found a Program Pack start code */ | ||
343 | |||
344 | /* Get the count of stuffing bytes & verify them */ | ||
345 | stuffing = q[13] & 7; | ||
346 | /* all stuffing bytes must be 0xff */ | ||
347 | for (i = 0; i < stuffing; i++) | ||
348 | if (q[14 + i] != 0xff) | ||
349 | break; | ||
350 | if (i == stuffing && /* right number of stuffing bytes*/ | ||
351 | (q[4] & 0xc4) == 0x44 && /* marker check */ | ||
352 | (q[12] & 3) == 3 && /* marker check */ | ||
353 | q[14 + stuffing] == 0 && /* PES Pack or Sys Hdr */ | ||
354 | q[15 + stuffing] == 0 && | ||
355 | q[16 + stuffing] == 1) { | ||
356 | /* We declare we actually found a Program Pack*/ | ||
357 | cx->search_pack_header = 0; /* expect vid PES */ | ||
358 | len = (char *)q - start; | ||
359 | cx18_setup_sliced_vbi_mdl(cx); | ||
360 | *stop = true; | ||
361 | break; | ||
362 | } | ||
363 | } | ||
364 | } | ||
365 | if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) { | ||
366 | CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n", | ||
367 | len, s->name); | ||
368 | return -EFAULT; | ||
369 | } | ||
370 | buf->readpos += len; | ||
371 | if (s->type == CX18_ENC_STREAM_TYPE_MPG && | ||
372 | buf != &cx->vbi.sliced_mpeg_buf) | ||
373 | cx->mpg_data_received += len; | ||
374 | return len; | ||
375 | } | ||
376 | |||
377 | static size_t cx18_copy_mdl_to_user(struct cx18_stream *s, | ||
378 | struct cx18_mdl *mdl, char __user *ubuf, size_t ucount) | ||
379 | { | ||
380 | size_t tot_written = 0; | ||
381 | int rc; | ||
382 | bool stop = false; | ||
383 | |||
384 | if (mdl->curr_buf == NULL) | ||
385 | mdl->curr_buf = list_first_entry(&mdl->buf_list, | ||
386 | struct cx18_buffer, list); | ||
387 | |||
388 | if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) { | ||
389 | /* | ||
390 | * For some reason we've exhausted the buffers, but the MDL | ||
391 | * object still said some data was unread. | ||
392 | * Fix that and bail out. | ||
393 | */ | ||
394 | mdl->readpos = mdl->bytesused; | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) { | ||
399 | |||
400 | if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused) | ||
401 | continue; | ||
402 | |||
403 | rc = cx18_copy_buf_to_user(s, mdl->curr_buf, ubuf + tot_written, | ||
404 | ucount - tot_written, &stop); | ||
405 | if (rc < 0) | ||
406 | return rc; | ||
407 | mdl->readpos += rc; | ||
408 | tot_written += rc; | ||
409 | |||
410 | if (stop || /* Forced stopping point for VBI insertion */ | ||
411 | tot_written >= ucount || /* Reader request statisfied */ | ||
412 | mdl->curr_buf->readpos < mdl->curr_buf->bytesused || | ||
413 | mdl->readpos >= mdl->bytesused) /* MDL buffers drained */ | ||
414 | break; | ||
415 | } | ||
416 | return tot_written; | ||
417 | } | ||
418 | |||
419 | static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, | ||
420 | size_t tot_count, int non_block) | ||
421 | { | ||
422 | struct cx18 *cx = s->cx; | ||
423 | size_t tot_written = 0; | ||
424 | int single_frame = 0; | ||
425 | |||
426 | if (atomic_read(&cx->ana_capturing) == 0 && s->id == -1) { | ||
427 | /* shouldn't happen */ | ||
428 | CX18_DEBUG_WARN("Stream %s not initialized before read\n", | ||
429 | s->name); | ||
430 | return -EIO; | ||
431 | } | ||
432 | |||
433 | /* Each VBI buffer is one frame, the v4l2 API says that for VBI the | ||
434 | frames should arrive one-by-one, so make sure we never output more | ||
435 | than one VBI frame at a time */ | ||
436 | if (s->type == CX18_ENC_STREAM_TYPE_VBI && !cx18_raw_vbi(cx)) | ||
437 | single_frame = 1; | ||
438 | |||
439 | for (;;) { | ||
440 | struct cx18_mdl *mdl; | ||
441 | int rc; | ||
442 | |||
443 | mdl = cx18_get_mdl(s, non_block, &rc); | ||
444 | /* if there is no data available... */ | ||
445 | if (mdl == NULL) { | ||
446 | /* if we got data, then return that regardless */ | ||
447 | if (tot_written) | ||
448 | break; | ||
449 | /* EOS condition */ | ||
450 | if (rc == 0) { | ||
451 | clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); | ||
452 | clear_bit(CX18_F_S_APPL_IO, &s->s_flags); | ||
453 | cx18_release_stream(s); | ||
454 | } | ||
455 | /* set errno */ | ||
456 | return rc; | ||
457 | } | ||
458 | |||
459 | rc = cx18_copy_mdl_to_user(s, mdl, ubuf + tot_written, | ||
460 | tot_count - tot_written); | ||
461 | |||
462 | if (mdl != &cx->vbi.sliced_mpeg_mdl) { | ||
463 | if (mdl->readpos == mdl->bytesused) | ||
464 | cx18_stream_put_mdl_fw(s, mdl); | ||
465 | else | ||
466 | cx18_push(s, mdl, &s->q_full); | ||
467 | } else if (mdl->readpos == mdl->bytesused) { | ||
468 | int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; | ||
469 | |||
470 | cx->vbi.sliced_mpeg_size[idx] = 0; | ||
471 | cx->vbi.inserted_frame++; | ||
472 | cx->vbi_data_inserted += mdl->bytesused; | ||
473 | } | ||
474 | if (rc < 0) | ||
475 | return rc; | ||
476 | tot_written += rc; | ||
477 | |||
478 | if (tot_written == tot_count || single_frame) | ||
479 | break; | ||
480 | } | ||
481 | return tot_written; | ||
482 | } | ||
483 | |||
484 | static ssize_t cx18_read_pos(struct cx18_stream *s, char __user *ubuf, | ||
485 | size_t count, loff_t *pos, int non_block) | ||
486 | { | ||
487 | ssize_t rc = count ? cx18_read(s, ubuf, count, non_block) : 0; | ||
488 | struct cx18 *cx = s->cx; | ||
489 | |||
490 | CX18_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc); | ||
491 | if (rc > 0) | ||
492 | pos += rc; | ||
493 | return rc; | ||
494 | } | ||
495 | |||
496 | int cx18_start_capture(struct cx18_open_id *id) | ||
497 | { | ||
498 | struct cx18 *cx = id->cx; | ||
499 | struct cx18_stream *s = &cx->streams[id->type]; | ||
500 | struct cx18_stream *s_vbi; | ||
501 | struct cx18_stream *s_idx; | ||
502 | |||
503 | if (s->type == CX18_ENC_STREAM_TYPE_RAD) { | ||
504 | /* you cannot read from these stream types. */ | ||
505 | return -EPERM; | ||
506 | } | ||
507 | |||
508 | /* Try to claim this stream. */ | ||
509 | if (cx18_claim_stream(id, s->type)) | ||
510 | return -EBUSY; | ||
511 | |||
512 | /* If capture is already in progress, then we also have to | ||
513 | do nothing extra. */ | ||
514 | if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) || | ||
515 | test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) { | ||
516 | set_bit(CX18_F_S_APPL_IO, &s->s_flags); | ||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | /* Start associated VBI or IDX stream capture if required */ | ||
521 | s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; | ||
522 | s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; | ||
523 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) { | ||
524 | /* | ||
525 | * The VBI and IDX streams should have been claimed | ||
526 | * automatically, if for internal use, when the MPG stream was | ||
527 | * claimed. We only need to start these streams capturing. | ||
528 | */ | ||
529 | if (test_bit(CX18_F_S_INTERNAL_USE, &s_idx->s_flags) && | ||
530 | !test_and_set_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) { | ||
531 | if (cx18_start_v4l2_encode_stream(s_idx)) { | ||
532 | CX18_DEBUG_WARN("IDX capture start failed\n"); | ||
533 | clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags); | ||
534 | goto start_failed; | ||
535 | } | ||
536 | CX18_DEBUG_INFO("IDX capture started\n"); | ||
537 | } | ||
538 | if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && | ||
539 | !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { | ||
540 | if (cx18_start_v4l2_encode_stream(s_vbi)) { | ||
541 | CX18_DEBUG_WARN("VBI capture start failed\n"); | ||
542 | clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); | ||
543 | goto start_failed; | ||
544 | } | ||
545 | CX18_DEBUG_INFO("VBI insertion started\n"); | ||
546 | } | ||
547 | } | ||
548 | |||
549 | /* Tell the card to start capturing */ | ||
550 | if (!cx18_start_v4l2_encode_stream(s)) { | ||
551 | /* We're done */ | ||
552 | set_bit(CX18_F_S_APPL_IO, &s->s_flags); | ||
553 | /* Resume a possibly paused encoder */ | ||
554 | if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) | ||
555 | cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle); | ||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | start_failed: | ||
560 | CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name); | ||
561 | |||
562 | /* | ||
563 | * The associated VBI and IDX streams for internal use are released | ||
564 | * automatically when the MPG stream is released. We only need to stop | ||
565 | * the associated stream. | ||
566 | */ | ||
567 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) { | ||
568 | /* Stop the IDX stream which is always for internal use */ | ||
569 | if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) { | ||
570 | cx18_stop_v4l2_encode_stream(s_idx, 0); | ||
571 | clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags); | ||
572 | } | ||
573 | /* Stop the VBI stream, if only running for internal use */ | ||
574 | if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) && | ||
575 | !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { | ||
576 | cx18_stop_v4l2_encode_stream(s_vbi, 0); | ||
577 | clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); | ||
578 | } | ||
579 | } | ||
580 | clear_bit(CX18_F_S_STREAMING, &s->s_flags); | ||
581 | cx18_release_stream(s); /* Also releases associated streams */ | ||
582 | return -EIO; | ||
583 | } | ||
584 | |||
585 | ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count, | ||
586 | loff_t *pos) | ||
587 | { | ||
588 | struct cx18_open_id *id = file2id(filp); | ||
589 | struct cx18 *cx = id->cx; | ||
590 | struct cx18_stream *s = &cx->streams[id->type]; | ||
591 | int rc; | ||
592 | |||
593 | CX18_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name); | ||
594 | |||
595 | mutex_lock(&cx->serialize_lock); | ||
596 | rc = cx18_start_capture(id); | ||
597 | mutex_unlock(&cx->serialize_lock); | ||
598 | if (rc) | ||
599 | return rc; | ||
600 | |||
601 | if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
602 | (id->type == CX18_ENC_STREAM_TYPE_YUV)) { | ||
603 | return videobuf_read_stream(&s->vbuf_q, buf, count, pos, 0, | ||
604 | filp->f_flags & O_NONBLOCK); | ||
605 | } | ||
606 | |||
607 | return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); | ||
608 | } | ||
609 | |||
610 | unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) | ||
611 | { | ||
612 | struct cx18_open_id *id = file2id(filp); | ||
613 | struct cx18 *cx = id->cx; | ||
614 | struct cx18_stream *s = &cx->streams[id->type]; | ||
615 | int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags); | ||
616 | |||
617 | /* Start a capture if there is none */ | ||
618 | if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) { | ||
619 | int rc; | ||
620 | |||
621 | mutex_lock(&cx->serialize_lock); | ||
622 | rc = cx18_start_capture(id); | ||
623 | mutex_unlock(&cx->serialize_lock); | ||
624 | if (rc) { | ||
625 | CX18_DEBUG_INFO("Could not start capture for %s (%d)\n", | ||
626 | s->name, rc); | ||
627 | return POLLERR; | ||
628 | } | ||
629 | CX18_DEBUG_FILE("Encoder poll started capture\n"); | ||
630 | } | ||
631 | |||
632 | if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
633 | (id->type == CX18_ENC_STREAM_TYPE_YUV)) { | ||
634 | int videobuf_poll = videobuf_poll_stream(filp, &s->vbuf_q, wait); | ||
635 | if (eof && videobuf_poll == POLLERR) | ||
636 | return POLLHUP; | ||
637 | else | ||
638 | return videobuf_poll; | ||
639 | } | ||
640 | |||
641 | /* add stream's waitq to the poll list */ | ||
642 | CX18_DEBUG_HI_FILE("Encoder poll\n"); | ||
643 | poll_wait(filp, &s->waitq, wait); | ||
644 | |||
645 | if (atomic_read(&s->q_full.depth)) | ||
646 | return POLLIN | POLLRDNORM; | ||
647 | if (eof) | ||
648 | return POLLHUP; | ||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma) | ||
653 | { | ||
654 | struct cx18_open_id *id = file->private_data; | ||
655 | struct cx18 *cx = id->cx; | ||
656 | struct cx18_stream *s = &cx->streams[id->type]; | ||
657 | int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags); | ||
658 | |||
659 | if ((s->vb_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
660 | (id->type == CX18_ENC_STREAM_TYPE_YUV)) { | ||
661 | |||
662 | /* Start a capture if there is none */ | ||
663 | if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) { | ||
664 | int rc; | ||
665 | |||
666 | mutex_lock(&cx->serialize_lock); | ||
667 | rc = cx18_start_capture(id); | ||
668 | mutex_unlock(&cx->serialize_lock); | ||
669 | if (rc) { | ||
670 | CX18_DEBUG_INFO( | ||
671 | "Could not start capture for %s (%d)\n", | ||
672 | s->name, rc); | ||
673 | return -EINVAL; | ||
674 | } | ||
675 | CX18_DEBUG_FILE("Encoder mmap started capture\n"); | ||
676 | } | ||
677 | |||
678 | return videobuf_mmap_mapper(&s->vbuf_q, vma); | ||
679 | } | ||
680 | |||
681 | return -EINVAL; | ||
682 | } | ||
683 | |||
684 | void cx18_vb_timeout(unsigned long data) | ||
685 | { | ||
686 | struct cx18_stream *s = (struct cx18_stream *)data; | ||
687 | struct cx18_videobuf_buffer *buf; | ||
688 | unsigned long flags; | ||
689 | |||
690 | /* Return all of the buffers in error state, so the vbi/vid inode | ||
691 | * can return from blocking. | ||
692 | */ | ||
693 | spin_lock_irqsave(&s->vb_lock, flags); | ||
694 | while (!list_empty(&s->vb_capture)) { | ||
695 | buf = list_entry(s->vb_capture.next, | ||
696 | struct cx18_videobuf_buffer, vb.queue); | ||
697 | list_del(&buf->vb.queue); | ||
698 | buf->vb.state = VIDEOBUF_ERROR; | ||
699 | wake_up(&buf->vb.done); | ||
700 | } | ||
701 | spin_unlock_irqrestore(&s->vb_lock, flags); | ||
702 | } | ||
703 | |||
704 | void cx18_stop_capture(struct cx18_open_id *id, int gop_end) | ||
705 | { | ||
706 | struct cx18 *cx = id->cx; | ||
707 | struct cx18_stream *s = &cx->streams[id->type]; | ||
708 | struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; | ||
709 | struct cx18_stream *s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; | ||
710 | |||
711 | CX18_DEBUG_IOCTL("close() of %s\n", s->name); | ||
712 | |||
713 | /* 'Unclaim' this stream */ | ||
714 | |||
715 | /* Stop capturing */ | ||
716 | if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) { | ||
717 | CX18_DEBUG_INFO("close stopping capture\n"); | ||
718 | if (id->type == CX18_ENC_STREAM_TYPE_MPG) { | ||
719 | /* Stop internal use associated VBI and IDX streams */ | ||
720 | if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) && | ||
721 | !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { | ||
722 | CX18_DEBUG_INFO("close stopping embedded VBI " | ||
723 | "capture\n"); | ||
724 | cx18_stop_v4l2_encode_stream(s_vbi, 0); | ||
725 | } | ||
726 | if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) { | ||
727 | CX18_DEBUG_INFO("close stopping IDX capture\n"); | ||
728 | cx18_stop_v4l2_encode_stream(s_idx, 0); | ||
729 | } | ||
730 | } | ||
731 | if (id->type == CX18_ENC_STREAM_TYPE_VBI && | ||
732 | test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) | ||
733 | /* Also used internally, don't stop capturing */ | ||
734 | s->id = -1; | ||
735 | else | ||
736 | cx18_stop_v4l2_encode_stream(s, gop_end); | ||
737 | } | ||
738 | if (!gop_end) { | ||
739 | clear_bit(CX18_F_S_APPL_IO, &s->s_flags); | ||
740 | clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); | ||
741 | cx18_release_stream(s); | ||
742 | } | ||
743 | } | ||
744 | |||
745 | int cx18_v4l2_close(struct file *filp) | ||
746 | { | ||
747 | struct v4l2_fh *fh = filp->private_data; | ||
748 | struct cx18_open_id *id = fh2id(fh); | ||
749 | struct cx18 *cx = id->cx; | ||
750 | struct cx18_stream *s = &cx->streams[id->type]; | ||
751 | |||
752 | CX18_DEBUG_IOCTL("close() of %s\n", s->name); | ||
753 | |||
754 | v4l2_fh_del(fh); | ||
755 | v4l2_fh_exit(fh); | ||
756 | |||
757 | /* Easy case first: this stream was never claimed by us */ | ||
758 | if (s->id != id->open_id) { | ||
759 | kfree(id); | ||
760 | return 0; | ||
761 | } | ||
762 | |||
763 | /* 'Unclaim' this stream */ | ||
764 | |||
765 | /* Stop radio */ | ||
766 | mutex_lock(&cx->serialize_lock); | ||
767 | if (id->type == CX18_ENC_STREAM_TYPE_RAD) { | ||
768 | /* Closing radio device, return to TV mode */ | ||
769 | cx18_mute(cx); | ||
770 | /* Mark that the radio is no longer in use */ | ||
771 | clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags); | ||
772 | /* Switch tuner to TV */ | ||
773 | cx18_call_all(cx, core, s_std, cx->std); | ||
774 | /* Select correct audio input (i.e. TV tuner or Line in) */ | ||
775 | cx18_audio_set_io(cx); | ||
776 | if (atomic_read(&cx->ana_capturing) > 0) { | ||
777 | /* Undo video mute */ | ||
778 | cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, | ||
779 | (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute) | | ||
780 | (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8))); | ||
781 | } | ||
782 | /* Done! Unmute and continue. */ | ||
783 | cx18_unmute(cx); | ||
784 | cx18_release_stream(s); | ||
785 | } else { | ||
786 | cx18_stop_capture(id, 0); | ||
787 | if (id->type == CX18_ENC_STREAM_TYPE_YUV) | ||
788 | videobuf_mmap_free(&id->vbuf_q); | ||
789 | } | ||
790 | kfree(id); | ||
791 | mutex_unlock(&cx->serialize_lock); | ||
792 | return 0; | ||
793 | } | ||
794 | |||
795 | static int cx18_serialized_open(struct cx18_stream *s, struct file *filp) | ||
796 | { | ||
797 | struct cx18 *cx = s->cx; | ||
798 | struct cx18_open_id *item; | ||
799 | |||
800 | CX18_DEBUG_FILE("open %s\n", s->name); | ||
801 | |||
802 | /* Allocate memory */ | ||
803 | item = kzalloc(sizeof(struct cx18_open_id), GFP_KERNEL); | ||
804 | if (NULL == item) { | ||
805 | CX18_DEBUG_WARN("nomem on v4l2 open\n"); | ||
806 | return -ENOMEM; | ||
807 | } | ||
808 | v4l2_fh_init(&item->fh, s->video_dev); | ||
809 | |||
810 | item->cx = cx; | ||
811 | item->type = s->type; | ||
812 | |||
813 | item->open_id = cx->open_id++; | ||
814 | filp->private_data = &item->fh; | ||
815 | |||
816 | if (item->type == CX18_ENC_STREAM_TYPE_RAD) { | ||
817 | /* Try to claim this stream */ | ||
818 | if (cx18_claim_stream(item, item->type)) { | ||
819 | /* No, it's already in use */ | ||
820 | v4l2_fh_exit(&item->fh); | ||
821 | kfree(item); | ||
822 | return -EBUSY; | ||
823 | } | ||
824 | |||
825 | if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) { | ||
826 | if (atomic_read(&cx->ana_capturing) > 0) { | ||
827 | /* switching to radio while capture is | ||
828 | in progress is not polite */ | ||
829 | cx18_release_stream(s); | ||
830 | v4l2_fh_exit(&item->fh); | ||
831 | kfree(item); | ||
832 | return -EBUSY; | ||
833 | } | ||
834 | } | ||
835 | |||
836 | /* Mark that the radio is being used. */ | ||
837 | set_bit(CX18_F_I_RADIO_USER, &cx->i_flags); | ||
838 | /* We have the radio */ | ||
839 | cx18_mute(cx); | ||
840 | /* Switch tuner to radio */ | ||
841 | cx18_call_all(cx, tuner, s_radio); | ||
842 | /* Select the correct audio input (i.e. radio tuner) */ | ||
843 | cx18_audio_set_io(cx); | ||
844 | /* Done! Unmute and continue. */ | ||
845 | cx18_unmute(cx); | ||
846 | } | ||
847 | v4l2_fh_add(&item->fh); | ||
848 | return 0; | ||
849 | } | ||
850 | |||
851 | int cx18_v4l2_open(struct file *filp) | ||
852 | { | ||
853 | int res; | ||
854 | struct video_device *video_dev = video_devdata(filp); | ||
855 | struct cx18_stream *s = video_get_drvdata(video_dev); | ||
856 | struct cx18 *cx = s->cx; | ||
857 | |||
858 | mutex_lock(&cx->serialize_lock); | ||
859 | if (cx18_init_on_first_open(cx)) { | ||
860 | CX18_ERR("Failed to initialize on %s\n", | ||
861 | video_device_node_name(video_dev)); | ||
862 | mutex_unlock(&cx->serialize_lock); | ||
863 | return -ENXIO; | ||
864 | } | ||
865 | res = cx18_serialized_open(s, filp); | ||
866 | mutex_unlock(&cx->serialize_lock); | ||
867 | return res; | ||
868 | } | ||
869 | |||
870 | void cx18_mute(struct cx18 *cx) | ||
871 | { | ||
872 | u32 h; | ||
873 | if (atomic_read(&cx->ana_capturing)) { | ||
874 | h = cx18_find_handle(cx); | ||
875 | if (h != CX18_INVALID_TASK_HANDLE) | ||
876 | cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 1); | ||
877 | else | ||
878 | CX18_ERR("Can't find valid task handle for mute\n"); | ||
879 | } | ||
880 | CX18_DEBUG_INFO("Mute\n"); | ||
881 | } | ||
882 | |||
883 | void cx18_unmute(struct cx18 *cx) | ||
884 | { | ||
885 | u32 h; | ||
886 | if (atomic_read(&cx->ana_capturing)) { | ||
887 | h = cx18_find_handle(cx); | ||
888 | if (h != CX18_INVALID_TASK_HANDLE) { | ||
889 | cx18_msleep_timeout(100, 0); | ||
890 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, h, 12); | ||
891 | cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, h, 0); | ||
892 | } else | ||
893 | CX18_ERR("Can't find valid task handle for unmute\n"); | ||
894 | } | ||
895 | CX18_DEBUG_INFO("Unmute\n"); | ||
896 | } | ||
diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h new file mode 100644 index 00000000000..b9e5110ad04 --- /dev/null +++ b/drivers/media/video/cx18/cx18-fileops.h | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * cx18 file operation functions | ||
3 | * | ||
4 | * Derived from ivtv-fileops.h | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
21 | * 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | /* Testing/Debugging */ | ||
25 | int cx18_v4l2_open(struct file *filp); | ||
26 | ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count, | ||
27 | loff_t *pos); | ||
28 | ssize_t cx18_v4l2_write(struct file *filp, const char __user *buf, size_t count, | ||
29 | loff_t *pos); | ||
30 | int cx18_v4l2_close(struct file *filp); | ||
31 | unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait); | ||
32 | int cx18_start_capture(struct cx18_open_id *id); | ||
33 | void cx18_stop_capture(struct cx18_open_id *id, int gop_end); | ||
34 | void cx18_mute(struct cx18 *cx); | ||
35 | void cx18_unmute(struct cx18 *cx); | ||
36 | int cx18_v4l2_mmap(struct file *file, struct vm_area_struct *vma); | ||
37 | void cx18_vb_timeout(unsigned long data); | ||
38 | |||
39 | /* Shared with cx18-alsa module */ | ||
40 | int cx18_claim_stream(struct cx18_open_id *id, int type); | ||
41 | void cx18_release_stream(struct cx18_stream *s); | ||
diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c new file mode 100644 index 00000000000..1b3fb502e6b --- /dev/null +++ b/drivers/media/video/cx18/cx18-firmware.c | |||
@@ -0,0 +1,448 @@ | |||
1 | /* | ||
2 | * cx18 firmware functions | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include "cx18-driver.h" | ||
24 | #include "cx18-io.h" | ||
25 | #include "cx18-scb.h" | ||
26 | #include "cx18-irq.h" | ||
27 | #include "cx18-firmware.h" | ||
28 | #include "cx18-cards.h" | ||
29 | #include <linux/firmware.h> | ||
30 | |||
31 | #define CX18_PROC_SOFT_RESET 0xc70010 | ||
32 | #define CX18_DDR_SOFT_RESET 0xc70014 | ||
33 | #define CX18_CLOCK_SELECT1 0xc71000 | ||
34 | #define CX18_CLOCK_SELECT2 0xc71004 | ||
35 | #define CX18_HALF_CLOCK_SELECT1 0xc71008 | ||
36 | #define CX18_HALF_CLOCK_SELECT2 0xc7100C | ||
37 | #define CX18_CLOCK_POLARITY1 0xc71010 | ||
38 | #define CX18_CLOCK_POLARITY2 0xc71014 | ||
39 | #define CX18_ADD_DELAY_ENABLE1 0xc71018 | ||
40 | #define CX18_ADD_DELAY_ENABLE2 0xc7101C | ||
41 | #define CX18_CLOCK_ENABLE1 0xc71020 | ||
42 | #define CX18_CLOCK_ENABLE2 0xc71024 | ||
43 | |||
44 | #define CX18_REG_BUS_TIMEOUT_EN 0xc72024 | ||
45 | |||
46 | #define CX18_FAST_CLOCK_PLL_INT 0xc78000 | ||
47 | #define CX18_FAST_CLOCK_PLL_FRAC 0xc78004 | ||
48 | #define CX18_FAST_CLOCK_PLL_POST 0xc78008 | ||
49 | #define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C | ||
50 | #define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010 | ||
51 | |||
52 | #define CX18_SLOW_CLOCK_PLL_INT 0xc78014 | ||
53 | #define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018 | ||
54 | #define CX18_SLOW_CLOCK_PLL_POST 0xc7801C | ||
55 | #define CX18_MPEG_CLOCK_PLL_INT 0xc78040 | ||
56 | #define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044 | ||
57 | #define CX18_MPEG_CLOCK_PLL_POST 0xc78048 | ||
58 | #define CX18_PLL_POWER_DOWN 0xc78088 | ||
59 | #define CX18_SW1_INT_STATUS 0xc73104 | ||
60 | #define CX18_SW1_INT_ENABLE_PCI 0xc7311C | ||
61 | #define CX18_SW2_INT_SET 0xc73140 | ||
62 | #define CX18_SW2_INT_STATUS 0xc73144 | ||
63 | #define CX18_ADEC_CONTROL 0xc78120 | ||
64 | |||
65 | #define CX18_DDR_REQUEST_ENABLE 0xc80000 | ||
66 | #define CX18_DDR_CHIP_CONFIG 0xc80004 | ||
67 | #define CX18_DDR_REFRESH 0xc80008 | ||
68 | #define CX18_DDR_TIMING1 0xc8000C | ||
69 | #define CX18_DDR_TIMING2 0xc80010 | ||
70 | #define CX18_DDR_POWER_REG 0xc8001C | ||
71 | |||
72 | #define CX18_DDR_TUNE_LANE 0xc80048 | ||
73 | #define CX18_DDR_INITIAL_EMRS 0xc80054 | ||
74 | #define CX18_DDR_MB_PER_ROW_7 0xc8009C | ||
75 | #define CX18_DDR_BASE_63_ADDR 0xc804FC | ||
76 | |||
77 | #define CX18_WMB_CLIENT02 0xc90108 | ||
78 | #define CX18_WMB_CLIENT05 0xc90114 | ||
79 | #define CX18_WMB_CLIENT06 0xc90118 | ||
80 | #define CX18_WMB_CLIENT07 0xc9011C | ||
81 | #define CX18_WMB_CLIENT08 0xc90120 | ||
82 | #define CX18_WMB_CLIENT09 0xc90124 | ||
83 | #define CX18_WMB_CLIENT10 0xc90128 | ||
84 | #define CX18_WMB_CLIENT11 0xc9012C | ||
85 | #define CX18_WMB_CLIENT12 0xc90130 | ||
86 | #define CX18_WMB_CLIENT13 0xc90134 | ||
87 | #define CX18_WMB_CLIENT14 0xc90138 | ||
88 | |||
89 | #define CX18_DSP0_INTERRUPT_MASK 0xd0004C | ||
90 | |||
91 | #define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */ | ||
92 | #define APU_ROM_SYNC2 0x72646548 /* "rdeH" */ | ||
93 | |||
94 | struct cx18_apu_rom_seghdr { | ||
95 | u32 sync1; | ||
96 | u32 sync2; | ||
97 | u32 addr; | ||
98 | u32 size; | ||
99 | }; | ||
100 | |||
101 | static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx) | ||
102 | { | ||
103 | const struct firmware *fw = NULL; | ||
104 | int i, j; | ||
105 | unsigned size; | ||
106 | u32 __iomem *dst = (u32 __iomem *)mem; | ||
107 | const u32 *src; | ||
108 | |||
109 | if (request_firmware(&fw, fn, &cx->pci_dev->dev)) { | ||
110 | CX18_ERR("Unable to open firmware %s\n", fn); | ||
111 | CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n"); | ||
112 | return -ENOMEM; | ||
113 | } | ||
114 | |||
115 | src = (const u32 *)fw->data; | ||
116 | |||
117 | for (i = 0; i < fw->size; i += 4096) { | ||
118 | cx18_setup_page(cx, i); | ||
119 | for (j = i; j < fw->size && j < i + 4096; j += 4) { | ||
120 | /* no need for endianness conversion on the ppc */ | ||
121 | cx18_raw_writel(cx, *src, dst); | ||
122 | if (cx18_raw_readl(cx, dst) != *src) { | ||
123 | CX18_ERR("Mismatch at offset %x\n", i); | ||
124 | release_firmware(fw); | ||
125 | cx18_setup_page(cx, 0); | ||
126 | return -EIO; | ||
127 | } | ||
128 | dst++; | ||
129 | src++; | ||
130 | } | ||
131 | } | ||
132 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) | ||
133 | CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); | ||
134 | size = fw->size; | ||
135 | release_firmware(fw); | ||
136 | cx18_setup_page(cx, SCB_OFFSET); | ||
137 | return size; | ||
138 | } | ||
139 | |||
140 | static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, | ||
141 | u32 *entry_addr) | ||
142 | { | ||
143 | const struct firmware *fw = NULL; | ||
144 | int i, j; | ||
145 | unsigned size; | ||
146 | const u32 *src; | ||
147 | struct cx18_apu_rom_seghdr seghdr; | ||
148 | const u8 *vers; | ||
149 | u32 offset = 0; | ||
150 | u32 apu_version = 0; | ||
151 | int sz; | ||
152 | |||
153 | if (request_firmware(&fw, fn, &cx->pci_dev->dev)) { | ||
154 | CX18_ERR("unable to open firmware %s\n", fn); | ||
155 | CX18_ERR("did you put the firmware in the hotplug firmware directory?\n"); | ||
156 | cx18_setup_page(cx, 0); | ||
157 | return -ENOMEM; | ||
158 | } | ||
159 | |||
160 | *entry_addr = 0; | ||
161 | src = (const u32 *)fw->data; | ||
162 | vers = fw->data + sizeof(seghdr); | ||
163 | sz = fw->size; | ||
164 | |||
165 | apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32]; | ||
166 | while (offset + sizeof(seghdr) < fw->size) { | ||
167 | /* TODO: byteswapping */ | ||
168 | memcpy(&seghdr, src + offset / 4, sizeof(seghdr)); | ||
169 | offset += sizeof(seghdr); | ||
170 | if (seghdr.sync1 != APU_ROM_SYNC1 || | ||
171 | seghdr.sync2 != APU_ROM_SYNC2) { | ||
172 | offset += seghdr.size; | ||
173 | continue; | ||
174 | } | ||
175 | CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr, | ||
176 | seghdr.addr + seghdr.size - 1); | ||
177 | if (*entry_addr == 0) | ||
178 | *entry_addr = seghdr.addr; | ||
179 | if (offset + seghdr.size > sz) | ||
180 | break; | ||
181 | for (i = 0; i < seghdr.size; i += 4096) { | ||
182 | cx18_setup_page(cx, seghdr.addr + i); | ||
183 | for (j = i; j < seghdr.size && j < i + 4096; j += 4) { | ||
184 | /* no need for endianness conversion on the ppc */ | ||
185 | cx18_raw_writel(cx, src[(offset + j) / 4], | ||
186 | dst + seghdr.addr + j); | ||
187 | if (cx18_raw_readl(cx, dst + seghdr.addr + j) | ||
188 | != src[(offset + j) / 4]) { | ||
189 | CX18_ERR("Mismatch at offset %x\n", | ||
190 | offset + j); | ||
191 | release_firmware(fw); | ||
192 | cx18_setup_page(cx, 0); | ||
193 | return -EIO; | ||
194 | } | ||
195 | } | ||
196 | } | ||
197 | offset += seghdr.size; | ||
198 | } | ||
199 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags)) | ||
200 | CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n", | ||
201 | fn, apu_version, fw->size); | ||
202 | size = fw->size; | ||
203 | release_firmware(fw); | ||
204 | cx18_setup_page(cx, 0); | ||
205 | return size; | ||
206 | } | ||
207 | |||
208 | void cx18_halt_firmware(struct cx18 *cx) | ||
209 | { | ||
210 | CX18_DEBUG_INFO("Preparing for firmware halt.\n"); | ||
211 | cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET, | ||
212 | 0x0000000F, 0x000F000F); | ||
213 | cx18_write_reg_expect(cx, 0x00020002, CX18_ADEC_CONTROL, | ||
214 | 0x00000002, 0x00020002); | ||
215 | } | ||
216 | |||
217 | void cx18_init_power(struct cx18 *cx, int lowpwr) | ||
218 | { | ||
219 | /* power-down Spare and AOM PLLs */ | ||
220 | /* power-up fast, slow and mpeg PLLs */ | ||
221 | cx18_write_reg(cx, 0x00000008, CX18_PLL_POWER_DOWN); | ||
222 | |||
223 | /* ADEC out of sleep */ | ||
224 | cx18_write_reg_expect(cx, 0x00020000, CX18_ADEC_CONTROL, | ||
225 | 0x00000000, 0x00020002); | ||
226 | |||
227 | /* | ||
228 | * The PLL parameters are based on the external crystal frequency that | ||
229 | * would ideally be: | ||
230 | * | ||
231 | * NTSC Color subcarrier freq * 8 = | ||
232 | * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz | ||
233 | * | ||
234 | * The accidents of history and rationale that explain from where this | ||
235 | * combination of magic numbers originate can be found in: | ||
236 | * | ||
237 | * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in | ||
238 | * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80 | ||
239 | * | ||
240 | * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the | ||
241 | * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83 | ||
242 | * | ||
243 | * As Mike Bradley has rightly pointed out, it's not the exact crystal | ||
244 | * frequency that matters, only that all parts of the driver and | ||
245 | * firmware are using the same value (close to the ideal value). | ||
246 | * | ||
247 | * Since I have a strong suspicion that, if the firmware ever assumes a | ||
248 | * crystal value at all, it will assume 28.636360 MHz, the crystal | ||
249 | * freq used in calculations in this driver will be: | ||
250 | * | ||
251 | * xtal_freq = 28.636360 MHz | ||
252 | * | ||
253 | * an error of less than 0.13 ppm which is way, way better than any off | ||
254 | * the shelf crystal will have for accuracy anyway. | ||
255 | * | ||
256 | * Below I aim to run the PLLs' VCOs near 400 MHz to minimze errors. | ||
257 | * | ||
258 | * Many thanks to Jeff Campbell and Mike Bradley for their extensive | ||
259 | * investigation, experimentation, testing, and suggested solutions of | ||
260 | * of audio/video sync problems with SVideo and CVBS captures. | ||
261 | */ | ||
262 | |||
263 | /* the fast clock is at 200/245 MHz */ | ||
264 | /* 1 * xtal_freq * 0x0d.f7df9b8 / 2 = 200 MHz: 400 MHz pre post-divide*/ | ||
265 | /* 1 * xtal_freq * 0x11.1c71eb8 / 2 = 245 MHz: 490 MHz pre post-divide*/ | ||
266 | cx18_write_reg(cx, lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT); | ||
267 | cx18_write_reg(cx, lowpwr ? 0x1EFBF37 : 0x038E3D7, | ||
268 | CX18_FAST_CLOCK_PLL_FRAC); | ||
269 | |||
270 | cx18_write_reg(cx, 2, CX18_FAST_CLOCK_PLL_POST); | ||
271 | cx18_write_reg(cx, 1, CX18_FAST_CLOCK_PLL_PRESCALE); | ||
272 | cx18_write_reg(cx, 4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH); | ||
273 | |||
274 | /* set slow clock to 125/120 MHz */ | ||
275 | /* xtal_freq * 0x0d.1861a20 / 3 = 125 MHz: 375 MHz before post-divide */ | ||
276 | /* xtal_freq * 0x0c.92493f8 / 3 = 120 MHz: 360 MHz before post-divide */ | ||
277 | cx18_write_reg(cx, lowpwr ? 0xD : 0xC, CX18_SLOW_CLOCK_PLL_INT); | ||
278 | cx18_write_reg(cx, lowpwr ? 0x30C344 : 0x124927F, | ||
279 | CX18_SLOW_CLOCK_PLL_FRAC); | ||
280 | cx18_write_reg(cx, 3, CX18_SLOW_CLOCK_PLL_POST); | ||
281 | |||
282 | /* mpeg clock pll 54MHz */ | ||
283 | /* xtal_freq * 0xf.15f17f0 / 8 = 54 MHz: 432 MHz before post-divide */ | ||
284 | cx18_write_reg(cx, 0xF, CX18_MPEG_CLOCK_PLL_INT); | ||
285 | cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC); | ||
286 | cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST); | ||
287 | |||
288 | /* Defaults */ | ||
289 | /* APU = SC or SC/2 = 125/62.5 */ | ||
290 | /* EPU = SC = 125 */ | ||
291 | /* DDR = FC = 180 */ | ||
292 | /* ENC = SC = 125 */ | ||
293 | /* AI1 = SC = 125 */ | ||
294 | /* VIM2 = disabled */ | ||
295 | /* PCI = FC/2 = 90 */ | ||
296 | /* AI2 = disabled */ | ||
297 | /* DEMUX = disabled */ | ||
298 | /* AO = SC/2 = 62.5 */ | ||
299 | /* SER = 54MHz */ | ||
300 | /* VFC = disabled */ | ||
301 | /* USB = disabled */ | ||
302 | |||
303 | if (lowpwr) { | ||
304 | cx18_write_reg_expect(cx, 0xFFFF0020, CX18_CLOCK_SELECT1, | ||
305 | 0x00000020, 0xFFFFFFFF); | ||
306 | cx18_write_reg_expect(cx, 0xFFFF0004, CX18_CLOCK_SELECT2, | ||
307 | 0x00000004, 0xFFFFFFFF); | ||
308 | } else { | ||
309 | /* This doesn't explicitly set every clock select */ | ||
310 | cx18_write_reg_expect(cx, 0x00060004, CX18_CLOCK_SELECT1, | ||
311 | 0x00000004, 0x00060006); | ||
312 | cx18_write_reg_expect(cx, 0x00060006, CX18_CLOCK_SELECT2, | ||
313 | 0x00000006, 0x00060006); | ||
314 | } | ||
315 | |||
316 | cx18_write_reg_expect(cx, 0xFFFF0002, CX18_HALF_CLOCK_SELECT1, | ||
317 | 0x00000002, 0xFFFFFFFF); | ||
318 | cx18_write_reg_expect(cx, 0xFFFF0104, CX18_HALF_CLOCK_SELECT2, | ||
319 | 0x00000104, 0xFFFFFFFF); | ||
320 | cx18_write_reg_expect(cx, 0xFFFF9026, CX18_CLOCK_ENABLE1, | ||
321 | 0x00009026, 0xFFFFFFFF); | ||
322 | cx18_write_reg_expect(cx, 0xFFFF3105, CX18_CLOCK_ENABLE2, | ||
323 | 0x00003105, 0xFFFFFFFF); | ||
324 | } | ||
325 | |||
326 | void cx18_init_memory(struct cx18 *cx) | ||
327 | { | ||
328 | cx18_msleep_timeout(10, 0); | ||
329 | cx18_write_reg_expect(cx, 0x00010000, CX18_DDR_SOFT_RESET, | ||
330 | 0x00000000, 0x00010001); | ||
331 | cx18_msleep_timeout(10, 0); | ||
332 | |||
333 | cx18_write_reg(cx, cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG); | ||
334 | |||
335 | cx18_msleep_timeout(10, 0); | ||
336 | |||
337 | cx18_write_reg(cx, cx->card->ddr.refresh, CX18_DDR_REFRESH); | ||
338 | cx18_write_reg(cx, cx->card->ddr.timing1, CX18_DDR_TIMING1); | ||
339 | cx18_write_reg(cx, cx->card->ddr.timing2, CX18_DDR_TIMING2); | ||
340 | |||
341 | cx18_msleep_timeout(10, 0); | ||
342 | |||
343 | /* Initialize DQS pad time */ | ||
344 | cx18_write_reg(cx, cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE); | ||
345 | cx18_write_reg(cx, cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS); | ||
346 | |||
347 | cx18_msleep_timeout(10, 0); | ||
348 | |||
349 | cx18_write_reg_expect(cx, 0x00020000, CX18_DDR_SOFT_RESET, | ||
350 | 0x00000000, 0x00020002); | ||
351 | cx18_msleep_timeout(10, 0); | ||
352 | |||
353 | /* use power-down mode when idle */ | ||
354 | cx18_write_reg(cx, 0x00000010, CX18_DDR_POWER_REG); | ||
355 | |||
356 | cx18_write_reg_expect(cx, 0x00010001, CX18_REG_BUS_TIMEOUT_EN, | ||
357 | 0x00000001, 0x00010001); | ||
358 | |||
359 | cx18_write_reg(cx, 0x48, CX18_DDR_MB_PER_ROW_7); | ||
360 | cx18_write_reg(cx, 0xE0000, CX18_DDR_BASE_63_ADDR); | ||
361 | |||
362 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT02); /* AO */ | ||
363 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT09); /* AI2 */ | ||
364 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT05); /* VIM1 */ | ||
365 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT06); /* AI1 */ | ||
366 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT07); /* 3D comb */ | ||
367 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT10); /* ME */ | ||
368 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT12); /* ENC */ | ||
369 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT13); /* PK */ | ||
370 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT11); /* RC */ | ||
371 | cx18_write_reg(cx, 0x00000101, CX18_WMB_CLIENT14); /* AVO */ | ||
372 | } | ||
373 | |||
374 | int cx18_firmware_init(struct cx18 *cx) | ||
375 | { | ||
376 | u32 fw_entry_addr; | ||
377 | int sz, retries; | ||
378 | u32 api_args[MAX_MB_ARGUMENTS]; | ||
379 | |||
380 | /* Allow chip to control CLKRUN */ | ||
381 | cx18_write_reg(cx, 0x5, CX18_DSP0_INTERRUPT_MASK); | ||
382 | |||
383 | /* Stop the firmware */ | ||
384 | cx18_write_reg_expect(cx, 0x000F000F, CX18_PROC_SOFT_RESET, | ||
385 | 0x0000000F, 0x000F000F); | ||
386 | |||
387 | cx18_msleep_timeout(1, 0); | ||
388 | |||
389 | /* If the CPU is still running */ | ||
390 | if ((cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 8) == 0) { | ||
391 | CX18_ERR("%s: couldn't stop CPU to load firmware\n", __func__); | ||
392 | return -EIO; | ||
393 | } | ||
394 | |||
395 | cx18_sw1_irq_enable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); | ||
396 | cx18_sw2_irq_enable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | ||
397 | |||
398 | sz = load_cpu_fw_direct("v4l-cx23418-cpu.fw", cx->enc_mem, cx); | ||
399 | if (sz <= 0) | ||
400 | return sz; | ||
401 | |||
402 | /* The SCB & IPC area *must* be correct before starting the firmwares */ | ||
403 | cx18_init_scb(cx); | ||
404 | |||
405 | fw_entry_addr = 0; | ||
406 | sz = load_apu_fw_direct("v4l-cx23418-apu.fw", cx->enc_mem, cx, | ||
407 | &fw_entry_addr); | ||
408 | if (sz <= 0) | ||
409 | return sz; | ||
410 | |||
411 | /* Start the CPU. The CPU will take care of the APU for us. */ | ||
412 | cx18_write_reg_expect(cx, 0x00080000, CX18_PROC_SOFT_RESET, | ||
413 | 0x00000000, 0x00080008); | ||
414 | |||
415 | /* Wait up to 500 ms for the APU to come out of reset */ | ||
416 | for (retries = 0; | ||
417 | retries < 50 && (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1; | ||
418 | retries++) | ||
419 | cx18_msleep_timeout(10, 0); | ||
420 | |||
421 | cx18_msleep_timeout(200, 0); | ||
422 | |||
423 | if (retries == 50 && | ||
424 | (cx18_read_reg(cx, CX18_PROC_SOFT_RESET) & 1) == 1) { | ||
425 | CX18_ERR("Could not start the CPU\n"); | ||
426 | return -EIO; | ||
427 | } | ||
428 | |||
429 | /* | ||
430 | * The CPU had once before set up to receive an interrupt for it's | ||
431 | * outgoing IRQ_CPU_TO_EPU_ACK to us. If it ever does this, we get an | ||
432 | * interrupt when it sends us an ack, but by the time we process it, | ||
433 | * that flag in the SW2 status register has been cleared by the CPU | ||
434 | * firmware. We'll prevent that not so useful condition from happening | ||
435 | * by clearing the CPU's interrupt enables for Ack IRQ's we want to | ||
436 | * process. | ||
437 | */ | ||
438 | cx18_sw2_irq_disable_cpu(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); | ||
439 | |||
440 | /* Try a benign command to see if the CPU is alive and well */ | ||
441 | sz = cx18_vapi_result(cx, api_args, CX18_CPU_DEBUG_PEEK32, 1, 0); | ||
442 | if (sz < 0) | ||
443 | return sz; | ||
444 | |||
445 | /* initialize GPIO */ | ||
446 | cx18_write_reg_expect(cx, 0x14001400, 0xc78110, 0x00001400, 0x14001400); | ||
447 | return 0; | ||
448 | } | ||
diff --git a/drivers/media/video/cx18/cx18-firmware.h b/drivers/media/video/cx18/cx18-firmware.h new file mode 100644 index 00000000000..38d4c05e849 --- /dev/null +++ b/drivers/media/video/cx18/cx18-firmware.h | |||
@@ -0,0 +1,25 @@ | |||
1 | /* | ||
2 | * cx18 firmware functions | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
19 | * 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | int cx18_firmware_init(struct cx18 *cx); | ||
23 | void cx18_halt_firmware(struct cx18 *cx); | ||
24 | void cx18_init_memory(struct cx18 *cx); | ||
25 | void cx18_init_power(struct cx18 *cx, int lowpwr); | ||
diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c new file mode 100644 index 00000000000..5374aeb0cd2 --- /dev/null +++ b/drivers/media/video/cx18/cx18-gpio.c | |||
@@ -0,0 +1,347 @@ | |||
1 | /* | ||
2 | * cx18 gpio functions | ||
3 | * | ||
4 | * Derived from ivtv-gpio.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | #include "cx18-io.h" | ||
27 | #include "cx18-cards.h" | ||
28 | #include "cx18-gpio.h" | ||
29 | #include "tuner-xc2028.h" | ||
30 | |||
31 | /********************* GPIO stuffs *********************/ | ||
32 | |||
33 | /* GPIO registers */ | ||
34 | #define CX18_REG_GPIO_IN 0xc72010 | ||
35 | #define CX18_REG_GPIO_OUT1 0xc78100 | ||
36 | #define CX18_REG_GPIO_DIR1 0xc78108 | ||
37 | #define CX18_REG_GPIO_OUT2 0xc78104 | ||
38 | #define CX18_REG_GPIO_DIR2 0xc7810c | ||
39 | |||
40 | /* | ||
41 | * HVR-1600 GPIO pins, courtesy of Hauppauge: | ||
42 | * | ||
43 | * gpio0: zilog ir process reset pin | ||
44 | * gpio1: zilog programming pin (you should never use this) | ||
45 | * gpio12: cx24227 reset pin | ||
46 | * gpio13: cs5345 reset pin | ||
47 | */ | ||
48 | |||
49 | /* | ||
50 | * File scope utility functions | ||
51 | */ | ||
52 | static void gpio_write(struct cx18 *cx) | ||
53 | { | ||
54 | u32 dir_lo = cx->gpio_dir & 0xffff; | ||
55 | u32 val_lo = cx->gpio_val & 0xffff; | ||
56 | u32 dir_hi = cx->gpio_dir >> 16; | ||
57 | u32 val_hi = cx->gpio_val >> 16; | ||
58 | |||
59 | cx18_write_reg_expect(cx, dir_lo << 16, | ||
60 | CX18_REG_GPIO_DIR1, ~dir_lo, dir_lo); | ||
61 | cx18_write_reg_expect(cx, (dir_lo << 16) | val_lo, | ||
62 | CX18_REG_GPIO_OUT1, val_lo, dir_lo); | ||
63 | cx18_write_reg_expect(cx, dir_hi << 16, | ||
64 | CX18_REG_GPIO_DIR2, ~dir_hi, dir_hi); | ||
65 | cx18_write_reg_expect(cx, (dir_hi << 16) | val_hi, | ||
66 | CX18_REG_GPIO_OUT2, val_hi, dir_hi); | ||
67 | } | ||
68 | |||
69 | static void gpio_update(struct cx18 *cx, u32 mask, u32 data) | ||
70 | { | ||
71 | if (mask == 0) | ||
72 | return; | ||
73 | |||
74 | mutex_lock(&cx->gpio_lock); | ||
75 | cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask); | ||
76 | gpio_write(cx); | ||
77 | mutex_unlock(&cx->gpio_lock); | ||
78 | } | ||
79 | |||
80 | static void gpio_reset_seq(struct cx18 *cx, u32 active_lo, u32 active_hi, | ||
81 | unsigned int assert_msecs, | ||
82 | unsigned int recovery_msecs) | ||
83 | { | ||
84 | u32 mask; | ||
85 | |||
86 | mask = active_lo | active_hi; | ||
87 | if (mask == 0) | ||
88 | return; | ||
89 | |||
90 | /* | ||
91 | * Assuming that active_hi and active_lo are a subsets of the bits in | ||
92 | * gpio_dir. Also assumes that active_lo and active_hi don't overlap | ||
93 | * in any bit position | ||
94 | */ | ||
95 | |||
96 | /* Assert */ | ||
97 | gpio_update(cx, mask, ~active_lo); | ||
98 | schedule_timeout_uninterruptible(msecs_to_jiffies(assert_msecs)); | ||
99 | |||
100 | /* Deassert */ | ||
101 | gpio_update(cx, mask, ~active_hi); | ||
102 | schedule_timeout_uninterruptible(msecs_to_jiffies(recovery_msecs)); | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * GPIO Multiplexer - logical device | ||
107 | */ | ||
108 | static int gpiomux_log_status(struct v4l2_subdev *sd) | ||
109 | { | ||
110 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
111 | |||
112 | mutex_lock(&cx->gpio_lock); | ||
113 | CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n", | ||
114 | cx->gpio_dir, cx->gpio_val); | ||
115 | mutex_unlock(&cx->gpio_lock); | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static int gpiomux_s_radio(struct v4l2_subdev *sd) | ||
120 | { | ||
121 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
122 | |||
123 | /* | ||
124 | * FIXME - work out the cx->active/audio_input mess - this is | ||
125 | * intended to handle the switch to radio mode and set the | ||
126 | * audio routing, but we need to update the state in cx | ||
127 | */ | ||
128 | gpio_update(cx, cx->card->gpio_audio_input.mask, | ||
129 | cx->card->gpio_audio_input.radio); | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) | ||
134 | { | ||
135 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
136 | u32 data; | ||
137 | |||
138 | switch (cx->card->audio_inputs[cx->audio_input].muxer_input) { | ||
139 | case 1: | ||
140 | data = cx->card->gpio_audio_input.linein; | ||
141 | break; | ||
142 | case 0: | ||
143 | data = cx->card->gpio_audio_input.tuner; | ||
144 | break; | ||
145 | default: | ||
146 | /* | ||
147 | * FIXME - work out the cx->active/audio_input mess - this is | ||
148 | * intended to handle the switch from radio mode and set the | ||
149 | * audio routing, but we need to update the state in cx | ||
150 | */ | ||
151 | data = cx->card->gpio_audio_input.tuner; | ||
152 | break; | ||
153 | } | ||
154 | gpio_update(cx, cx->card->gpio_audio_input.mask, data); | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int gpiomux_s_audio_routing(struct v4l2_subdev *sd, | ||
159 | u32 input, u32 output, u32 config) | ||
160 | { | ||
161 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
162 | u32 data; | ||
163 | |||
164 | switch (input) { | ||
165 | case 0: | ||
166 | data = cx->card->gpio_audio_input.tuner; | ||
167 | break; | ||
168 | case 1: | ||
169 | data = cx->card->gpio_audio_input.linein; | ||
170 | break; | ||
171 | case 2: | ||
172 | data = cx->card->gpio_audio_input.radio; | ||
173 | break; | ||
174 | default: | ||
175 | return -EINVAL; | ||
176 | } | ||
177 | gpio_update(cx, cx->card->gpio_audio_input.mask, data); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static const struct v4l2_subdev_core_ops gpiomux_core_ops = { | ||
182 | .log_status = gpiomux_log_status, | ||
183 | .s_std = gpiomux_s_std, | ||
184 | }; | ||
185 | |||
186 | static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = { | ||
187 | .s_radio = gpiomux_s_radio, | ||
188 | }; | ||
189 | |||
190 | static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = { | ||
191 | .s_routing = gpiomux_s_audio_routing, | ||
192 | }; | ||
193 | |||
194 | static const struct v4l2_subdev_ops gpiomux_ops = { | ||
195 | .core = &gpiomux_core_ops, | ||
196 | .tuner = &gpiomux_tuner_ops, | ||
197 | .audio = &gpiomux_audio_ops, | ||
198 | }; | ||
199 | |||
200 | /* | ||
201 | * GPIO Reset Controller - logical device | ||
202 | */ | ||
203 | static int resetctrl_log_status(struct v4l2_subdev *sd) | ||
204 | { | ||
205 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
206 | |||
207 | mutex_lock(&cx->gpio_lock); | ||
208 | CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n", | ||
209 | cx->gpio_dir, cx->gpio_val); | ||
210 | mutex_unlock(&cx->gpio_lock); | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static int resetctrl_reset(struct v4l2_subdev *sd, u32 val) | ||
215 | { | ||
216 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
217 | const struct cx18_gpio_i2c_slave_reset *p; | ||
218 | |||
219 | p = &cx->card->gpio_i2c_slave_reset; | ||
220 | switch (val) { | ||
221 | case CX18_GPIO_RESET_I2C: | ||
222 | gpio_reset_seq(cx, p->active_lo_mask, p->active_hi_mask, | ||
223 | p->msecs_asserted, p->msecs_recovery); | ||
224 | break; | ||
225 | case CX18_GPIO_RESET_Z8F0811: | ||
226 | /* | ||
227 | * Assert timing for the Z8F0811 on HVR-1600 boards: | ||
228 | * 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to | ||
229 | * initiate | ||
230 | * 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock | ||
231 | * cycles (6,601,085 nanoseconds ~= 7 milliseconds) | ||
232 | * 3. DBG pin must be high before chip exits reset for normal | ||
233 | * operation. DBG is open drain and hopefully pulled high | ||
234 | * since we don't normally drive it (GPIO 1?) for the | ||
235 | * HVR-1600 | ||
236 | * 4. Z8F0811 won't exit reset until RESET is deasserted | ||
237 | * 5. Zilog comes out of reset, loads reset vector address and | ||
238 | * executes from there. Required recovery delay unknown. | ||
239 | */ | ||
240 | gpio_reset_seq(cx, p->ir_reset_mask, 0, | ||
241 | p->msecs_asserted, p->msecs_recovery); | ||
242 | break; | ||
243 | case CX18_GPIO_RESET_XC2028: | ||
244 | if (cx->card->tuners[0].tuner == TUNER_XC2028) | ||
245 | gpio_reset_seq(cx, (1 << cx->card->xceive_pin), 0, | ||
246 | 1, 1); | ||
247 | break; | ||
248 | } | ||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | static const struct v4l2_subdev_core_ops resetctrl_core_ops = { | ||
253 | .log_status = resetctrl_log_status, | ||
254 | .reset = resetctrl_reset, | ||
255 | }; | ||
256 | |||
257 | static const struct v4l2_subdev_ops resetctrl_ops = { | ||
258 | .core = &resetctrl_core_ops, | ||
259 | }; | ||
260 | |||
261 | /* | ||
262 | * External entry points | ||
263 | */ | ||
264 | void cx18_gpio_init(struct cx18 *cx) | ||
265 | { | ||
266 | mutex_lock(&cx->gpio_lock); | ||
267 | cx->gpio_dir = cx->card->gpio_init.direction; | ||
268 | cx->gpio_val = cx->card->gpio_init.initial_value; | ||
269 | |||
270 | if (cx->card->tuners[0].tuner == TUNER_XC2028) { | ||
271 | cx->gpio_dir |= 1 << cx->card->xceive_pin; | ||
272 | cx->gpio_val |= 1 << cx->card->xceive_pin; | ||
273 | } | ||
274 | |||
275 | if (cx->gpio_dir == 0) { | ||
276 | mutex_unlock(&cx->gpio_lock); | ||
277 | return; | ||
278 | } | ||
279 | |||
280 | CX18_DEBUG_INFO("GPIO initial dir: %08x/%08x out: %08x/%08x\n", | ||
281 | cx18_read_reg(cx, CX18_REG_GPIO_DIR1), | ||
282 | cx18_read_reg(cx, CX18_REG_GPIO_DIR2), | ||
283 | cx18_read_reg(cx, CX18_REG_GPIO_OUT1), | ||
284 | cx18_read_reg(cx, CX18_REG_GPIO_OUT2)); | ||
285 | |||
286 | gpio_write(cx); | ||
287 | mutex_unlock(&cx->gpio_lock); | ||
288 | } | ||
289 | |||
290 | int cx18_gpio_register(struct cx18 *cx, u32 hw) | ||
291 | { | ||
292 | struct v4l2_subdev *sd; | ||
293 | const struct v4l2_subdev_ops *ops; | ||
294 | char *str; | ||
295 | |||
296 | switch (hw) { | ||
297 | case CX18_HW_GPIO_MUX: | ||
298 | sd = &cx->sd_gpiomux; | ||
299 | ops = &gpiomux_ops; | ||
300 | str = "gpio-mux"; | ||
301 | break; | ||
302 | case CX18_HW_GPIO_RESET_CTRL: | ||
303 | sd = &cx->sd_resetctrl; | ||
304 | ops = &resetctrl_ops; | ||
305 | str = "gpio-reset-ctrl"; | ||
306 | break; | ||
307 | default: | ||
308 | return -EINVAL; | ||
309 | } | ||
310 | |||
311 | v4l2_subdev_init(sd, ops); | ||
312 | v4l2_set_subdevdata(sd, cx); | ||
313 | snprintf(sd->name, sizeof(sd->name), "%s %s", cx->v4l2_dev.name, str); | ||
314 | sd->grp_id = hw; | ||
315 | return v4l2_device_register_subdev(&cx->v4l2_dev, sd); | ||
316 | } | ||
317 | |||
318 | void cx18_reset_ir_gpio(void *data) | ||
319 | { | ||
320 | struct cx18 *cx = to_cx18((struct v4l2_device *)data); | ||
321 | |||
322 | if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0) | ||
323 | return; | ||
324 | |||
325 | CX18_DEBUG_INFO("Resetting IR microcontroller\n"); | ||
326 | |||
327 | v4l2_subdev_call(&cx->sd_resetctrl, | ||
328 | core, reset, CX18_GPIO_RESET_Z8F0811); | ||
329 | } | ||
330 | EXPORT_SYMBOL(cx18_reset_ir_gpio); | ||
331 | /* This symbol is exported for use by lirc_pvr150 for the IR-blaster */ | ||
332 | |||
333 | /* Xceive tuner reset function */ | ||
334 | int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value) | ||
335 | { | ||
336 | struct i2c_algo_bit_data *algo = dev; | ||
337 | struct cx18_i2c_algo_callback_data *cb_data = algo->data; | ||
338 | struct cx18 *cx = cb_data->cx; | ||
339 | |||
340 | if (cmd != XC2028_TUNER_RESET || | ||
341 | cx->card->tuners[0].tuner != TUNER_XC2028) | ||
342 | return 0; | ||
343 | |||
344 | CX18_DEBUG_INFO("Resetting XCeive tuner\n"); | ||
345 | return v4l2_subdev_call(&cx->sd_resetctrl, | ||
346 | core, reset, CX18_GPIO_RESET_XC2028); | ||
347 | } | ||
diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h new file mode 100644 index 00000000000..4aea2ef88e8 --- /dev/null +++ b/drivers/media/video/cx18/cx18-gpio.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * cx18 gpio functions | ||
3 | * | ||
4 | * Derived from ivtv-gpio.h | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | void cx18_gpio_init(struct cx18 *cx); | ||
25 | int cx18_gpio_register(struct cx18 *cx, u32 hw); | ||
26 | |||
27 | enum cx18_gpio_reset_type { | ||
28 | CX18_GPIO_RESET_I2C = 0, | ||
29 | CX18_GPIO_RESET_Z8F0811 = 1, | ||
30 | CX18_GPIO_RESET_XC2028 = 2, | ||
31 | }; | ||
32 | |||
33 | void cx18_reset_ir_gpio(void *data); | ||
34 | int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value); | ||
diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c new file mode 100644 index 00000000000..040aaa87579 --- /dev/null +++ b/drivers/media/video/cx18/cx18-i2c.c | |||
@@ -0,0 +1,330 @@ | |||
1 | /* | ||
2 | * cx18 I2C functions | ||
3 | * | ||
4 | * Derived from ivtv-i2c.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | #include "cx18-io.h" | ||
27 | #include "cx18-cards.h" | ||
28 | #include "cx18-gpio.h" | ||
29 | #include "cx18-i2c.h" | ||
30 | #include "cx18-irq.h" | ||
31 | |||
32 | #define CX18_REG_I2C_1_WR 0xf15000 | ||
33 | #define CX18_REG_I2C_1_RD 0xf15008 | ||
34 | #define CX18_REG_I2C_2_WR 0xf25100 | ||
35 | #define CX18_REG_I2C_2_RD 0xf25108 | ||
36 | |||
37 | #define SETSCL_BIT 0x0001 | ||
38 | #define SETSDL_BIT 0x0002 | ||
39 | #define GETSCL_BIT 0x0004 | ||
40 | #define GETSDL_BIT 0x0008 | ||
41 | |||
42 | #define CX18_CS5345_I2C_ADDR 0x4c | ||
43 | #define CX18_Z8F0811_IR_TX_I2C_ADDR 0x70 | ||
44 | #define CX18_Z8F0811_IR_RX_I2C_ADDR 0x71 | ||
45 | |||
46 | /* This array should match the CX18_HW_ defines */ | ||
47 | static const u8 hw_addrs[] = { | ||
48 | 0, /* CX18_HW_TUNER */ | ||
49 | 0, /* CX18_HW_TVEEPROM */ | ||
50 | CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */ | ||
51 | 0, /* CX18_HW_DVB */ | ||
52 | 0, /* CX18_HW_418_AV */ | ||
53 | 0, /* CX18_HW_GPIO_MUX */ | ||
54 | 0, /* CX18_HW_GPIO_RESET_CTRL */ | ||
55 | CX18_Z8F0811_IR_TX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_TX_HAUP */ | ||
56 | CX18_Z8F0811_IR_RX_I2C_ADDR, /* CX18_HW_Z8F0811_IR_RX_HAUP */ | ||
57 | }; | ||
58 | |||
59 | /* This array should match the CX18_HW_ defines */ | ||
60 | /* This might well become a card-specific array */ | ||
61 | static const u8 hw_bus[] = { | ||
62 | 1, /* CX18_HW_TUNER */ | ||
63 | 0, /* CX18_HW_TVEEPROM */ | ||
64 | 0, /* CX18_HW_CS5345 */ | ||
65 | 0, /* CX18_HW_DVB */ | ||
66 | 0, /* CX18_HW_418_AV */ | ||
67 | 0, /* CX18_HW_GPIO_MUX */ | ||
68 | 0, /* CX18_HW_GPIO_RESET_CTRL */ | ||
69 | 0, /* CX18_HW_Z8F0811_IR_TX_HAUP */ | ||
70 | 0, /* CX18_HW_Z8F0811_IR_RX_HAUP */ | ||
71 | }; | ||
72 | |||
73 | /* This array should match the CX18_HW_ defines */ | ||
74 | static const char * const hw_devicenames[] = { | ||
75 | "tuner", | ||
76 | "tveeprom", | ||
77 | "cs5345", | ||
78 | "cx23418_DTV", | ||
79 | "cx23418_AV", | ||
80 | "gpio_mux", | ||
81 | "gpio_reset_ctrl", | ||
82 | "ir_tx_z8f0811_haup", | ||
83 | "ir_rx_z8f0811_haup", | ||
84 | }; | ||
85 | |||
86 | static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, | ||
87 | const char *type, u8 addr) | ||
88 | { | ||
89 | struct i2c_board_info info; | ||
90 | struct IR_i2c_init_data *init_data = &cx->ir_i2c_init_data; | ||
91 | unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; | ||
92 | |||
93 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
94 | strlcpy(info.type, type, I2C_NAME_SIZE); | ||
95 | |||
96 | /* Our default information for ir-kbd-i2c.c to use */ | ||
97 | switch (hw) { | ||
98 | case CX18_HW_Z8F0811_IR_RX_HAUP: | ||
99 | init_data->ir_codes = RC_MAP_HAUPPAUGE; | ||
100 | init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; | ||
101 | init_data->type = RC_TYPE_RC5; | ||
102 | init_data->name = cx->card_name; | ||
103 | info.platform_data = init_data; | ||
104 | break; | ||
105 | } | ||
106 | |||
107 | return i2c_new_probed_device(adap, &info, addr_list, NULL) == NULL ? | ||
108 | -1 : 0; | ||
109 | } | ||
110 | |||
111 | int cx18_i2c_register(struct cx18 *cx, unsigned idx) | ||
112 | { | ||
113 | struct v4l2_subdev *sd; | ||
114 | int bus = hw_bus[idx]; | ||
115 | struct i2c_adapter *adap = &cx->i2c_adap[bus]; | ||
116 | const char *type = hw_devicenames[idx]; | ||
117 | u32 hw = 1 << idx; | ||
118 | |||
119 | if (idx >= ARRAY_SIZE(hw_addrs)) | ||
120 | return -1; | ||
121 | |||
122 | if (hw == CX18_HW_TUNER) { | ||
123 | /* special tuner group handling */ | ||
124 | sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, | ||
125 | adap, type, 0, cx->card_i2c->radio); | ||
126 | if (sd != NULL) | ||
127 | sd->grp_id = hw; | ||
128 | sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, | ||
129 | adap, type, 0, cx->card_i2c->demod); | ||
130 | if (sd != NULL) | ||
131 | sd->grp_id = hw; | ||
132 | sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, | ||
133 | adap, type, 0, cx->card_i2c->tv); | ||
134 | if (sd != NULL) | ||
135 | sd->grp_id = hw; | ||
136 | return sd != NULL ? 0 : -1; | ||
137 | } | ||
138 | |||
139 | if (hw & CX18_HW_IR_ANY) | ||
140 | return cx18_i2c_new_ir(cx, adap, hw, type, hw_addrs[idx]); | ||
141 | |||
142 | /* Is it not an I2C device or one we do not wish to register? */ | ||
143 | if (!hw_addrs[idx]) | ||
144 | return -1; | ||
145 | |||
146 | /* It's an I2C device other than an analog tuner or IR chip */ | ||
147 | sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, type, hw_addrs[idx], | ||
148 | NULL); | ||
149 | if (sd != NULL) | ||
150 | sd->grp_id = hw; | ||
151 | return sd != NULL ? 0 : -1; | ||
152 | } | ||
153 | |||
154 | /* Find the first member of the subdev group id in hw */ | ||
155 | struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw) | ||
156 | { | ||
157 | struct v4l2_subdev *result = NULL; | ||
158 | struct v4l2_subdev *sd; | ||
159 | |||
160 | spin_lock(&cx->v4l2_dev.lock); | ||
161 | v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) { | ||
162 | if (sd->grp_id == hw) { | ||
163 | result = sd; | ||
164 | break; | ||
165 | } | ||
166 | } | ||
167 | spin_unlock(&cx->v4l2_dev.lock); | ||
168 | return result; | ||
169 | } | ||
170 | |||
171 | static void cx18_setscl(void *data, int state) | ||
172 | { | ||
173 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; | ||
174 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; | ||
175 | u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; | ||
176 | u32 r = cx18_read_reg(cx, addr); | ||
177 | |||
178 | if (state) | ||
179 | cx18_write_reg(cx, r | SETSCL_BIT, addr); | ||
180 | else | ||
181 | cx18_write_reg(cx, r & ~SETSCL_BIT, addr); | ||
182 | } | ||
183 | |||
184 | static void cx18_setsda(void *data, int state) | ||
185 | { | ||
186 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; | ||
187 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; | ||
188 | u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR; | ||
189 | u32 r = cx18_read_reg(cx, addr); | ||
190 | |||
191 | if (state) | ||
192 | cx18_write_reg(cx, r | SETSDL_BIT, addr); | ||
193 | else | ||
194 | cx18_write_reg(cx, r & ~SETSDL_BIT, addr); | ||
195 | } | ||
196 | |||
197 | static int cx18_getscl(void *data) | ||
198 | { | ||
199 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; | ||
200 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; | ||
201 | u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; | ||
202 | |||
203 | return cx18_read_reg(cx, addr) & GETSCL_BIT; | ||
204 | } | ||
205 | |||
206 | static int cx18_getsda(void *data) | ||
207 | { | ||
208 | struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx; | ||
209 | int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index; | ||
210 | u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD; | ||
211 | |||
212 | return cx18_read_reg(cx, addr) & GETSDL_BIT; | ||
213 | } | ||
214 | |||
215 | /* template for i2c-bit-algo */ | ||
216 | static struct i2c_adapter cx18_i2c_adap_template = { | ||
217 | .name = "cx18 i2c driver", | ||
218 | .algo = NULL, /* set by i2c-algo-bit */ | ||
219 | .algo_data = NULL, /* filled from template */ | ||
220 | .owner = THIS_MODULE, | ||
221 | }; | ||
222 | |||
223 | #define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */ | ||
224 | #define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */ | ||
225 | |||
226 | static struct i2c_algo_bit_data cx18_i2c_algo_template = { | ||
227 | .setsda = cx18_setsda, | ||
228 | .setscl = cx18_setscl, | ||
229 | .getsda = cx18_getsda, | ||
230 | .getscl = cx18_getscl, | ||
231 | .udelay = CX18_SCL_PERIOD/2, /* 1/2 clock period in usec*/ | ||
232 | .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */ | ||
233 | }; | ||
234 | |||
235 | /* init + register i2c algo-bit adapter */ | ||
236 | int init_cx18_i2c(struct cx18 *cx) | ||
237 | { | ||
238 | int i, err; | ||
239 | CX18_DEBUG_I2C("i2c init\n"); | ||
240 | |||
241 | for (i = 0; i < 2; i++) { | ||
242 | /* Setup algorithm for adapter */ | ||
243 | memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template, | ||
244 | sizeof(struct i2c_algo_bit_data)); | ||
245 | cx->i2c_algo_cb_data[i].cx = cx; | ||
246 | cx->i2c_algo_cb_data[i].bus_index = i; | ||
247 | cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i]; | ||
248 | |||
249 | /* Setup adapter */ | ||
250 | memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template, | ||
251 | sizeof(struct i2c_adapter)); | ||
252 | cx->i2c_adap[i].algo_data = &cx->i2c_algo[i]; | ||
253 | sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name), | ||
254 | " #%d-%d", cx->instance, i); | ||
255 | i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev); | ||
256 | cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev; | ||
257 | } | ||
258 | |||
259 | if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) { | ||
260 | /* Reset/Unreset I2C hardware block */ | ||
261 | /* Clock select 220MHz */ | ||
262 | cx18_write_reg_expect(cx, 0x10000000, 0xc71004, | ||
263 | 0x00000000, 0x10001000); | ||
264 | /* Clock Enable */ | ||
265 | cx18_write_reg_expect(cx, 0x10001000, 0xc71024, | ||
266 | 0x00001000, 0x10001000); | ||
267 | } | ||
268 | /* courtesy of Steven Toth <stoth@hauppauge.com> */ | ||
269 | cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); | ||
270 | mdelay(10); | ||
271 | cx18_write_reg_expect(cx, 0x00c000c0, 0xc7001c, 0x000000c0, 0x00c000c0); | ||
272 | mdelay(10); | ||
273 | cx18_write_reg_expect(cx, 0x00c00000, 0xc7001c, 0x00000000, 0x00c000c0); | ||
274 | mdelay(10); | ||
275 | |||
276 | /* Set to edge-triggered intrs. */ | ||
277 | cx18_write_reg(cx, 0x00c00000, 0xc730c8); | ||
278 | /* Clear any stale intrs */ | ||
279 | cx18_write_reg_expect(cx, HW2_I2C1_INT|HW2_I2C2_INT, HW2_INT_CLR_STATUS, | ||
280 | ~(HW2_I2C1_INT|HW2_I2C2_INT), HW2_I2C1_INT|HW2_I2C2_INT); | ||
281 | |||
282 | /* Hw I2C1 Clock Freq ~100kHz */ | ||
283 | cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_1_WR); | ||
284 | cx18_setscl(&cx->i2c_algo_cb_data[0], 1); | ||
285 | cx18_setsda(&cx->i2c_algo_cb_data[0], 1); | ||
286 | |||
287 | /* Hw I2C2 Clock Freq ~100kHz */ | ||
288 | cx18_write_reg(cx, 0x00021c0f & ~4, CX18_REG_I2C_2_WR); | ||
289 | cx18_setscl(&cx->i2c_algo_cb_data[1], 1); | ||
290 | cx18_setsda(&cx->i2c_algo_cb_data[1], 1); | ||
291 | |||
292 | cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, | ||
293 | core, reset, (u32) CX18_GPIO_RESET_I2C); | ||
294 | |||
295 | err = i2c_bit_add_bus(&cx->i2c_adap[0]); | ||
296 | if (err) | ||
297 | goto err; | ||
298 | err = i2c_bit_add_bus(&cx->i2c_adap[1]); | ||
299 | if (err) | ||
300 | goto err_del_bus_0; | ||
301 | return 0; | ||
302 | |||
303 | err_del_bus_0: | ||
304 | i2c_del_adapter(&cx->i2c_adap[0]); | ||
305 | err: | ||
306 | return err; | ||
307 | } | ||
308 | |||
309 | void exit_cx18_i2c(struct cx18 *cx) | ||
310 | { | ||
311 | int i; | ||
312 | CX18_DEBUG_I2C("i2c exit\n"); | ||
313 | cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_1_WR) | 4, | ||
314 | CX18_REG_I2C_1_WR); | ||
315 | cx18_write_reg(cx, cx18_read_reg(cx, CX18_REG_I2C_2_WR) | 4, | ||
316 | CX18_REG_I2C_2_WR); | ||
317 | |||
318 | for (i = 0; i < 2; i++) { | ||
319 | i2c_del_adapter(&cx->i2c_adap[i]); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | /* | ||
324 | Hauppauge HVR1600 should have: | ||
325 | 32 cx24227 | ||
326 | 98 unknown | ||
327 | a0 eeprom | ||
328 | c2 tuner | ||
329 | e? zilog ir | ||
330 | */ | ||
diff --git a/drivers/media/video/cx18/cx18-i2c.h b/drivers/media/video/cx18/cx18-i2c.h new file mode 100644 index 00000000000..bdfd1921e30 --- /dev/null +++ b/drivers/media/video/cx18/cx18-i2c.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * cx18 I2C functions | ||
3 | * | ||
4 | * Derived from ivtv-i2c.h | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
21 | * 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | int cx18_i2c_register(struct cx18 *cx, unsigned idx); | ||
25 | struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw); | ||
26 | |||
27 | /* init + register i2c algo-bit adapter */ | ||
28 | int init_cx18_i2c(struct cx18 *cx); | ||
29 | void exit_cx18_i2c(struct cx18 *cx); | ||
diff --git a/drivers/media/video/cx18/cx18-io.c b/drivers/media/video/cx18/cx18-io.c new file mode 100644 index 00000000000..49b9dbd0624 --- /dev/null +++ b/drivers/media/video/cx18/cx18-io.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * cx18 driver PCI memory mapped IO access routines | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include "cx18-driver.h" | ||
24 | #include "cx18-io.h" | ||
25 | #include "cx18-irq.h" | ||
26 | |||
27 | void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count) | ||
28 | { | ||
29 | u8 __iomem *dst = addr; | ||
30 | u16 val2 = val | (val << 8); | ||
31 | u32 val4 = val2 | (val2 << 16); | ||
32 | |||
33 | /* Align writes on the CX23418's addresses */ | ||
34 | if ((count > 0) && ((unsigned long)dst & 1)) { | ||
35 | cx18_writeb(cx, (u8) val, dst); | ||
36 | count--; | ||
37 | dst++; | ||
38 | } | ||
39 | if ((count > 1) && ((unsigned long)dst & 2)) { | ||
40 | cx18_writew(cx, val2, dst); | ||
41 | count -= 2; | ||
42 | dst += 2; | ||
43 | } | ||
44 | while (count > 3) { | ||
45 | cx18_writel(cx, val4, dst); | ||
46 | count -= 4; | ||
47 | dst += 4; | ||
48 | } | ||
49 | if (count > 1) { | ||
50 | cx18_writew(cx, val2, dst); | ||
51 | count -= 2; | ||
52 | dst += 2; | ||
53 | } | ||
54 | if (count > 0) | ||
55 | cx18_writeb(cx, (u8) val, dst); | ||
56 | } | ||
57 | |||
58 | void cx18_sw1_irq_enable(struct cx18 *cx, u32 val) | ||
59 | { | ||
60 | cx18_write_reg_expect(cx, val, SW1_INT_STATUS, ~val, val); | ||
61 | cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) | val; | ||
62 | cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI); | ||
63 | } | ||
64 | |||
65 | void cx18_sw1_irq_disable(struct cx18 *cx, u32 val) | ||
66 | { | ||
67 | cx->sw1_irq_mask = cx18_read_reg(cx, SW1_INT_ENABLE_PCI) & ~val; | ||
68 | cx18_write_reg(cx, cx->sw1_irq_mask, SW1_INT_ENABLE_PCI); | ||
69 | } | ||
70 | |||
71 | void cx18_sw2_irq_enable(struct cx18 *cx, u32 val) | ||
72 | { | ||
73 | cx18_write_reg_expect(cx, val, SW2_INT_STATUS, ~val, val); | ||
74 | cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) | val; | ||
75 | cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI); | ||
76 | } | ||
77 | |||
78 | void cx18_sw2_irq_disable(struct cx18 *cx, u32 val) | ||
79 | { | ||
80 | cx->sw2_irq_mask = cx18_read_reg(cx, SW2_INT_ENABLE_PCI) & ~val; | ||
81 | cx18_write_reg(cx, cx->sw2_irq_mask, SW2_INT_ENABLE_PCI); | ||
82 | } | ||
83 | |||
84 | void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val) | ||
85 | { | ||
86 | u32 r; | ||
87 | r = cx18_read_reg(cx, SW2_INT_ENABLE_CPU); | ||
88 | cx18_write_reg(cx, r & ~val, SW2_INT_ENABLE_CPU); | ||
89 | } | ||
90 | |||
91 | void cx18_setup_page(struct cx18 *cx, u32 addr) | ||
92 | { | ||
93 | u32 val; | ||
94 | val = cx18_read_reg(cx, 0xD000F8); | ||
95 | val = (val & ~0x1f00) | ((addr >> 17) & 0x1f00); | ||
96 | cx18_write_reg(cx, val, 0xD000F8); | ||
97 | } | ||
diff --git a/drivers/media/video/cx18/cx18-io.h b/drivers/media/video/cx18/cx18-io.h new file mode 100644 index 00000000000..18974d886cf --- /dev/null +++ b/drivers/media/video/cx18/cx18-io.h | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * cx18 driver PCI memory mapped IO access routines | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #ifndef CX18_IO_H | ||
24 | #define CX18_IO_H | ||
25 | |||
26 | #include "cx18-driver.h" | ||
27 | |||
28 | /* | ||
29 | * Readback and retry of MMIO access for reliability: | ||
30 | * The concept was suggested by Steve Toth <stoth@linuxtv.org>. | ||
31 | * The implmentation is the fault of Andy Walls <awalls@md.metrocast.net>. | ||
32 | * | ||
33 | * *write* functions are implied to retry the mmio unless suffixed with _noretry | ||
34 | * *read* functions never retry the mmio (it never helps to do so) | ||
35 | */ | ||
36 | |||
37 | /* Non byteswapping memory mapped IO */ | ||
38 | static inline u32 cx18_raw_readl(struct cx18 *cx, const void __iomem *addr) | ||
39 | { | ||
40 | return __raw_readl(addr); | ||
41 | } | ||
42 | |||
43 | static inline | ||
44 | void cx18_raw_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr) | ||
45 | { | ||
46 | __raw_writel(val, addr); | ||
47 | } | ||
48 | |||
49 | static inline void cx18_raw_writel(struct cx18 *cx, u32 val, void __iomem *addr) | ||
50 | { | ||
51 | int i; | ||
52 | for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { | ||
53 | cx18_raw_writel_noretry(cx, val, addr); | ||
54 | if (val == cx18_raw_readl(cx, addr)) | ||
55 | break; | ||
56 | } | ||
57 | } | ||
58 | |||
59 | /* Normal memory mapped IO */ | ||
60 | static inline u32 cx18_readl(struct cx18 *cx, const void __iomem *addr) | ||
61 | { | ||
62 | return readl(addr); | ||
63 | } | ||
64 | |||
65 | static inline | ||
66 | void cx18_writel_noretry(struct cx18 *cx, u32 val, void __iomem *addr) | ||
67 | { | ||
68 | writel(val, addr); | ||
69 | } | ||
70 | |||
71 | static inline void cx18_writel(struct cx18 *cx, u32 val, void __iomem *addr) | ||
72 | { | ||
73 | int i; | ||
74 | for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { | ||
75 | cx18_writel_noretry(cx, val, addr); | ||
76 | if (val == cx18_readl(cx, addr)) | ||
77 | break; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | static inline | ||
82 | void cx18_writel_expect(struct cx18 *cx, u32 val, void __iomem *addr, | ||
83 | u32 eval, u32 mask) | ||
84 | { | ||
85 | int i; | ||
86 | u32 r; | ||
87 | eval &= mask; | ||
88 | for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { | ||
89 | cx18_writel_noretry(cx, val, addr); | ||
90 | r = cx18_readl(cx, addr); | ||
91 | if (r == 0xffffffff && eval != 0xffffffff) | ||
92 | continue; | ||
93 | if (eval == (r & mask)) | ||
94 | break; | ||
95 | } | ||
96 | } | ||
97 | |||
98 | static inline u16 cx18_readw(struct cx18 *cx, const void __iomem *addr) | ||
99 | { | ||
100 | return readw(addr); | ||
101 | } | ||
102 | |||
103 | static inline | ||
104 | void cx18_writew_noretry(struct cx18 *cx, u16 val, void __iomem *addr) | ||
105 | { | ||
106 | writew(val, addr); | ||
107 | } | ||
108 | |||
109 | static inline void cx18_writew(struct cx18 *cx, u16 val, void __iomem *addr) | ||
110 | { | ||
111 | int i; | ||
112 | for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { | ||
113 | cx18_writew_noretry(cx, val, addr); | ||
114 | if (val == cx18_readw(cx, addr)) | ||
115 | break; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | static inline u8 cx18_readb(struct cx18 *cx, const void __iomem *addr) | ||
120 | { | ||
121 | return readb(addr); | ||
122 | } | ||
123 | |||
124 | static inline | ||
125 | void cx18_writeb_noretry(struct cx18 *cx, u8 val, void __iomem *addr) | ||
126 | { | ||
127 | writeb(val, addr); | ||
128 | } | ||
129 | |||
130 | static inline void cx18_writeb(struct cx18 *cx, u8 val, void __iomem *addr) | ||
131 | { | ||
132 | int i; | ||
133 | for (i = 0; i < CX18_MAX_MMIO_WR_RETRIES; i++) { | ||
134 | cx18_writeb_noretry(cx, val, addr); | ||
135 | if (val == cx18_readb(cx, addr)) | ||
136 | break; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | static inline | ||
141 | void cx18_memcpy_fromio(struct cx18 *cx, void *to, | ||
142 | const void __iomem *from, unsigned int len) | ||
143 | { | ||
144 | memcpy_fromio(to, from, len); | ||
145 | } | ||
146 | |||
147 | void cx18_memset_io(struct cx18 *cx, void __iomem *addr, int val, size_t count); | ||
148 | |||
149 | |||
150 | /* Access "register" region of CX23418 memory mapped I/O */ | ||
151 | static inline void cx18_write_reg_noretry(struct cx18 *cx, u32 val, u32 reg) | ||
152 | { | ||
153 | cx18_writel_noretry(cx, val, cx->reg_mem + reg); | ||
154 | } | ||
155 | |||
156 | static inline void cx18_write_reg(struct cx18 *cx, u32 val, u32 reg) | ||
157 | { | ||
158 | cx18_writel(cx, val, cx->reg_mem + reg); | ||
159 | } | ||
160 | |||
161 | static inline void cx18_write_reg_expect(struct cx18 *cx, u32 val, u32 reg, | ||
162 | u32 eval, u32 mask) | ||
163 | { | ||
164 | cx18_writel_expect(cx, val, cx->reg_mem + reg, eval, mask); | ||
165 | } | ||
166 | |||
167 | static inline u32 cx18_read_reg(struct cx18 *cx, u32 reg) | ||
168 | { | ||
169 | return cx18_readl(cx, cx->reg_mem + reg); | ||
170 | } | ||
171 | |||
172 | |||
173 | /* Access "encoder memory" region of CX23418 memory mapped I/O */ | ||
174 | static inline void cx18_write_enc(struct cx18 *cx, u32 val, u32 addr) | ||
175 | { | ||
176 | cx18_writel(cx, val, cx->enc_mem + addr); | ||
177 | } | ||
178 | |||
179 | static inline u32 cx18_read_enc(struct cx18 *cx, u32 addr) | ||
180 | { | ||
181 | return cx18_readl(cx, cx->enc_mem + addr); | ||
182 | } | ||
183 | |||
184 | void cx18_sw1_irq_enable(struct cx18 *cx, u32 val); | ||
185 | void cx18_sw1_irq_disable(struct cx18 *cx, u32 val); | ||
186 | void cx18_sw2_irq_enable(struct cx18 *cx, u32 val); | ||
187 | void cx18_sw2_irq_disable(struct cx18 *cx, u32 val); | ||
188 | void cx18_sw2_irq_disable_cpu(struct cx18 *cx, u32 val); | ||
189 | void cx18_setup_page(struct cx18 *cx, u32 addr); | ||
190 | |||
191 | #endif /* CX18_IO_H */ | ||
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c new file mode 100644 index 00000000000..afe0a29e720 --- /dev/null +++ b/drivers/media/video/cx18/cx18-ioctl.c | |||
@@ -0,0 +1,1212 @@ | |||
1 | /* | ||
2 | * cx18 ioctl system call | ||
3 | * | ||
4 | * Derived from ivtv-ioctl.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | #include "cx18-io.h" | ||
27 | #include "cx18-version.h" | ||
28 | #include "cx18-mailbox.h" | ||
29 | #include "cx18-i2c.h" | ||
30 | #include "cx18-queue.h" | ||
31 | #include "cx18-fileops.h" | ||
32 | #include "cx18-vbi.h" | ||
33 | #include "cx18-audio.h" | ||
34 | #include "cx18-video.h" | ||
35 | #include "cx18-streams.h" | ||
36 | #include "cx18-ioctl.h" | ||
37 | #include "cx18-gpio.h" | ||
38 | #include "cx18-controls.h" | ||
39 | #include "cx18-cards.h" | ||
40 | #include "cx18-av-core.h" | ||
41 | #include <media/tveeprom.h> | ||
42 | #include <media/v4l2-chip-ident.h> | ||
43 | |||
44 | u16 cx18_service2vbi(int type) | ||
45 | { | ||
46 | switch (type) { | ||
47 | case V4L2_SLICED_TELETEXT_B: | ||
48 | return CX18_SLICED_TYPE_TELETEXT_B; | ||
49 | case V4L2_SLICED_CAPTION_525: | ||
50 | return CX18_SLICED_TYPE_CAPTION_525; | ||
51 | case V4L2_SLICED_WSS_625: | ||
52 | return CX18_SLICED_TYPE_WSS_625; | ||
53 | case V4L2_SLICED_VPS: | ||
54 | return CX18_SLICED_TYPE_VPS; | ||
55 | default: | ||
56 | return 0; | ||
57 | } | ||
58 | } | ||
59 | |||
60 | /* Check if VBI services are allowed on the (field, line) for the video std */ | ||
61 | static int valid_service_line(int field, int line, int is_pal) | ||
62 | { | ||
63 | return (is_pal && line >= 6 && | ||
64 | ((field == 0 && line <= 23) || (field == 1 && line <= 22))) || | ||
65 | (!is_pal && line >= 10 && line < 22); | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * For a (field, line, std) and inbound potential set of services for that line, | ||
70 | * return the first valid service of those passed in the incoming set for that | ||
71 | * line in priority order: | ||
72 | * CC, VPS, or WSS over TELETEXT for well known lines | ||
73 | * TELETEXT, before VPS, before CC, before WSS, for other lines | ||
74 | */ | ||
75 | static u16 select_service_from_set(int field, int line, u16 set, int is_pal) | ||
76 | { | ||
77 | u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); | ||
78 | int i; | ||
79 | |||
80 | set = set & valid_set; | ||
81 | if (set == 0 || !valid_service_line(field, line, is_pal)) | ||
82 | return 0; | ||
83 | if (!is_pal) { | ||
84 | if (line == 21 && (set & V4L2_SLICED_CAPTION_525)) | ||
85 | return V4L2_SLICED_CAPTION_525; | ||
86 | } else { | ||
87 | if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS)) | ||
88 | return V4L2_SLICED_VPS; | ||
89 | if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625)) | ||
90 | return V4L2_SLICED_WSS_625; | ||
91 | if (line == 23) | ||
92 | return 0; | ||
93 | } | ||
94 | for (i = 0; i < 32; i++) { | ||
95 | if ((1 << i) & set) | ||
96 | return 1 << i; | ||
97 | } | ||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * Expand the service_set of *fmt into valid service_lines for the std, | ||
103 | * and clear the passed in fmt->service_set | ||
104 | */ | ||
105 | void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) | ||
106 | { | ||
107 | u16 set = fmt->service_set; | ||
108 | int f, l; | ||
109 | |||
110 | fmt->service_set = 0; | ||
111 | for (f = 0; f < 2; f++) { | ||
112 | for (l = 0; l < 24; l++) | ||
113 | fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal); | ||
114 | } | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * Sanitize the service_lines in *fmt per the video std, and return 1 | ||
119 | * if any service_line is left as valid after santization | ||
120 | */ | ||
121 | static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) | ||
122 | { | ||
123 | int f, l; | ||
124 | u16 set = 0; | ||
125 | |||
126 | for (f = 0; f < 2; f++) { | ||
127 | for (l = 0; l < 24; l++) { | ||
128 | fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); | ||
129 | set |= fmt->service_lines[f][l]; | ||
130 | } | ||
131 | } | ||
132 | return set != 0; | ||
133 | } | ||
134 | |||
135 | /* Compute the service_set from the assumed valid service_lines of *fmt */ | ||
136 | u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt) | ||
137 | { | ||
138 | int f, l; | ||
139 | u16 set = 0; | ||
140 | |||
141 | for (f = 0; f < 2; f++) { | ||
142 | for (l = 0; l < 24; l++) | ||
143 | set |= fmt->service_lines[f][l]; | ||
144 | } | ||
145 | return set; | ||
146 | } | ||
147 | |||
148 | static int cx18_g_fmt_vid_cap(struct file *file, void *fh, | ||
149 | struct v4l2_format *fmt) | ||
150 | { | ||
151 | struct cx18_open_id *id = fh2id(fh); | ||
152 | struct cx18 *cx = id->cx; | ||
153 | struct cx18_stream *s = &cx->streams[id->type]; | ||
154 | struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; | ||
155 | |||
156 | pixfmt->width = cx->cxhdl.width; | ||
157 | pixfmt->height = cx->cxhdl.height; | ||
158 | pixfmt->colorspace = V4L2_COLORSPACE_SMPTE170M; | ||
159 | pixfmt->field = V4L2_FIELD_INTERLACED; | ||
160 | pixfmt->priv = 0; | ||
161 | if (id->type == CX18_ENC_STREAM_TYPE_YUV) { | ||
162 | pixfmt->pixelformat = s->pixelformat; | ||
163 | /* HM12 YUV size is (Y=(h*720) + UV=(h*(720/2))) | ||
164 | UYUV YUV size is (Y=(h*720) + UV=(h*(720))) */ | ||
165 | if (s->pixelformat == V4L2_PIX_FMT_HM12) | ||
166 | pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2; | ||
167 | else | ||
168 | pixfmt->sizeimage = pixfmt->height * 720 * 2; | ||
169 | pixfmt->bytesperline = 720; | ||
170 | } else { | ||
171 | pixfmt->pixelformat = V4L2_PIX_FMT_MPEG; | ||
172 | pixfmt->sizeimage = 128 * 1024; | ||
173 | pixfmt->bytesperline = 0; | ||
174 | } | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static int cx18_g_fmt_vbi_cap(struct file *file, void *fh, | ||
179 | struct v4l2_format *fmt) | ||
180 | { | ||
181 | struct cx18 *cx = fh2id(fh)->cx; | ||
182 | struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi; | ||
183 | |||
184 | vbifmt->sampling_rate = 27000000; | ||
185 | vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */ | ||
186 | vbifmt->samples_per_line = vbi_active_samples - 4; | ||
187 | vbifmt->sample_format = V4L2_PIX_FMT_GREY; | ||
188 | vbifmt->start[0] = cx->vbi.start[0]; | ||
189 | vbifmt->start[1] = cx->vbi.start[1]; | ||
190 | vbifmt->count[0] = vbifmt->count[1] = cx->vbi.count; | ||
191 | vbifmt->flags = 0; | ||
192 | vbifmt->reserved[0] = 0; | ||
193 | vbifmt->reserved[1] = 0; | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh, | ||
198 | struct v4l2_format *fmt) | ||
199 | { | ||
200 | struct cx18 *cx = fh2id(fh)->cx; | ||
201 | struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; | ||
202 | |||
203 | /* sane, V4L2 spec compliant, defaults */ | ||
204 | vbifmt->reserved[0] = 0; | ||
205 | vbifmt->reserved[1] = 0; | ||
206 | vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; | ||
207 | memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); | ||
208 | vbifmt->service_set = 0; | ||
209 | |||
210 | /* | ||
211 | * Fetch the configured service_lines and total service_set from the | ||
212 | * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in | ||
213 | * fmt->fmt.sliced under valid calling conditions | ||
214 | */ | ||
215 | if (v4l2_subdev_call(cx->sd_av, vbi, g_sliced_fmt, &fmt->fmt.sliced)) | ||
216 | return -EINVAL; | ||
217 | |||
218 | /* Ensure V4L2 spec compliant output */ | ||
219 | vbifmt->reserved[0] = 0; | ||
220 | vbifmt->reserved[1] = 0; | ||
221 | vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; | ||
222 | vbifmt->service_set = cx18_get_service_set(vbifmt); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static int cx18_try_fmt_vid_cap(struct file *file, void *fh, | ||
227 | struct v4l2_format *fmt) | ||
228 | { | ||
229 | struct cx18_open_id *id = fh2id(fh); | ||
230 | struct cx18 *cx = id->cx; | ||
231 | int w = fmt->fmt.pix.width; | ||
232 | int h = fmt->fmt.pix.height; | ||
233 | int min_h = 2; | ||
234 | |||
235 | w = min(w, 720); | ||
236 | w = max(w, 2); | ||
237 | if (id->type == CX18_ENC_STREAM_TYPE_YUV) { | ||
238 | /* YUV height must be a multiple of 32 */ | ||
239 | h &= ~0x1f; | ||
240 | min_h = 32; | ||
241 | } | ||
242 | h = min(h, cx->is_50hz ? 576 : 480); | ||
243 | h = max(h, min_h); | ||
244 | |||
245 | fmt->fmt.pix.width = w; | ||
246 | fmt->fmt.pix.height = h; | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | static int cx18_try_fmt_vbi_cap(struct file *file, void *fh, | ||
251 | struct v4l2_format *fmt) | ||
252 | { | ||
253 | return cx18_g_fmt_vbi_cap(file, fh, fmt); | ||
254 | } | ||
255 | |||
256 | static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh, | ||
257 | struct v4l2_format *fmt) | ||
258 | { | ||
259 | struct cx18 *cx = fh2id(fh)->cx; | ||
260 | struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; | ||
261 | |||
262 | vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; | ||
263 | vbifmt->reserved[0] = 0; | ||
264 | vbifmt->reserved[1] = 0; | ||
265 | |||
266 | /* If given a service set, expand it validly & clear passed in set */ | ||
267 | if (vbifmt->service_set) | ||
268 | cx18_expand_service_set(vbifmt, cx->is_50hz); | ||
269 | /* Sanitize the service_lines, and compute the new set if any valid */ | ||
270 | if (check_service_set(vbifmt, cx->is_50hz)) | ||
271 | vbifmt->service_set = cx18_get_service_set(vbifmt); | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | static int cx18_s_fmt_vid_cap(struct file *file, void *fh, | ||
276 | struct v4l2_format *fmt) | ||
277 | { | ||
278 | struct cx18_open_id *id = fh2id(fh); | ||
279 | struct cx18 *cx = id->cx; | ||
280 | struct v4l2_mbus_framefmt mbus_fmt; | ||
281 | struct cx18_stream *s = &cx->streams[id->type]; | ||
282 | int ret; | ||
283 | int w, h; | ||
284 | |||
285 | ret = cx18_try_fmt_vid_cap(file, fh, fmt); | ||
286 | if (ret) | ||
287 | return ret; | ||
288 | w = fmt->fmt.pix.width; | ||
289 | h = fmt->fmt.pix.height; | ||
290 | |||
291 | if (cx->cxhdl.width == w && cx->cxhdl.height == h && | ||
292 | s->pixelformat == fmt->fmt.pix.pixelformat) | ||
293 | return 0; | ||
294 | |||
295 | if (atomic_read(&cx->ana_capturing) > 0) | ||
296 | return -EBUSY; | ||
297 | |||
298 | s->pixelformat = fmt->fmt.pix.pixelformat; | ||
299 | |||
300 | mbus_fmt.width = cx->cxhdl.width = w; | ||
301 | mbus_fmt.height = cx->cxhdl.height = h; | ||
302 | mbus_fmt.code = V4L2_MBUS_FMT_FIXED; | ||
303 | v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &mbus_fmt); | ||
304 | return cx18_g_fmt_vid_cap(file, fh, fmt); | ||
305 | } | ||
306 | |||
307 | static int cx18_s_fmt_vbi_cap(struct file *file, void *fh, | ||
308 | struct v4l2_format *fmt) | ||
309 | { | ||
310 | struct cx18_open_id *id = fh2id(fh); | ||
311 | struct cx18 *cx = id->cx; | ||
312 | int ret; | ||
313 | |||
314 | /* | ||
315 | * Changing the Encoder's Raw VBI parameters won't have any effect | ||
316 | * if any analog capture is ongoing | ||
317 | */ | ||
318 | if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) | ||
319 | return -EBUSY; | ||
320 | |||
321 | /* | ||
322 | * Set the digitizer registers for raw active VBI. | ||
323 | * Note cx18_av_vbi_wipes out a lot of the passed in fmt under valid | ||
324 | * calling conditions | ||
325 | */ | ||
326 | ret = v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &fmt->fmt.vbi); | ||
327 | if (ret) | ||
328 | return ret; | ||
329 | |||
330 | /* Store our new v4l2 (non-)sliced VBI state */ | ||
331 | cx->vbi.sliced_in->service_set = 0; | ||
332 | cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; | ||
333 | |||
334 | return cx18_g_fmt_vbi_cap(file, fh, fmt); | ||
335 | } | ||
336 | |||
337 | static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh, | ||
338 | struct v4l2_format *fmt) | ||
339 | { | ||
340 | struct cx18_open_id *id = fh2id(fh); | ||
341 | struct cx18 *cx = id->cx; | ||
342 | int ret; | ||
343 | struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; | ||
344 | |||
345 | cx18_try_fmt_sliced_vbi_cap(file, fh, fmt); | ||
346 | |||
347 | /* | ||
348 | * Changing the Encoder's Raw VBI parameters won't have any effect | ||
349 | * if any analog capture is ongoing | ||
350 | */ | ||
351 | if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0) | ||
352 | return -EBUSY; | ||
353 | |||
354 | /* | ||
355 | * Set the service_lines requested in the digitizer/slicer registers. | ||
356 | * Note, cx18_av_vbi() wipes some "impossible" service lines in the | ||
357 | * passed in fmt->fmt.sliced under valid calling conditions | ||
358 | */ | ||
359 | ret = v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &fmt->fmt.sliced); | ||
360 | if (ret) | ||
361 | return ret; | ||
362 | /* Store our current v4l2 sliced VBI settings */ | ||
363 | cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; | ||
364 | memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in)); | ||
365 | return 0; | ||
366 | } | ||
367 | |||
368 | static int cx18_g_chip_ident(struct file *file, void *fh, | ||
369 | struct v4l2_dbg_chip_ident *chip) | ||
370 | { | ||
371 | struct cx18 *cx = fh2id(fh)->cx; | ||
372 | int err = 0; | ||
373 | |||
374 | chip->ident = V4L2_IDENT_NONE; | ||
375 | chip->revision = 0; | ||
376 | switch (chip->match.type) { | ||
377 | case V4L2_CHIP_MATCH_HOST: | ||
378 | switch (chip->match.addr) { | ||
379 | case 0: | ||
380 | chip->ident = V4L2_IDENT_CX23418; | ||
381 | chip->revision = cx18_read_reg(cx, 0xC72028); | ||
382 | break; | ||
383 | case 1: | ||
384 | /* | ||
385 | * The A/V decoder is always present, but in the rare | ||
386 | * case that the card doesn't have analog, we don't | ||
387 | * use it. We find it w/o using the cx->sd_av pointer | ||
388 | */ | ||
389 | cx18_call_hw(cx, CX18_HW_418_AV, | ||
390 | core, g_chip_ident, chip); | ||
391 | break; | ||
392 | default: | ||
393 | /* | ||
394 | * Could return ident = V4L2_IDENT_UNKNOWN if we had | ||
395 | * other host chips at higher addresses, but we don't | ||
396 | */ | ||
397 | err = -EINVAL; /* per V4L2 spec */ | ||
398 | break; | ||
399 | } | ||
400 | break; | ||
401 | case V4L2_CHIP_MATCH_I2C_DRIVER: | ||
402 | /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */ | ||
403 | cx18_call_all(cx, core, g_chip_ident, chip); | ||
404 | break; | ||
405 | case V4L2_CHIP_MATCH_I2C_ADDR: | ||
406 | /* | ||
407 | * We could return V4L2_IDENT_UNKNOWN, but we don't do the work | ||
408 | * to look if a chip is at the address with no driver. That's a | ||
409 | * dangerous thing to do with EEPROMs anyway. | ||
410 | */ | ||
411 | cx18_call_all(cx, core, g_chip_ident, chip); | ||
412 | break; | ||
413 | default: | ||
414 | err = -EINVAL; | ||
415 | break; | ||
416 | } | ||
417 | return err; | ||
418 | } | ||
419 | |||
420 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
421 | static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg) | ||
422 | { | ||
423 | struct v4l2_dbg_register *regs = arg; | ||
424 | |||
425 | if (!capable(CAP_SYS_ADMIN)) | ||
426 | return -EPERM; | ||
427 | if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE) | ||
428 | return -EINVAL; | ||
429 | |||
430 | regs->size = 4; | ||
431 | if (cmd == VIDIOC_DBG_S_REGISTER) | ||
432 | cx18_write_enc(cx, regs->val, regs->reg); | ||
433 | else | ||
434 | regs->val = cx18_read_enc(cx, regs->reg); | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static int cx18_g_register(struct file *file, void *fh, | ||
439 | struct v4l2_dbg_register *reg) | ||
440 | { | ||
441 | struct cx18 *cx = fh2id(fh)->cx; | ||
442 | |||
443 | if (v4l2_chip_match_host(®->match)) | ||
444 | return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg); | ||
445 | /* FIXME - errors shouldn't be ignored */ | ||
446 | cx18_call_all(cx, core, g_register, reg); | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static int cx18_s_register(struct file *file, void *fh, | ||
451 | struct v4l2_dbg_register *reg) | ||
452 | { | ||
453 | struct cx18 *cx = fh2id(fh)->cx; | ||
454 | |||
455 | if (v4l2_chip_match_host(®->match)) | ||
456 | return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg); | ||
457 | /* FIXME - errors shouldn't be ignored */ | ||
458 | cx18_call_all(cx, core, s_register, reg); | ||
459 | return 0; | ||
460 | } | ||
461 | #endif | ||
462 | |||
463 | static int cx18_querycap(struct file *file, void *fh, | ||
464 | struct v4l2_capability *vcap) | ||
465 | { | ||
466 | struct cx18 *cx = fh2id(fh)->cx; | ||
467 | |||
468 | strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver)); | ||
469 | strlcpy(vcap->card, cx->card_name, sizeof(vcap->card)); | ||
470 | snprintf(vcap->bus_info, sizeof(vcap->bus_info), | ||
471 | "PCI:%s", pci_name(cx->pci_dev)); | ||
472 | vcap->capabilities = cx->v4l2_cap; /* capabilities */ | ||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | static int cx18_enumaudio(struct file *file, void *fh, struct v4l2_audio *vin) | ||
477 | { | ||
478 | struct cx18 *cx = fh2id(fh)->cx; | ||
479 | |||
480 | return cx18_get_audio_input(cx, vin->index, vin); | ||
481 | } | ||
482 | |||
483 | static int cx18_g_audio(struct file *file, void *fh, struct v4l2_audio *vin) | ||
484 | { | ||
485 | struct cx18 *cx = fh2id(fh)->cx; | ||
486 | |||
487 | vin->index = cx->audio_input; | ||
488 | return cx18_get_audio_input(cx, vin->index, vin); | ||
489 | } | ||
490 | |||
491 | static int cx18_s_audio(struct file *file, void *fh, struct v4l2_audio *vout) | ||
492 | { | ||
493 | struct cx18 *cx = fh2id(fh)->cx; | ||
494 | |||
495 | if (vout->index >= cx->nof_audio_inputs) | ||
496 | return -EINVAL; | ||
497 | cx->audio_input = vout->index; | ||
498 | cx18_audio_set_io(cx); | ||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | static int cx18_enum_input(struct file *file, void *fh, struct v4l2_input *vin) | ||
503 | { | ||
504 | struct cx18 *cx = fh2id(fh)->cx; | ||
505 | |||
506 | /* set it to defaults from our table */ | ||
507 | return cx18_get_input(cx, vin->index, vin); | ||
508 | } | ||
509 | |||
510 | static int cx18_cropcap(struct file *file, void *fh, | ||
511 | struct v4l2_cropcap *cropcap) | ||
512 | { | ||
513 | struct cx18 *cx = fh2id(fh)->cx; | ||
514 | |||
515 | if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
516 | return -EINVAL; | ||
517 | cropcap->bounds.top = cropcap->bounds.left = 0; | ||
518 | cropcap->bounds.width = 720; | ||
519 | cropcap->bounds.height = cx->is_50hz ? 576 : 480; | ||
520 | cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10; | ||
521 | cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11; | ||
522 | cropcap->defrect = cropcap->bounds; | ||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop) | ||
527 | { | ||
528 | struct cx18_open_id *id = fh2id(fh); | ||
529 | struct cx18 *cx = id->cx; | ||
530 | |||
531 | if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
532 | return -EINVAL; | ||
533 | CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n"); | ||
534 | return -EINVAL; | ||
535 | } | ||
536 | |||
537 | static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop) | ||
538 | { | ||
539 | struct cx18 *cx = fh2id(fh)->cx; | ||
540 | |||
541 | if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | ||
542 | return -EINVAL; | ||
543 | CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n"); | ||
544 | return -EINVAL; | ||
545 | } | ||
546 | |||
547 | static int cx18_enum_fmt_vid_cap(struct file *file, void *fh, | ||
548 | struct v4l2_fmtdesc *fmt) | ||
549 | { | ||
550 | static const struct v4l2_fmtdesc formats[] = { | ||
551 | { 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, | ||
552 | "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, { 0, 0, 0, 0 } | ||
553 | }, | ||
554 | { 1, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FMT_FLAG_COMPRESSED, | ||
555 | "MPEG", V4L2_PIX_FMT_MPEG, { 0, 0, 0, 0 } | ||
556 | }, | ||
557 | { 2, V4L2_BUF_TYPE_VIDEO_CAPTURE, 0, | ||
558 | "UYVY 4:2:2", V4L2_PIX_FMT_UYVY, { 0, 0, 0, 0 } | ||
559 | }, | ||
560 | }; | ||
561 | |||
562 | if (fmt->index > ARRAY_SIZE(formats) - 1) | ||
563 | return -EINVAL; | ||
564 | *fmt = formats[fmt->index]; | ||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | static int cx18_g_input(struct file *file, void *fh, unsigned int *i) | ||
569 | { | ||
570 | struct cx18 *cx = fh2id(fh)->cx; | ||
571 | |||
572 | *i = cx->active_input; | ||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | int cx18_s_input(struct file *file, void *fh, unsigned int inp) | ||
577 | { | ||
578 | struct cx18_open_id *id = fh2id(fh); | ||
579 | struct cx18 *cx = id->cx; | ||
580 | |||
581 | if (inp >= cx->nof_inputs) | ||
582 | return -EINVAL; | ||
583 | |||
584 | if (inp == cx->active_input) { | ||
585 | CX18_DEBUG_INFO("Input unchanged\n"); | ||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | CX18_DEBUG_INFO("Changing input from %d to %d\n", | ||
590 | cx->active_input, inp); | ||
591 | |||
592 | cx->active_input = inp; | ||
593 | /* Set the audio input to whatever is appropriate for the input type. */ | ||
594 | cx->audio_input = cx->card->video_inputs[inp].audio_index; | ||
595 | |||
596 | /* prevent others from messing with the streams until | ||
597 | we're finished changing inputs. */ | ||
598 | cx18_mute(cx); | ||
599 | cx18_video_set_io(cx); | ||
600 | cx18_audio_set_io(cx); | ||
601 | cx18_unmute(cx); | ||
602 | return 0; | ||
603 | } | ||
604 | |||
605 | static int cx18_g_frequency(struct file *file, void *fh, | ||
606 | struct v4l2_frequency *vf) | ||
607 | { | ||
608 | struct cx18 *cx = fh2id(fh)->cx; | ||
609 | |||
610 | if (vf->tuner != 0) | ||
611 | return -EINVAL; | ||
612 | |||
613 | cx18_call_all(cx, tuner, g_frequency, vf); | ||
614 | return 0; | ||
615 | } | ||
616 | |||
617 | int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf) | ||
618 | { | ||
619 | struct cx18_open_id *id = fh2id(fh); | ||
620 | struct cx18 *cx = id->cx; | ||
621 | |||
622 | if (vf->tuner != 0) | ||
623 | return -EINVAL; | ||
624 | |||
625 | cx18_mute(cx); | ||
626 | CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency); | ||
627 | cx18_call_all(cx, tuner, s_frequency, vf); | ||
628 | cx18_unmute(cx); | ||
629 | return 0; | ||
630 | } | ||
631 | |||
632 | static int cx18_g_std(struct file *file, void *fh, v4l2_std_id *std) | ||
633 | { | ||
634 | struct cx18 *cx = fh2id(fh)->cx; | ||
635 | |||
636 | *std = cx->std; | ||
637 | return 0; | ||
638 | } | ||
639 | |||
640 | int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) | ||
641 | { | ||
642 | struct cx18_open_id *id = fh2id(fh); | ||
643 | struct cx18 *cx = id->cx; | ||
644 | |||
645 | if ((*std & V4L2_STD_ALL) == 0) | ||
646 | return -EINVAL; | ||
647 | |||
648 | if (*std == cx->std) | ||
649 | return 0; | ||
650 | |||
651 | if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) || | ||
652 | atomic_read(&cx->ana_capturing) > 0) { | ||
653 | /* Switching standard would turn off the radio or mess | ||
654 | with already running streams, prevent that by | ||
655 | returning EBUSY. */ | ||
656 | return -EBUSY; | ||
657 | } | ||
658 | |||
659 | cx->std = *std; | ||
660 | cx->is_60hz = (*std & V4L2_STD_525_60) ? 1 : 0; | ||
661 | cx->is_50hz = !cx->is_60hz; | ||
662 | cx2341x_handler_set_50hz(&cx->cxhdl, cx->is_50hz); | ||
663 | cx->cxhdl.width = 720; | ||
664 | cx->cxhdl.height = cx->is_50hz ? 576 : 480; | ||
665 | cx->vbi.count = cx->is_50hz ? 18 : 12; | ||
666 | cx->vbi.start[0] = cx->is_50hz ? 6 : 10; | ||
667 | cx->vbi.start[1] = cx->is_50hz ? 318 : 273; | ||
668 | CX18_DEBUG_INFO("Switching standard to %llx.\n", | ||
669 | (unsigned long long) cx->std); | ||
670 | |||
671 | /* Tuner */ | ||
672 | cx18_call_all(cx, core, s_std, cx->std); | ||
673 | return 0; | ||
674 | } | ||
675 | |||
676 | static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) | ||
677 | { | ||
678 | struct cx18_open_id *id = fh2id(fh); | ||
679 | struct cx18 *cx = id->cx; | ||
680 | |||
681 | if (vt->index != 0) | ||
682 | return -EINVAL; | ||
683 | |||
684 | cx18_call_all(cx, tuner, s_tuner, vt); | ||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt) | ||
689 | { | ||
690 | struct cx18 *cx = fh2id(fh)->cx; | ||
691 | |||
692 | if (vt->index != 0) | ||
693 | return -EINVAL; | ||
694 | |||
695 | cx18_call_all(cx, tuner, g_tuner, vt); | ||
696 | |||
697 | if (vt->type == V4L2_TUNER_RADIO) | ||
698 | strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name)); | ||
699 | else | ||
700 | strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name)); | ||
701 | return 0; | ||
702 | } | ||
703 | |||
704 | static int cx18_g_sliced_vbi_cap(struct file *file, void *fh, | ||
705 | struct v4l2_sliced_vbi_cap *cap) | ||
706 | { | ||
707 | struct cx18 *cx = fh2id(fh)->cx; | ||
708 | int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; | ||
709 | int f, l; | ||
710 | |||
711 | if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) | ||
712 | return -EINVAL; | ||
713 | |||
714 | cap->service_set = 0; | ||
715 | for (f = 0; f < 2; f++) { | ||
716 | for (l = 0; l < 24; l++) { | ||
717 | if (valid_service_line(f, l, cx->is_50hz)) { | ||
718 | /* | ||
719 | * We can find all v4l2 supported vbi services | ||
720 | * for the standard, on a valid line for the std | ||
721 | */ | ||
722 | cap->service_lines[f][l] = set; | ||
723 | cap->service_set |= set; | ||
724 | } else | ||
725 | cap->service_lines[f][l] = 0; | ||
726 | } | ||
727 | } | ||
728 | for (f = 0; f < 3; f++) | ||
729 | cap->reserved[f] = 0; | ||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static int _cx18_process_idx_data(struct cx18_buffer *buf, | ||
734 | struct v4l2_enc_idx *idx) | ||
735 | { | ||
736 | int consumed, remaining; | ||
737 | struct v4l2_enc_idx_entry *e_idx; | ||
738 | struct cx18_enc_idx_entry *e_buf; | ||
739 | |||
740 | /* Frame type lookup: 1=I, 2=P, 4=B */ | ||
741 | const int mapping[8] = { | ||
742 | -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, | ||
743 | -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1 | ||
744 | }; | ||
745 | |||
746 | /* | ||
747 | * Assumption here is that a buf holds an integral number of | ||
748 | * struct cx18_enc_idx_entry objects and is properly aligned. | ||
749 | * This is enforced by the module options on IDX buffer sizes. | ||
750 | */ | ||
751 | remaining = buf->bytesused - buf->readpos; | ||
752 | consumed = 0; | ||
753 | e_idx = &idx->entry[idx->entries]; | ||
754 | e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos]; | ||
755 | |||
756 | while (remaining >= sizeof(struct cx18_enc_idx_entry) && | ||
757 | idx->entries < V4L2_ENC_IDX_ENTRIES) { | ||
758 | |||
759 | e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32) | ||
760 | | le32_to_cpu(e_buf->offset_low); | ||
761 | |||
762 | e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32) | ||
763 | | le32_to_cpu(e_buf->pts_low); | ||
764 | |||
765 | e_idx->length = le32_to_cpu(e_buf->length); | ||
766 | |||
767 | e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7]; | ||
768 | |||
769 | e_idx->reserved[0] = 0; | ||
770 | e_idx->reserved[1] = 0; | ||
771 | |||
772 | idx->entries++; | ||
773 | e_idx = &idx->entry[idx->entries]; | ||
774 | e_buf++; | ||
775 | |||
776 | remaining -= sizeof(struct cx18_enc_idx_entry); | ||
777 | consumed += sizeof(struct cx18_enc_idx_entry); | ||
778 | } | ||
779 | |||
780 | /* Swallow any partial entries at the end, if there are any */ | ||
781 | if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry)) | ||
782 | consumed += remaining; | ||
783 | |||
784 | buf->readpos += consumed; | ||
785 | return consumed; | ||
786 | } | ||
787 | |||
788 | static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl, | ||
789 | struct v4l2_enc_idx *idx) | ||
790 | { | ||
791 | if (s->type != CX18_ENC_STREAM_TYPE_IDX) | ||
792 | return -EINVAL; | ||
793 | |||
794 | if (mdl->curr_buf == NULL) | ||
795 | mdl->curr_buf = list_first_entry(&mdl->buf_list, | ||
796 | struct cx18_buffer, list); | ||
797 | |||
798 | if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) { | ||
799 | /* | ||
800 | * For some reason we've exhausted the buffers, but the MDL | ||
801 | * object still said some data was unread. | ||
802 | * Fix that and bail out. | ||
803 | */ | ||
804 | mdl->readpos = mdl->bytesused; | ||
805 | return 0; | ||
806 | } | ||
807 | |||
808 | list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) { | ||
809 | |||
810 | /* Skip any empty buffers in the MDL */ | ||
811 | if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused) | ||
812 | continue; | ||
813 | |||
814 | mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx); | ||
815 | |||
816 | /* exit when MDL drained or request satisfied */ | ||
817 | if (idx->entries >= V4L2_ENC_IDX_ENTRIES || | ||
818 | mdl->curr_buf->readpos < mdl->curr_buf->bytesused || | ||
819 | mdl->readpos >= mdl->bytesused) | ||
820 | break; | ||
821 | } | ||
822 | return 0; | ||
823 | } | ||
824 | |||
825 | static int cx18_g_enc_index(struct file *file, void *fh, | ||
826 | struct v4l2_enc_idx *idx) | ||
827 | { | ||
828 | struct cx18 *cx = fh2id(fh)->cx; | ||
829 | struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; | ||
830 | s32 tmp; | ||
831 | struct cx18_mdl *mdl; | ||
832 | |||
833 | if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */ | ||
834 | return -EINVAL; | ||
835 | |||
836 | /* Compute the best case number of entries we can buffer */ | ||
837 | tmp = s->buffers - | ||
838 | s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN; | ||
839 | if (tmp <= 0) | ||
840 | tmp = 1; | ||
841 | tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry); | ||
842 | |||
843 | /* Fill out the header of the return structure */ | ||
844 | idx->entries = 0; | ||
845 | idx->entries_cap = tmp; | ||
846 | memset(idx->reserved, 0, sizeof(idx->reserved)); | ||
847 | |||
848 | /* Pull IDX MDLs and buffers from q_full and populate the entries */ | ||
849 | do { | ||
850 | mdl = cx18_dequeue(s, &s->q_full); | ||
851 | if (mdl == NULL) /* No more IDX data right now */ | ||
852 | break; | ||
853 | |||
854 | /* Extract the Index entry data from the MDL and buffers */ | ||
855 | cx18_process_idx_data(s, mdl, idx); | ||
856 | if (mdl->readpos < mdl->bytesused) { | ||
857 | /* We finished with data remaining, push the MDL back */ | ||
858 | cx18_push(s, mdl, &s->q_full); | ||
859 | break; | ||
860 | } | ||
861 | |||
862 | /* We drained this MDL, schedule it to go to the firmware */ | ||
863 | cx18_enqueue(s, mdl, &s->q_free); | ||
864 | |||
865 | } while (idx->entries < V4L2_ENC_IDX_ENTRIES); | ||
866 | |||
867 | /* Tell the work handler to send free IDX MDLs to the firmware */ | ||
868 | cx18_stream_load_fw_queue(s); | ||
869 | return 0; | ||
870 | } | ||
871 | |||
872 | static struct videobuf_queue *cx18_vb_queue(struct cx18_open_id *id) | ||
873 | { | ||
874 | struct videobuf_queue *q = NULL; | ||
875 | struct cx18 *cx = id->cx; | ||
876 | struct cx18_stream *s = &cx->streams[id->type]; | ||
877 | |||
878 | switch (s->vb_type) { | ||
879 | case V4L2_BUF_TYPE_VIDEO_CAPTURE: | ||
880 | q = &s->vbuf_q; | ||
881 | break; | ||
882 | case V4L2_BUF_TYPE_VBI_CAPTURE: | ||
883 | break; | ||
884 | default: | ||
885 | break; | ||
886 | } | ||
887 | return q; | ||
888 | } | ||
889 | |||
890 | static int cx18_streamon(struct file *file, void *priv, | ||
891 | enum v4l2_buf_type type) | ||
892 | { | ||
893 | struct cx18_open_id *id = file->private_data; | ||
894 | struct cx18 *cx = id->cx; | ||
895 | struct cx18_stream *s = &cx->streams[id->type]; | ||
896 | |||
897 | /* Start the hardware only if we're the video device */ | ||
898 | if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
899 | (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
900 | return -EINVAL; | ||
901 | |||
902 | if (id->type != CX18_ENC_STREAM_TYPE_YUV) | ||
903 | return -EINVAL; | ||
904 | |||
905 | /* Establish a buffer timeout */ | ||
906 | mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies); | ||
907 | |||
908 | return videobuf_streamon(cx18_vb_queue(id)); | ||
909 | } | ||
910 | |||
911 | static int cx18_streamoff(struct file *file, void *priv, | ||
912 | enum v4l2_buf_type type) | ||
913 | { | ||
914 | struct cx18_open_id *id = file->private_data; | ||
915 | struct cx18 *cx = id->cx; | ||
916 | struct cx18_stream *s = &cx->streams[id->type]; | ||
917 | |||
918 | /* Start the hardware only if we're the video device */ | ||
919 | if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
920 | (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
921 | return -EINVAL; | ||
922 | |||
923 | if (id->type != CX18_ENC_STREAM_TYPE_YUV) | ||
924 | return -EINVAL; | ||
925 | |||
926 | return videobuf_streamoff(cx18_vb_queue(id)); | ||
927 | } | ||
928 | |||
929 | static int cx18_reqbufs(struct file *file, void *priv, | ||
930 | struct v4l2_requestbuffers *rb) | ||
931 | { | ||
932 | struct cx18_open_id *id = file->private_data; | ||
933 | struct cx18 *cx = id->cx; | ||
934 | struct cx18_stream *s = &cx->streams[id->type]; | ||
935 | |||
936 | if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
937 | (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
938 | return -EINVAL; | ||
939 | |||
940 | return videobuf_reqbufs(cx18_vb_queue(id), rb); | ||
941 | } | ||
942 | |||
943 | static int cx18_querybuf(struct file *file, void *priv, | ||
944 | struct v4l2_buffer *b) | ||
945 | { | ||
946 | struct cx18_open_id *id = file->private_data; | ||
947 | struct cx18 *cx = id->cx; | ||
948 | struct cx18_stream *s = &cx->streams[id->type]; | ||
949 | |||
950 | if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
951 | (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
952 | return -EINVAL; | ||
953 | |||
954 | return videobuf_querybuf(cx18_vb_queue(id), b); | ||
955 | } | ||
956 | |||
957 | static int cx18_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) | ||
958 | { | ||
959 | struct cx18_open_id *id = file->private_data; | ||
960 | struct cx18 *cx = id->cx; | ||
961 | struct cx18_stream *s = &cx->streams[id->type]; | ||
962 | |||
963 | if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
964 | (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
965 | return -EINVAL; | ||
966 | |||
967 | return videobuf_qbuf(cx18_vb_queue(id), b); | ||
968 | } | ||
969 | |||
970 | static int cx18_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) | ||
971 | { | ||
972 | struct cx18_open_id *id = file->private_data; | ||
973 | struct cx18 *cx = id->cx; | ||
974 | struct cx18_stream *s = &cx->streams[id->type]; | ||
975 | |||
976 | if ((s->vb_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && | ||
977 | (s->vb_type != V4L2_BUF_TYPE_VBI_CAPTURE)) | ||
978 | return -EINVAL; | ||
979 | |||
980 | return videobuf_dqbuf(cx18_vb_queue(id), b, file->f_flags & O_NONBLOCK); | ||
981 | } | ||
982 | |||
983 | static int cx18_encoder_cmd(struct file *file, void *fh, | ||
984 | struct v4l2_encoder_cmd *enc) | ||
985 | { | ||
986 | struct cx18_open_id *id = fh2id(fh); | ||
987 | struct cx18 *cx = id->cx; | ||
988 | u32 h; | ||
989 | |||
990 | switch (enc->cmd) { | ||
991 | case V4L2_ENC_CMD_START: | ||
992 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); | ||
993 | enc->flags = 0; | ||
994 | return cx18_start_capture(id); | ||
995 | |||
996 | case V4L2_ENC_CMD_STOP: | ||
997 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); | ||
998 | enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; | ||
999 | cx18_stop_capture(id, | ||
1000 | enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); | ||
1001 | break; | ||
1002 | |||
1003 | case V4L2_ENC_CMD_PAUSE: | ||
1004 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); | ||
1005 | enc->flags = 0; | ||
1006 | if (!atomic_read(&cx->ana_capturing)) | ||
1007 | return -EPERM; | ||
1008 | if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) | ||
1009 | return 0; | ||
1010 | h = cx18_find_handle(cx); | ||
1011 | if (h == CX18_INVALID_TASK_HANDLE) { | ||
1012 | CX18_ERR("Can't find valid task handle for " | ||
1013 | "V4L2_ENC_CMD_PAUSE\n"); | ||
1014 | return -EBADFD; | ||
1015 | } | ||
1016 | cx18_mute(cx); | ||
1017 | cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, h); | ||
1018 | break; | ||
1019 | |||
1020 | case V4L2_ENC_CMD_RESUME: | ||
1021 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); | ||
1022 | enc->flags = 0; | ||
1023 | if (!atomic_read(&cx->ana_capturing)) | ||
1024 | return -EPERM; | ||
1025 | if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags)) | ||
1026 | return 0; | ||
1027 | h = cx18_find_handle(cx); | ||
1028 | if (h == CX18_INVALID_TASK_HANDLE) { | ||
1029 | CX18_ERR("Can't find valid task handle for " | ||
1030 | "V4L2_ENC_CMD_RESUME\n"); | ||
1031 | return -EBADFD; | ||
1032 | } | ||
1033 | cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, h); | ||
1034 | cx18_unmute(cx); | ||
1035 | break; | ||
1036 | |||
1037 | default: | ||
1038 | CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); | ||
1039 | return -EINVAL; | ||
1040 | } | ||
1041 | return 0; | ||
1042 | } | ||
1043 | |||
1044 | static int cx18_try_encoder_cmd(struct file *file, void *fh, | ||
1045 | struct v4l2_encoder_cmd *enc) | ||
1046 | { | ||
1047 | struct cx18 *cx = fh2id(fh)->cx; | ||
1048 | |||
1049 | switch (enc->cmd) { | ||
1050 | case V4L2_ENC_CMD_START: | ||
1051 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_START\n"); | ||
1052 | enc->flags = 0; | ||
1053 | break; | ||
1054 | |||
1055 | case V4L2_ENC_CMD_STOP: | ||
1056 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_STOP\n"); | ||
1057 | enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; | ||
1058 | break; | ||
1059 | |||
1060 | case V4L2_ENC_CMD_PAUSE: | ||
1061 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_PAUSE\n"); | ||
1062 | enc->flags = 0; | ||
1063 | break; | ||
1064 | |||
1065 | case V4L2_ENC_CMD_RESUME: | ||
1066 | CX18_DEBUG_IOCTL("V4L2_ENC_CMD_RESUME\n"); | ||
1067 | enc->flags = 0; | ||
1068 | break; | ||
1069 | |||
1070 | default: | ||
1071 | CX18_DEBUG_IOCTL("Unknown cmd %d\n", enc->cmd); | ||
1072 | return -EINVAL; | ||
1073 | } | ||
1074 | return 0; | ||
1075 | } | ||
1076 | |||
1077 | static int cx18_log_status(struct file *file, void *fh) | ||
1078 | { | ||
1079 | struct cx18 *cx = fh2id(fh)->cx; | ||
1080 | struct v4l2_input vidin; | ||
1081 | struct v4l2_audio audin; | ||
1082 | int i; | ||
1083 | |||
1084 | CX18_INFO("================= START STATUS CARD #%d " | ||
1085 | "=================\n", cx->instance); | ||
1086 | CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name); | ||
1087 | if (cx->hw_flags & CX18_HW_TVEEPROM) { | ||
1088 | struct tveeprom tv; | ||
1089 | |||
1090 | cx18_read_eeprom(cx, &tv); | ||
1091 | } | ||
1092 | cx18_call_all(cx, core, log_status); | ||
1093 | cx18_get_input(cx, cx->active_input, &vidin); | ||
1094 | cx18_get_audio_input(cx, cx->audio_input, &audin); | ||
1095 | CX18_INFO("Video Input: %s\n", vidin.name); | ||
1096 | CX18_INFO("Audio Input: %s\n", audin.name); | ||
1097 | mutex_lock(&cx->gpio_lock); | ||
1098 | CX18_INFO("GPIO: direction 0x%08x, value 0x%08x\n", | ||
1099 | cx->gpio_dir, cx->gpio_val); | ||
1100 | mutex_unlock(&cx->gpio_lock); | ||
1101 | CX18_INFO("Tuner: %s\n", | ||
1102 | test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV"); | ||
1103 | v4l2_ctrl_handler_log_status(&cx->cxhdl.hdl, cx->v4l2_dev.name); | ||
1104 | CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags); | ||
1105 | for (i = 0; i < CX18_MAX_STREAMS; i++) { | ||
1106 | struct cx18_stream *s = &cx->streams[i]; | ||
1107 | |||
1108 | if (s->video_dev == NULL || s->buffers == 0) | ||
1109 | continue; | ||
1110 | CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", | ||
1111 | s->name, s->s_flags, | ||
1112 | atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100 | ||
1113 | / s->buffers, | ||
1114 | (s->buffers * s->buf_size) / 1024, s->buffers); | ||
1115 | } | ||
1116 | CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", | ||
1117 | (long long)cx->mpg_data_received, | ||
1118 | (long long)cx->vbi_data_inserted); | ||
1119 | CX18_INFO("================== END STATUS CARD #%d " | ||
1120 | "==================\n", cx->instance); | ||
1121 | return 0; | ||
1122 | } | ||
1123 | |||
1124 | static long cx18_default(struct file *file, void *fh, bool valid_prio, | ||
1125 | int cmd, void *arg) | ||
1126 | { | ||
1127 | struct cx18 *cx = fh2id(fh)->cx; | ||
1128 | |||
1129 | switch (cmd) { | ||
1130 | case VIDIOC_INT_RESET: { | ||
1131 | u32 val = *(u32 *)arg; | ||
1132 | |||
1133 | if ((val == 0) || (val & 0x01)) | ||
1134 | cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset, | ||
1135 | (u32) CX18_GPIO_RESET_Z8F0811); | ||
1136 | break; | ||
1137 | } | ||
1138 | |||
1139 | default: | ||
1140 | return -EINVAL; | ||
1141 | } | ||
1142 | return 0; | ||
1143 | } | ||
1144 | |||
1145 | long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, | ||
1146 | unsigned long arg) | ||
1147 | { | ||
1148 | struct video_device *vfd = video_devdata(filp); | ||
1149 | struct cx18_open_id *id = file2id(filp); | ||
1150 | struct cx18 *cx = id->cx; | ||
1151 | long res; | ||
1152 | |||
1153 | mutex_lock(&cx->serialize_lock); | ||
1154 | |||
1155 | if (cx18_debug & CX18_DBGFLG_IOCTL) | ||
1156 | vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; | ||
1157 | res = video_ioctl2(filp, cmd, arg); | ||
1158 | vfd->debug = 0; | ||
1159 | mutex_unlock(&cx->serialize_lock); | ||
1160 | return res; | ||
1161 | } | ||
1162 | |||
1163 | static const struct v4l2_ioctl_ops cx18_ioctl_ops = { | ||
1164 | .vidioc_querycap = cx18_querycap, | ||
1165 | .vidioc_s_audio = cx18_s_audio, | ||
1166 | .vidioc_g_audio = cx18_g_audio, | ||
1167 | .vidioc_enumaudio = cx18_enumaudio, | ||
1168 | .vidioc_enum_input = cx18_enum_input, | ||
1169 | .vidioc_cropcap = cx18_cropcap, | ||
1170 | .vidioc_s_crop = cx18_s_crop, | ||
1171 | .vidioc_g_crop = cx18_g_crop, | ||
1172 | .vidioc_g_input = cx18_g_input, | ||
1173 | .vidioc_s_input = cx18_s_input, | ||
1174 | .vidioc_g_frequency = cx18_g_frequency, | ||
1175 | .vidioc_s_frequency = cx18_s_frequency, | ||
1176 | .vidioc_s_tuner = cx18_s_tuner, | ||
1177 | .vidioc_g_tuner = cx18_g_tuner, | ||
1178 | .vidioc_g_enc_index = cx18_g_enc_index, | ||
1179 | .vidioc_g_std = cx18_g_std, | ||
1180 | .vidioc_s_std = cx18_s_std, | ||
1181 | .vidioc_log_status = cx18_log_status, | ||
1182 | .vidioc_enum_fmt_vid_cap = cx18_enum_fmt_vid_cap, | ||
1183 | .vidioc_encoder_cmd = cx18_encoder_cmd, | ||
1184 | .vidioc_try_encoder_cmd = cx18_try_encoder_cmd, | ||
1185 | .vidioc_g_fmt_vid_cap = cx18_g_fmt_vid_cap, | ||
1186 | .vidioc_g_fmt_vbi_cap = cx18_g_fmt_vbi_cap, | ||
1187 | .vidioc_g_fmt_sliced_vbi_cap = cx18_g_fmt_sliced_vbi_cap, | ||
1188 | .vidioc_s_fmt_vid_cap = cx18_s_fmt_vid_cap, | ||
1189 | .vidioc_s_fmt_vbi_cap = cx18_s_fmt_vbi_cap, | ||
1190 | .vidioc_s_fmt_sliced_vbi_cap = cx18_s_fmt_sliced_vbi_cap, | ||
1191 | .vidioc_try_fmt_vid_cap = cx18_try_fmt_vid_cap, | ||
1192 | .vidioc_try_fmt_vbi_cap = cx18_try_fmt_vbi_cap, | ||
1193 | .vidioc_try_fmt_sliced_vbi_cap = cx18_try_fmt_sliced_vbi_cap, | ||
1194 | .vidioc_g_sliced_vbi_cap = cx18_g_sliced_vbi_cap, | ||
1195 | .vidioc_g_chip_ident = cx18_g_chip_ident, | ||
1196 | #ifdef CONFIG_VIDEO_ADV_DEBUG | ||
1197 | .vidioc_g_register = cx18_g_register, | ||
1198 | .vidioc_s_register = cx18_s_register, | ||
1199 | #endif | ||
1200 | .vidioc_default = cx18_default, | ||
1201 | .vidioc_streamon = cx18_streamon, | ||
1202 | .vidioc_streamoff = cx18_streamoff, | ||
1203 | .vidioc_reqbufs = cx18_reqbufs, | ||
1204 | .vidioc_querybuf = cx18_querybuf, | ||
1205 | .vidioc_qbuf = cx18_qbuf, | ||
1206 | .vidioc_dqbuf = cx18_dqbuf, | ||
1207 | }; | ||
1208 | |||
1209 | void cx18_set_funcs(struct video_device *vdev) | ||
1210 | { | ||
1211 | vdev->ioctl_ops = &cx18_ioctl_ops; | ||
1212 | } | ||
diff --git a/drivers/media/video/cx18/cx18-ioctl.h b/drivers/media/video/cx18/cx18-ioctl.h new file mode 100644 index 00000000000..dcb2559ad52 --- /dev/null +++ b/drivers/media/video/cx18/cx18-ioctl.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * cx18 ioctl system call | ||
3 | * | ||
4 | * Derived from ivtv-ioctl.h | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | u16 cx18_service2vbi(int type); | ||
26 | void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); | ||
27 | u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt); | ||
28 | void cx18_set_funcs(struct video_device *vdev); | ||
29 | int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std); | ||
30 | int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf); | ||
31 | int cx18_s_input(struct file *file, void *fh, unsigned int inp); | ||
32 | long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd, | ||
33 | unsigned long arg); | ||
diff --git a/drivers/media/video/cx18/cx18-irq.c b/drivers/media/video/cx18/cx18-irq.c new file mode 100644 index 00000000000..80edfe93a3d --- /dev/null +++ b/drivers/media/video/cx18/cx18-irq.c | |||
@@ -0,0 +1,81 @@ | |||
1 | /* | ||
2 | * cx18 interrupt handling | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include "cx18-driver.h" | ||
24 | #include "cx18-io.h" | ||
25 | #include "cx18-irq.h" | ||
26 | #include "cx18-mailbox.h" | ||
27 | #include "cx18-scb.h" | ||
28 | |||
29 | static void xpu_ack(struct cx18 *cx, u32 sw2) | ||
30 | { | ||
31 | if (sw2 & IRQ_CPU_TO_EPU_ACK) | ||
32 | wake_up(&cx->mb_cpu_waitq); | ||
33 | if (sw2 & IRQ_APU_TO_EPU_ACK) | ||
34 | wake_up(&cx->mb_apu_waitq); | ||
35 | } | ||
36 | |||
37 | static void epu_cmd(struct cx18 *cx, u32 sw1) | ||
38 | { | ||
39 | if (sw1 & IRQ_CPU_TO_EPU) | ||
40 | cx18_api_epu_cmd_irq(cx, CPU); | ||
41 | if (sw1 & IRQ_APU_TO_EPU) | ||
42 | cx18_api_epu_cmd_irq(cx, APU); | ||
43 | } | ||
44 | |||
45 | irqreturn_t cx18_irq_handler(int irq, void *dev_id) | ||
46 | { | ||
47 | struct cx18 *cx = (struct cx18 *)dev_id; | ||
48 | u32 sw1, sw2, hw2; | ||
49 | |||
50 | sw1 = cx18_read_reg(cx, SW1_INT_STATUS) & cx->sw1_irq_mask; | ||
51 | sw2 = cx18_read_reg(cx, SW2_INT_STATUS) & cx->sw2_irq_mask; | ||
52 | hw2 = cx18_read_reg(cx, HW2_INT_CLR_STATUS) & cx->hw2_irq_mask; | ||
53 | |||
54 | if (sw1) | ||
55 | cx18_write_reg_expect(cx, sw1, SW1_INT_STATUS, ~sw1, sw1); | ||
56 | if (sw2) | ||
57 | cx18_write_reg_expect(cx, sw2, SW2_INT_STATUS, ~sw2, sw2); | ||
58 | if (hw2) | ||
59 | cx18_write_reg_expect(cx, hw2, HW2_INT_CLR_STATUS, ~hw2, hw2); | ||
60 | |||
61 | if (sw1 || sw2 || hw2) | ||
62 | CX18_DEBUG_HI_IRQ("received interrupts " | ||
63 | "SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2); | ||
64 | |||
65 | /* | ||
66 | * SW1 responses have to happen first. The sending XPU times out the | ||
67 | * incoming mailboxes on us rather rapidly. | ||
68 | */ | ||
69 | if (sw1) | ||
70 | epu_cmd(cx, sw1); | ||
71 | |||
72 | /* To do: interrupt-based I2C handling | ||
73 | if (hw2 & (HW2_I2C1_INT|HW2_I2C2_INT)) { | ||
74 | } | ||
75 | */ | ||
76 | |||
77 | if (sw2) | ||
78 | xpu_ack(cx, sw2); | ||
79 | |||
80 | return (sw1 || sw2 || hw2) ? IRQ_HANDLED : IRQ_NONE; | ||
81 | } | ||
diff --git a/drivers/media/video/cx18/cx18-irq.h b/drivers/media/video/cx18/cx18-irq.h new file mode 100644 index 00000000000..30e7eaf8cb5 --- /dev/null +++ b/drivers/media/video/cx18/cx18-irq.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * cx18 interrupt handling | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #define HW2_I2C1_INT (1 << 22) | ||
24 | #define HW2_I2C2_INT (1 << 23) | ||
25 | #define HW2_INT_CLR_STATUS 0xc730c4 | ||
26 | #define HW2_INT_MASK5_PCI 0xc730e4 | ||
27 | #define SW1_INT_SET 0xc73100 | ||
28 | #define SW1_INT_STATUS 0xc73104 | ||
29 | #define SW1_INT_ENABLE_PCI 0xc7311c | ||
30 | #define SW2_INT_SET 0xc73140 | ||
31 | #define SW2_INT_STATUS 0xc73144 | ||
32 | #define SW2_INT_ENABLE_CPU 0xc73158 | ||
33 | #define SW2_INT_ENABLE_PCI 0xc7315c | ||
34 | |||
35 | irqreturn_t cx18_irq_handler(int irq, void *dev_id); | ||
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c new file mode 100644 index 00000000000..c07191e09fc --- /dev/null +++ b/drivers/media/video/cx18/cx18-mailbox.c | |||
@@ -0,0 +1,869 @@ | |||
1 | /* | ||
2 | * cx18 mailbox functions | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include <stdarg.h> | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | #include "cx18-io.h" | ||
27 | #include "cx18-scb.h" | ||
28 | #include "cx18-irq.h" | ||
29 | #include "cx18-mailbox.h" | ||
30 | #include "cx18-queue.h" | ||
31 | #include "cx18-streams.h" | ||
32 | #include "cx18-alsa-pcm.h" /* FIXME make configurable */ | ||
33 | |||
34 | static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" }; | ||
35 | |||
36 | #define API_FAST (1 << 2) /* Short timeout */ | ||
37 | #define API_SLOW (1 << 3) /* Additional 300ms timeout */ | ||
38 | |||
39 | struct cx18_api_info { | ||
40 | u32 cmd; | ||
41 | u8 flags; /* Flags, see above */ | ||
42 | u8 rpu; /* Processing unit */ | ||
43 | const char *name; /* The name of the command */ | ||
44 | }; | ||
45 | |||
46 | #define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x } | ||
47 | |||
48 | static const struct cx18_api_info api_info[] = { | ||
49 | /* MPEG encoder API */ | ||
50 | API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), | ||
51 | API_ENTRY(CPU, CX18_EPU_DEBUG, 0), | ||
52 | API_ENTRY(CPU, CX18_CREATE_TASK, 0), | ||
53 | API_ENTRY(CPU, CX18_DESTROY_TASK, 0), | ||
54 | API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW), | ||
55 | API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW), | ||
56 | API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0), | ||
57 | API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0), | ||
58 | API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0), | ||
59 | API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0), | ||
60 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0), | ||
61 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0), | ||
62 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0), | ||
63 | API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0), | ||
64 | API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0), | ||
65 | API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0), | ||
66 | API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0), | ||
67 | API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0), | ||
68 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0), | ||
69 | API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0), | ||
70 | API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0), | ||
71 | API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW), | ||
72 | API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0), | ||
73 | API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0), | ||
74 | API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0), | ||
75 | API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0), | ||
76 | API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0), | ||
77 | API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0), | ||
78 | API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0), | ||
79 | API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0), | ||
80 | API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0), | ||
81 | API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0), | ||
82 | API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0), | ||
83 | API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0), | ||
84 | API_ENTRY(CPU, CX18_CPU_SET_VFC_PARAM, 0), | ||
85 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0), | ||
86 | API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST), | ||
87 | API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW), | ||
88 | API_ENTRY(APU, CX18_APU_START, 0), | ||
89 | API_ENTRY(APU, CX18_APU_STOP, 0), | ||
90 | API_ENTRY(APU, CX18_APU_RESETAI, 0), | ||
91 | API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0), | ||
92 | API_ENTRY(0, 0, 0), | ||
93 | }; | ||
94 | |||
95 | static const struct cx18_api_info *find_api_info(u32 cmd) | ||
96 | { | ||
97 | int i; | ||
98 | |||
99 | for (i = 0; api_info[i].cmd; i++) | ||
100 | if (api_info[i].cmd == cmd) | ||
101 | return &api_info[i]; | ||
102 | return NULL; | ||
103 | } | ||
104 | |||
105 | /* Call with buf of n*11+1 bytes */ | ||
106 | static char *u32arr2hex(u32 data[], int n, char *buf) | ||
107 | { | ||
108 | char *p; | ||
109 | int i; | ||
110 | |||
111 | for (i = 0, p = buf; i < n; i++, p += 11) { | ||
112 | /* kernel snprintf() appends '\0' always */ | ||
113 | snprintf(p, 12, " %#010x", data[i]); | ||
114 | } | ||
115 | *p = '\0'; | ||
116 | return buf; | ||
117 | } | ||
118 | |||
119 | static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) | ||
120 | { | ||
121 | char argstr[MAX_MB_ARGUMENTS*11+1]; | ||
122 | |||
123 | if (!(cx18_debug & CX18_DBGFLG_API)) | ||
124 | return; | ||
125 | |||
126 | CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s" | ||
127 | "\n", name, mb->request, mb->ack, mb->cmd, mb->error, | ||
128 | u32arr2hex(mb->args, MAX_MB_ARGUMENTS, argstr)); | ||
129 | } | ||
130 | |||
131 | |||
132 | /* | ||
133 | * Functions that run in a work_queue work handling context | ||
134 | */ | ||
135 | |||
136 | static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl) | ||
137 | { | ||
138 | struct cx18_buffer *buf; | ||
139 | |||
140 | if (s->dvb == NULL || !s->dvb->enabled || mdl->bytesused == 0) | ||
141 | return; | ||
142 | |||
143 | /* We ignore mdl and buf readpos accounting here - it doesn't matter */ | ||
144 | |||
145 | /* The likely case */ | ||
146 | if (list_is_singular(&mdl->buf_list)) { | ||
147 | buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, | ||
148 | list); | ||
149 | if (buf->bytesused) | ||
150 | dvb_dmx_swfilter(&s->dvb->demux, | ||
151 | buf->buf, buf->bytesused); | ||
152 | return; | ||
153 | } | ||
154 | |||
155 | list_for_each_entry(buf, &mdl->buf_list, list) { | ||
156 | if (buf->bytesused == 0) | ||
157 | break; | ||
158 | dvb_dmx_swfilter(&s->dvb->demux, buf->buf, buf->bytesused); | ||
159 | } | ||
160 | } | ||
161 | |||
162 | static void cx18_mdl_send_to_videobuf(struct cx18_stream *s, | ||
163 | struct cx18_mdl *mdl) | ||
164 | { | ||
165 | struct cx18_videobuf_buffer *vb_buf; | ||
166 | struct cx18_buffer *buf; | ||
167 | u8 *p; | ||
168 | u32 offset = 0; | ||
169 | int dispatch = 0; | ||
170 | |||
171 | if (mdl->bytesused == 0) | ||
172 | return; | ||
173 | |||
174 | /* Acquire a videobuf buffer, clone to and and release it */ | ||
175 | spin_lock(&s->vb_lock); | ||
176 | if (list_empty(&s->vb_capture)) | ||
177 | goto out; | ||
178 | |||
179 | vb_buf = list_first_entry(&s->vb_capture, struct cx18_videobuf_buffer, | ||
180 | vb.queue); | ||
181 | |||
182 | p = videobuf_to_vmalloc(&vb_buf->vb); | ||
183 | if (!p) | ||
184 | goto out; | ||
185 | |||
186 | offset = vb_buf->bytes_used; | ||
187 | list_for_each_entry(buf, &mdl->buf_list, list) { | ||
188 | if (buf->bytesused == 0) | ||
189 | break; | ||
190 | |||
191 | if ((offset + buf->bytesused) <= vb_buf->vb.bsize) { | ||
192 | memcpy(p + offset, buf->buf, buf->bytesused); | ||
193 | offset += buf->bytesused; | ||
194 | vb_buf->bytes_used += buf->bytesused; | ||
195 | } | ||
196 | } | ||
197 | |||
198 | /* If we've filled the buffer as per the callers res then dispatch it */ | ||
199 | if (vb_buf->bytes_used >= (vb_buf->vb.width * vb_buf->vb.height * 2)) { | ||
200 | dispatch = 1; | ||
201 | vb_buf->bytes_used = 0; | ||
202 | } | ||
203 | |||
204 | if (dispatch) { | ||
205 | vb_buf->vb.ts = ktime_to_timeval(ktime_get()); | ||
206 | list_del(&vb_buf->vb.queue); | ||
207 | vb_buf->vb.state = VIDEOBUF_DONE; | ||
208 | wake_up(&vb_buf->vb.done); | ||
209 | } | ||
210 | |||
211 | mod_timer(&s->vb_timeout, msecs_to_jiffies(2000) + jiffies); | ||
212 | |||
213 | out: | ||
214 | spin_unlock(&s->vb_lock); | ||
215 | } | ||
216 | |||
217 | static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s, | ||
218 | struct cx18_mdl *mdl) | ||
219 | { | ||
220 | struct cx18_buffer *buf; | ||
221 | |||
222 | if (mdl->bytesused == 0) | ||
223 | return; | ||
224 | |||
225 | /* We ignore mdl and buf readpos accounting here - it doesn't matter */ | ||
226 | |||
227 | /* The likely case */ | ||
228 | if (list_is_singular(&mdl->buf_list)) { | ||
229 | buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, | ||
230 | list); | ||
231 | if (buf->bytesused) | ||
232 | cx->pcm_announce_callback(cx->alsa, buf->buf, | ||
233 | buf->bytesused); | ||
234 | return; | ||
235 | } | ||
236 | |||
237 | list_for_each_entry(buf, &mdl->buf_list, list) { | ||
238 | if (buf->bytesused == 0) | ||
239 | break; | ||
240 | cx->pcm_announce_callback(cx->alsa, buf->buf, buf->bytesused); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) | ||
245 | { | ||
246 | u32 handle, mdl_ack_count, id; | ||
247 | struct cx18_mailbox *mb; | ||
248 | struct cx18_mdl_ack *mdl_ack; | ||
249 | struct cx18_stream *s; | ||
250 | struct cx18_mdl *mdl; | ||
251 | int i; | ||
252 | |||
253 | mb = &order->mb; | ||
254 | handle = mb->args[0]; | ||
255 | s = cx18_handle_to_stream(cx, handle); | ||
256 | |||
257 | if (s == NULL) { | ||
258 | CX18_WARN("Got DMA done notification for unknown/inactive" | ||
259 | " handle %d, %s mailbox seq no %d\n", handle, | ||
260 | (order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) ? | ||
261 | "stale" : "good", mb->request); | ||
262 | return; | ||
263 | } | ||
264 | |||
265 | mdl_ack_count = mb->args[2]; | ||
266 | mdl_ack = order->mdl_ack; | ||
267 | for (i = 0; i < mdl_ack_count; i++, mdl_ack++) { | ||
268 | id = mdl_ack->id; | ||
269 | /* | ||
270 | * Simple integrity check for processing a stale (and possibly | ||
271 | * inconsistent mailbox): make sure the MDL id is in the | ||
272 | * valid range for the stream. | ||
273 | * | ||
274 | * We go through the trouble of dealing with stale mailboxes | ||
275 | * because most of the time, the mailbox data is still valid and | ||
276 | * unchanged (and in practice the firmware ping-pongs the | ||
277 | * two mdl_ack buffers so mdl_acks are not stale). | ||
278 | * | ||
279 | * There are occasions when we get a half changed mailbox, | ||
280 | * which this check catches for a handle & id mismatch. If the | ||
281 | * handle and id do correspond, the worst case is that we | ||
282 | * completely lost the old MDL, but pick up the new MDL | ||
283 | * early (but the new mdl_ack is guaranteed to be good in this | ||
284 | * case as the firmware wouldn't point us to a new mdl_ack until | ||
285 | * it's filled in). | ||
286 | * | ||
287 | * cx18_queue_get_mdl() will detect the lost MDLs | ||
288 | * and send them back to q_free for fw rotation eventually. | ||
289 | */ | ||
290 | if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && | ||
291 | !(id >= s->mdl_base_idx && | ||
292 | id < (s->mdl_base_idx + s->buffers))) { | ||
293 | CX18_WARN("Fell behind! Ignoring stale mailbox with " | ||
294 | " inconsistent data. Lost MDL for mailbox " | ||
295 | "seq no %d\n", mb->request); | ||
296 | break; | ||
297 | } | ||
298 | mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used); | ||
299 | |||
300 | CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id); | ||
301 | if (mdl == NULL) { | ||
302 | CX18_WARN("Could not find MDL %d for stream %s\n", | ||
303 | id, s->name); | ||
304 | continue; | ||
305 | } | ||
306 | |||
307 | CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", | ||
308 | s->name, mdl->bytesused); | ||
309 | |||
310 | if (s->type == CX18_ENC_STREAM_TYPE_TS) { | ||
311 | cx18_mdl_send_to_dvb(s, mdl); | ||
312 | cx18_enqueue(s, mdl, &s->q_free); | ||
313 | } else if (s->type == CX18_ENC_STREAM_TYPE_PCM) { | ||
314 | /* Pass the data to cx18-alsa */ | ||
315 | if (cx->pcm_announce_callback != NULL) { | ||
316 | cx18_mdl_send_to_alsa(cx, s, mdl); | ||
317 | cx18_enqueue(s, mdl, &s->q_free); | ||
318 | } else { | ||
319 | cx18_enqueue(s, mdl, &s->q_full); | ||
320 | } | ||
321 | } else if (s->type == CX18_ENC_STREAM_TYPE_YUV) { | ||
322 | cx18_mdl_send_to_videobuf(s, mdl); | ||
323 | cx18_enqueue(s, mdl, &s->q_free); | ||
324 | } else { | ||
325 | cx18_enqueue(s, mdl, &s->q_full); | ||
326 | if (s->type == CX18_ENC_STREAM_TYPE_IDX) | ||
327 | cx18_stream_rotate_idx_mdls(cx); | ||
328 | } | ||
329 | } | ||
330 | /* Put as many MDLs as possible back into fw use */ | ||
331 | cx18_stream_load_fw_queue(s); | ||
332 | |||
333 | wake_up(&cx->dma_waitq); | ||
334 | if (s->id != -1) | ||
335 | wake_up(&s->waitq); | ||
336 | } | ||
337 | |||
338 | static void epu_debug(struct cx18 *cx, struct cx18_in_work_order *order) | ||
339 | { | ||
340 | char *p; | ||
341 | char *str = order->str; | ||
342 | |||
343 | CX18_DEBUG_INFO("%x %s\n", order->mb.args[0], str); | ||
344 | p = strchr(str, '.'); | ||
345 | if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str) | ||
346 | CX18_INFO("FW version: %s\n", p - 1); | ||
347 | } | ||
348 | |||
349 | static void epu_cmd(struct cx18 *cx, struct cx18_in_work_order *order) | ||
350 | { | ||
351 | switch (order->rpu) { | ||
352 | case CPU: | ||
353 | { | ||
354 | switch (order->mb.cmd) { | ||
355 | case CX18_EPU_DMA_DONE: | ||
356 | epu_dma_done(cx, order); | ||
357 | break; | ||
358 | case CX18_EPU_DEBUG: | ||
359 | epu_debug(cx, order); | ||
360 | break; | ||
361 | default: | ||
362 | CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", | ||
363 | order->mb.cmd); | ||
364 | break; | ||
365 | } | ||
366 | break; | ||
367 | } | ||
368 | case APU: | ||
369 | CX18_WARN("Unknown APU to EPU mailbox command %#0x\n", | ||
370 | order->mb.cmd); | ||
371 | break; | ||
372 | default: | ||
373 | break; | ||
374 | } | ||
375 | } | ||
376 | |||
377 | static | ||
378 | void free_in_work_order(struct cx18 *cx, struct cx18_in_work_order *order) | ||
379 | { | ||
380 | atomic_set(&order->pending, 0); | ||
381 | } | ||
382 | |||
383 | void cx18_in_work_handler(struct work_struct *work) | ||
384 | { | ||
385 | struct cx18_in_work_order *order = | ||
386 | container_of(work, struct cx18_in_work_order, work); | ||
387 | struct cx18 *cx = order->cx; | ||
388 | epu_cmd(cx, order); | ||
389 | free_in_work_order(cx, order); | ||
390 | } | ||
391 | |||
392 | |||
393 | /* | ||
394 | * Functions that run in an interrupt handling context | ||
395 | */ | ||
396 | |||
397 | static void mb_ack_irq(struct cx18 *cx, struct cx18_in_work_order *order) | ||
398 | { | ||
399 | struct cx18_mailbox __iomem *ack_mb; | ||
400 | u32 ack_irq, req; | ||
401 | |||
402 | switch (order->rpu) { | ||
403 | case APU: | ||
404 | ack_irq = IRQ_EPU_TO_APU_ACK; | ||
405 | ack_mb = &cx->scb->apu2epu_mb; | ||
406 | break; | ||
407 | case CPU: | ||
408 | ack_irq = IRQ_EPU_TO_CPU_ACK; | ||
409 | ack_mb = &cx->scb->cpu2epu_mb; | ||
410 | break; | ||
411 | default: | ||
412 | CX18_WARN("Unhandled RPU (%d) for command %x ack\n", | ||
413 | order->rpu, order->mb.cmd); | ||
414 | return; | ||
415 | } | ||
416 | |||
417 | req = order->mb.request; | ||
418 | /* Don't ack if the RPU has gotten impatient and timed us out */ | ||
419 | if (req != cx18_readl(cx, &ack_mb->request) || | ||
420 | req == cx18_readl(cx, &ack_mb->ack)) { | ||
421 | CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our " | ||
422 | "incoming %s to EPU mailbox (sequence no. %u) " | ||
423 | "while processing\n", | ||
424 | rpu_str[order->rpu], rpu_str[order->rpu], req); | ||
425 | order->flags |= CX18_F_EWO_MB_STALE_WHILE_PROC; | ||
426 | return; | ||
427 | } | ||
428 | cx18_writel(cx, req, &ack_mb->ack); | ||
429 | cx18_write_reg_expect(cx, ack_irq, SW2_INT_SET, ack_irq, ack_irq); | ||
430 | return; | ||
431 | } | ||
432 | |||
433 | static int epu_dma_done_irq(struct cx18 *cx, struct cx18_in_work_order *order) | ||
434 | { | ||
435 | u32 handle, mdl_ack_offset, mdl_ack_count; | ||
436 | struct cx18_mailbox *mb; | ||
437 | |||
438 | mb = &order->mb; | ||
439 | handle = mb->args[0]; | ||
440 | mdl_ack_offset = mb->args[1]; | ||
441 | mdl_ack_count = mb->args[2]; | ||
442 | |||
443 | if (handle == CX18_INVALID_TASK_HANDLE || | ||
444 | mdl_ack_count == 0 || mdl_ack_count > CX18_MAX_MDL_ACKS) { | ||
445 | if ((order->flags & CX18_F_EWO_MB_STALE) == 0) | ||
446 | mb_ack_irq(cx, order); | ||
447 | return -1; | ||
448 | } | ||
449 | |||
450 | cx18_memcpy_fromio(cx, order->mdl_ack, cx->enc_mem + mdl_ack_offset, | ||
451 | sizeof(struct cx18_mdl_ack) * mdl_ack_count); | ||
452 | |||
453 | if ((order->flags & CX18_F_EWO_MB_STALE) == 0) | ||
454 | mb_ack_irq(cx, order); | ||
455 | return 1; | ||
456 | } | ||
457 | |||
458 | static | ||
459 | int epu_debug_irq(struct cx18 *cx, struct cx18_in_work_order *order) | ||
460 | { | ||
461 | u32 str_offset; | ||
462 | char *str = order->str; | ||
463 | |||
464 | str[0] = '\0'; | ||
465 | str_offset = order->mb.args[1]; | ||
466 | if (str_offset) { | ||
467 | cx18_setup_page(cx, str_offset); | ||
468 | cx18_memcpy_fromio(cx, str, cx->enc_mem + str_offset, 252); | ||
469 | str[252] = '\0'; | ||
470 | cx18_setup_page(cx, SCB_OFFSET); | ||
471 | } | ||
472 | |||
473 | if ((order->flags & CX18_F_EWO_MB_STALE) == 0) | ||
474 | mb_ack_irq(cx, order); | ||
475 | |||
476 | return str_offset ? 1 : 0; | ||
477 | } | ||
478 | |||
479 | static inline | ||
480 | int epu_cmd_irq(struct cx18 *cx, struct cx18_in_work_order *order) | ||
481 | { | ||
482 | int ret = -1; | ||
483 | |||
484 | switch (order->rpu) { | ||
485 | case CPU: | ||
486 | { | ||
487 | switch (order->mb.cmd) { | ||
488 | case CX18_EPU_DMA_DONE: | ||
489 | ret = epu_dma_done_irq(cx, order); | ||
490 | break; | ||
491 | case CX18_EPU_DEBUG: | ||
492 | ret = epu_debug_irq(cx, order); | ||
493 | break; | ||
494 | default: | ||
495 | CX18_WARN("Unknown CPU to EPU mailbox command %#0x\n", | ||
496 | order->mb.cmd); | ||
497 | break; | ||
498 | } | ||
499 | break; | ||
500 | } | ||
501 | case APU: | ||
502 | CX18_WARN("Unknown APU to EPU mailbox command %#0x\n", | ||
503 | order->mb.cmd); | ||
504 | break; | ||
505 | default: | ||
506 | break; | ||
507 | } | ||
508 | return ret; | ||
509 | } | ||
510 | |||
511 | static inline | ||
512 | struct cx18_in_work_order *alloc_in_work_order_irq(struct cx18 *cx) | ||
513 | { | ||
514 | int i; | ||
515 | struct cx18_in_work_order *order = NULL; | ||
516 | |||
517 | for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) { | ||
518 | /* | ||
519 | * We only need "pending" atomic to inspect its contents, | ||
520 | * and need not do a check and set because: | ||
521 | * 1. Any work handler thread only clears "pending" and only | ||
522 | * on one, particular work order at a time, per handler thread. | ||
523 | * 2. "pending" is only set here, and we're serialized because | ||
524 | * we're called in an IRQ handler context. | ||
525 | */ | ||
526 | if (atomic_read(&cx->in_work_order[i].pending) == 0) { | ||
527 | order = &cx->in_work_order[i]; | ||
528 | atomic_set(&order->pending, 1); | ||
529 | break; | ||
530 | } | ||
531 | } | ||
532 | return order; | ||
533 | } | ||
534 | |||
535 | void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) | ||
536 | { | ||
537 | struct cx18_mailbox __iomem *mb; | ||
538 | struct cx18_mailbox *order_mb; | ||
539 | struct cx18_in_work_order *order; | ||
540 | int submit; | ||
541 | |||
542 | switch (rpu) { | ||
543 | case CPU: | ||
544 | mb = &cx->scb->cpu2epu_mb; | ||
545 | break; | ||
546 | case APU: | ||
547 | mb = &cx->scb->apu2epu_mb; | ||
548 | break; | ||
549 | default: | ||
550 | return; | ||
551 | } | ||
552 | |||
553 | order = alloc_in_work_order_irq(cx); | ||
554 | if (order == NULL) { | ||
555 | CX18_WARN("Unable to find blank work order form to schedule " | ||
556 | "incoming mailbox command processing\n"); | ||
557 | return; | ||
558 | } | ||
559 | |||
560 | order->flags = 0; | ||
561 | order->rpu = rpu; | ||
562 | order_mb = &order->mb; | ||
563 | |||
564 | /* mb->cmd and mb->args[0] through mb->args[2] */ | ||
565 | cx18_memcpy_fromio(cx, &order_mb->cmd, &mb->cmd, 4 * sizeof(u32)); | ||
566 | /* mb->request and mb->ack. N.B. we want to read mb->ack last */ | ||
567 | cx18_memcpy_fromio(cx, &order_mb->request, &mb->request, | ||
568 | 2 * sizeof(u32)); | ||
569 | |||
570 | if (order_mb->request == order_mb->ack) { | ||
571 | CX18_DEBUG_WARN("Possibly falling behind: %s self-ack'ed our " | ||
572 | "incoming %s to EPU mailbox (sequence no. %u)" | ||
573 | "\n", | ||
574 | rpu_str[rpu], rpu_str[rpu], order_mb->request); | ||
575 | if (cx18_debug & CX18_DBGFLG_WARN) | ||
576 | dump_mb(cx, order_mb, "incoming"); | ||
577 | order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT; | ||
578 | } | ||
579 | |||
580 | /* | ||
581 | * Individual EPU command processing is responsible for ack-ing | ||
582 | * a non-stale mailbox as soon as possible | ||
583 | */ | ||
584 | submit = epu_cmd_irq(cx, order); | ||
585 | if (submit > 0) { | ||
586 | queue_work(cx->in_work_queue, &order->work); | ||
587 | } | ||
588 | } | ||
589 | |||
590 | |||
591 | /* | ||
592 | * Functions called from a non-interrupt, non work_queue context | ||
593 | */ | ||
594 | |||
595 | static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) | ||
596 | { | ||
597 | const struct cx18_api_info *info = find_api_info(cmd); | ||
598 | u32 state, irq, req, ack, err; | ||
599 | struct cx18_mailbox __iomem *mb; | ||
600 | u32 __iomem *xpu_state; | ||
601 | wait_queue_head_t *waitq; | ||
602 | struct mutex *mb_lock; | ||
603 | unsigned long int t0, timeout, ret; | ||
604 | int i; | ||
605 | char argstr[MAX_MB_ARGUMENTS*11+1]; | ||
606 | DEFINE_WAIT(w); | ||
607 | |||
608 | if (info == NULL) { | ||
609 | CX18_WARN("unknown cmd %x\n", cmd); | ||
610 | return -EINVAL; | ||
611 | } | ||
612 | |||
613 | if (cx18_debug & CX18_DBGFLG_API) { /* only call u32arr2hex if needed */ | ||
614 | if (cmd == CX18_CPU_DE_SET_MDL) { | ||
615 | if (cx18_debug & CX18_DBGFLG_HIGHVOL) | ||
616 | CX18_DEBUG_HI_API("%s\tcmd %#010x args%s\n", | ||
617 | info->name, cmd, | ||
618 | u32arr2hex(data, args, argstr)); | ||
619 | } else | ||
620 | CX18_DEBUG_API("%s\tcmd %#010x args%s\n", | ||
621 | info->name, cmd, | ||
622 | u32arr2hex(data, args, argstr)); | ||
623 | } | ||
624 | |||
625 | switch (info->rpu) { | ||
626 | case APU: | ||
627 | waitq = &cx->mb_apu_waitq; | ||
628 | mb_lock = &cx->epu2apu_mb_lock; | ||
629 | irq = IRQ_EPU_TO_APU; | ||
630 | mb = &cx->scb->epu2apu_mb; | ||
631 | xpu_state = &cx->scb->apu_state; | ||
632 | break; | ||
633 | case CPU: | ||
634 | waitq = &cx->mb_cpu_waitq; | ||
635 | mb_lock = &cx->epu2cpu_mb_lock; | ||
636 | irq = IRQ_EPU_TO_CPU; | ||
637 | mb = &cx->scb->epu2cpu_mb; | ||
638 | xpu_state = &cx->scb->cpu_state; | ||
639 | break; | ||
640 | default: | ||
641 | CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); | ||
642 | return -EINVAL; | ||
643 | } | ||
644 | |||
645 | mutex_lock(mb_lock); | ||
646 | /* | ||
647 | * Wait for an in-use mailbox to complete | ||
648 | * | ||
649 | * If the XPU is responding with Ack's, the mailbox shouldn't be in | ||
650 | * a busy state, since we serialize access to it on our end. | ||
651 | * | ||
652 | * If the wait for ack after sending a previous command was interrupted | ||
653 | * by a signal, we may get here and find a busy mailbox. After waiting, | ||
654 | * mark it "not busy" from our end, if the XPU hasn't ack'ed it still. | ||
655 | */ | ||
656 | state = cx18_readl(cx, xpu_state); | ||
657 | req = cx18_readl(cx, &mb->request); | ||
658 | timeout = msecs_to_jiffies(10); | ||
659 | ret = wait_event_timeout(*waitq, | ||
660 | (ack = cx18_readl(cx, &mb->ack)) == req, | ||
661 | timeout); | ||
662 | if (req != ack) { | ||
663 | /* waited long enough, make the mbox "not busy" from our end */ | ||
664 | cx18_writel(cx, req, &mb->ack); | ||
665 | CX18_ERR("mbox was found stuck busy when setting up for %s; " | ||
666 | "clearing busy and trying to proceed\n", info->name); | ||
667 | } else if (ret != timeout) | ||
668 | CX18_DEBUG_API("waited %u msecs for busy mbox to be acked\n", | ||
669 | jiffies_to_msecs(timeout-ret)); | ||
670 | |||
671 | /* Build the outgoing mailbox */ | ||
672 | req = ((req & 0xfffffffe) == 0xfffffffe) ? 1 : req + 1; | ||
673 | |||
674 | cx18_writel(cx, cmd, &mb->cmd); | ||
675 | for (i = 0; i < args; i++) | ||
676 | cx18_writel(cx, data[i], &mb->args[i]); | ||
677 | cx18_writel(cx, 0, &mb->error); | ||
678 | cx18_writel(cx, req, &mb->request); | ||
679 | cx18_writel(cx, req - 1, &mb->ack); /* ensure ack & req are distinct */ | ||
680 | |||
681 | /* | ||
682 | * Notify the XPU and wait for it to send an Ack back | ||
683 | */ | ||
684 | timeout = msecs_to_jiffies((info->flags & API_FAST) ? 10 : 20); | ||
685 | |||
686 | CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n", | ||
687 | irq, info->name); | ||
688 | |||
689 | /* So we don't miss the wakeup, prepare to wait before notifying fw */ | ||
690 | prepare_to_wait(waitq, &w, TASK_UNINTERRUPTIBLE); | ||
691 | cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); | ||
692 | |||
693 | t0 = jiffies; | ||
694 | ack = cx18_readl(cx, &mb->ack); | ||
695 | if (ack != req) { | ||
696 | schedule_timeout(timeout); | ||
697 | ret = jiffies - t0; | ||
698 | ack = cx18_readl(cx, &mb->ack); | ||
699 | } else { | ||
700 | ret = jiffies - t0; | ||
701 | } | ||
702 | |||
703 | finish_wait(waitq, &w); | ||
704 | |||
705 | if (req != ack) { | ||
706 | mutex_unlock(mb_lock); | ||
707 | if (ret >= timeout) { | ||
708 | /* Timed out */ | ||
709 | CX18_DEBUG_WARN("sending %s timed out waiting %d msecs " | ||
710 | "for RPU acknowledgement\n", | ||
711 | info->name, jiffies_to_msecs(ret)); | ||
712 | } else { | ||
713 | CX18_DEBUG_WARN("woken up before mailbox ack was ready " | ||
714 | "after submitting %s to RPU. only " | ||
715 | "waited %d msecs on req %u but awakened" | ||
716 | " with unmatched ack %u\n", | ||
717 | info->name, | ||
718 | jiffies_to_msecs(ret), | ||
719 | req, ack); | ||
720 | } | ||
721 | return -EINVAL; | ||
722 | } | ||
723 | |||
724 | if (ret >= timeout) | ||
725 | CX18_DEBUG_WARN("failed to be awakened upon RPU acknowledgment " | ||
726 | "sending %s; timed out waiting %d msecs\n", | ||
727 | info->name, jiffies_to_msecs(ret)); | ||
728 | else | ||
729 | CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", | ||
730 | jiffies_to_msecs(ret), info->name); | ||
731 | |||
732 | /* Collect data returned by the XPU */ | ||
733 | for (i = 0; i < MAX_MB_ARGUMENTS; i++) | ||
734 | data[i] = cx18_readl(cx, &mb->args[i]); | ||
735 | err = cx18_readl(cx, &mb->error); | ||
736 | mutex_unlock(mb_lock); | ||
737 | |||
738 | /* | ||
739 | * Wait for XPU to perform extra actions for the caller in some cases. | ||
740 | * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs | ||
741 | * back in a burst shortly thereafter | ||
742 | */ | ||
743 | if (info->flags & API_SLOW) | ||
744 | cx18_msleep_timeout(300, 0); | ||
745 | |||
746 | if (err) | ||
747 | CX18_DEBUG_API("mailbox error %08x for command %s\n", err, | ||
748 | info->name); | ||
749 | return err ? -EIO : 0; | ||
750 | } | ||
751 | |||
752 | int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]) | ||
753 | { | ||
754 | return cx18_api_call(cx, cmd, args, data); | ||
755 | } | ||
756 | |||
757 | static int cx18_set_filter_param(struct cx18_stream *s) | ||
758 | { | ||
759 | struct cx18 *cx = s->cx; | ||
760 | u32 mode; | ||
761 | int ret; | ||
762 | |||
763 | mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0); | ||
764 | ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, | ||
765 | s->handle, 1, mode, cx->spatial_strength); | ||
766 | mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0); | ||
767 | ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, | ||
768 | s->handle, 0, mode, cx->temporal_strength); | ||
769 | ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4, | ||
770 | s->handle, 2, cx->filter_mode >> 2, 0); | ||
771 | return ret; | ||
772 | } | ||
773 | |||
774 | int cx18_api_func(void *priv, u32 cmd, int in, int out, | ||
775 | u32 data[CX2341X_MBOX_MAX_DATA]) | ||
776 | { | ||
777 | struct cx18_stream *s = priv; | ||
778 | struct cx18 *cx = s->cx; | ||
779 | |||
780 | switch (cmd) { | ||
781 | case CX2341X_ENC_SET_OUTPUT_PORT: | ||
782 | return 0; | ||
783 | case CX2341X_ENC_SET_FRAME_RATE: | ||
784 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6, | ||
785 | s->handle, 0, 0, 0, 0, data[0]); | ||
786 | case CX2341X_ENC_SET_FRAME_SIZE: | ||
787 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3, | ||
788 | s->handle, data[1], data[0]); | ||
789 | case CX2341X_ENC_SET_STREAM_TYPE: | ||
790 | return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2, | ||
791 | s->handle, data[0]); | ||
792 | case CX2341X_ENC_SET_ASPECT_RATIO: | ||
793 | return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2, | ||
794 | s->handle, data[0]); | ||
795 | |||
796 | case CX2341X_ENC_SET_GOP_PROPERTIES: | ||
797 | return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3, | ||
798 | s->handle, data[0], data[1]); | ||
799 | case CX2341X_ENC_SET_GOP_CLOSURE: | ||
800 | return 0; | ||
801 | case CX2341X_ENC_SET_AUDIO_PROPERTIES: | ||
802 | return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2, | ||
803 | s->handle, data[0]); | ||
804 | case CX2341X_ENC_MUTE_AUDIO: | ||
805 | return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2, | ||
806 | s->handle, data[0]); | ||
807 | case CX2341X_ENC_SET_BIT_RATE: | ||
808 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5, | ||
809 | s->handle, data[0], data[1], data[2], data[3]); | ||
810 | case CX2341X_ENC_MUTE_VIDEO: | ||
811 | return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, | ||
812 | s->handle, data[0]); | ||
813 | case CX2341X_ENC_SET_FRAME_DROP_RATE: | ||
814 | return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2, | ||
815 | s->handle, data[0]); | ||
816 | case CX2341X_ENC_MISC: | ||
817 | return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4, | ||
818 | s->handle, data[0], data[1], data[2]); | ||
819 | case CX2341X_ENC_SET_DNR_FILTER_MODE: | ||
820 | cx->filter_mode = (data[0] & 3) | (data[1] << 2); | ||
821 | return cx18_set_filter_param(s); | ||
822 | case CX2341X_ENC_SET_DNR_FILTER_PROPS: | ||
823 | cx->spatial_strength = data[0]; | ||
824 | cx->temporal_strength = data[1]; | ||
825 | return cx18_set_filter_param(s); | ||
826 | case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE: | ||
827 | return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3, | ||
828 | s->handle, data[0], data[1]); | ||
829 | case CX2341X_ENC_SET_CORING_LEVELS: | ||
830 | return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5, | ||
831 | s->handle, data[0], data[1], data[2], data[3]); | ||
832 | } | ||
833 | CX18_WARN("Unknown cmd %x\n", cmd); | ||
834 | return 0; | ||
835 | } | ||
836 | |||
837 | int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], | ||
838 | u32 cmd, int args, ...) | ||
839 | { | ||
840 | va_list ap; | ||
841 | int i; | ||
842 | |||
843 | va_start(ap, args); | ||
844 | for (i = 0; i < args; i++) | ||
845 | data[i] = va_arg(ap, u32); | ||
846 | va_end(ap); | ||
847 | return cx18_api(cx, cmd, args, data); | ||
848 | } | ||
849 | |||
850 | int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...) | ||
851 | { | ||
852 | u32 data[MAX_MB_ARGUMENTS]; | ||
853 | va_list ap; | ||
854 | int i; | ||
855 | |||
856 | if (cx == NULL) { | ||
857 | CX18_ERR("cx == NULL (cmd=%x)\n", cmd); | ||
858 | return 0; | ||
859 | } | ||
860 | if (args > MAX_MB_ARGUMENTS) { | ||
861 | CX18_ERR("args too big (cmd=%x)\n", cmd); | ||
862 | args = MAX_MB_ARGUMENTS; | ||
863 | } | ||
864 | va_start(ap, args); | ||
865 | for (i = 0; i < args; i++) | ||
866 | data[i] = va_arg(ap, u32); | ||
867 | va_end(ap); | ||
868 | return cx18_api(cx, cmd, args, data); | ||
869 | } | ||
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h new file mode 100644 index 00000000000..05fe6bdbe06 --- /dev/null +++ b/drivers/media/video/cx18/cx18-mailbox.h | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * cx18 mailbox functions | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #ifndef _CX18_MAILBOX_H_ | ||
24 | #define _CX18_MAILBOX_H_ | ||
25 | |||
26 | /* mailbox max args */ | ||
27 | #define MAX_MB_ARGUMENTS 6 | ||
28 | /* compatibility, should be same as the define in cx2341x.h */ | ||
29 | #define CX2341X_MBOX_MAX_DATA 16 | ||
30 | |||
31 | #define MB_RESERVED_HANDLE_0 0 | ||
32 | #define MB_RESERVED_HANDLE_1 0xFFFFFFFF | ||
33 | |||
34 | #define APU 0 | ||
35 | #define CPU 1 | ||
36 | #define EPU 2 | ||
37 | #define HPU 3 | ||
38 | |||
39 | struct cx18; | ||
40 | |||
41 | /* | ||
42 | * This structure is used by CPU to provide completed MDL & buffers information. | ||
43 | * Its structure is dictated by the layout of the SCB, required by the | ||
44 | * firmware, but its definition needs to be here, instead of in cx18-scb.h, | ||
45 | * for mailbox work order scheduling | ||
46 | */ | ||
47 | struct cx18_mdl_ack { | ||
48 | u32 id; /* ID of a completed MDL */ | ||
49 | u32 data_used; /* Total data filled in the MDL with 'id' */ | ||
50 | }; | ||
51 | |||
52 | /* The cx18_mailbox struct is the mailbox structure which is used for passing | ||
53 | messages between processors */ | ||
54 | struct cx18_mailbox { | ||
55 | /* The sender sets a handle in 'request' after he fills the command. The | ||
56 | 'request' should be different than 'ack'. The sender, also, generates | ||
57 | an interrupt on XPU2YPU_irq where XPU is the sender and YPU is the | ||
58 | receiver. */ | ||
59 | u32 request; | ||
60 | /* The receiver detects a new command when 'req' is different than 'ack'. | ||
61 | He sets 'ack' to the same value as 'req' to clear the command. He, also, | ||
62 | generates an interrupt on YPU2XPU_irq where XPU is the sender and YPU | ||
63 | is the receiver. */ | ||
64 | u32 ack; | ||
65 | u32 reserved[6]; | ||
66 | /* 'cmd' identifies the command. The list of these commands are in | ||
67 | cx23418.h */ | ||
68 | u32 cmd; | ||
69 | /* Each command can have up to 6 arguments */ | ||
70 | u32 args[MAX_MB_ARGUMENTS]; | ||
71 | /* The return code can be one of the codes in the file cx23418.h. If the | ||
72 | command is completed successfuly, the error will be ERR_SYS_SUCCESS. | ||
73 | If it is pending, the code is ERR_SYS_PENDING. If it failed, the error | ||
74 | code would indicate the task from which the error originated and will | ||
75 | be one of the errors in cx23418.h. In that case, the following | ||
76 | applies ((error & 0xff) != 0). | ||
77 | If the command is pending, the return will be passed in a MB from the | ||
78 | receiver to the sender. 'req' will be returned in args[0] */ | ||
79 | u32 error; | ||
80 | }; | ||
81 | |||
82 | struct cx18_stream; | ||
83 | |||
84 | int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]); | ||
85 | int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd, | ||
86 | int args, ...); | ||
87 | int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...); | ||
88 | int cx18_api_func(void *priv, u32 cmd, int in, int out, | ||
89 | u32 data[CX2341X_MBOX_MAX_DATA]); | ||
90 | |||
91 | void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu); | ||
92 | |||
93 | void cx18_in_work_handler(struct work_struct *work); | ||
94 | |||
95 | #endif | ||
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c new file mode 100644 index 00000000000..8884537bd62 --- /dev/null +++ b/drivers/media/video/cx18/cx18-queue.c | |||
@@ -0,0 +1,443 @@ | |||
1 | /* | ||
2 | * cx18 buffer queues | ||
3 | * | ||
4 | * Derived from ivtv-queue.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | #include "cx18-queue.h" | ||
27 | #include "cx18-streams.h" | ||
28 | #include "cx18-scb.h" | ||
29 | #include "cx18-io.h" | ||
30 | |||
31 | void cx18_buf_swap(struct cx18_buffer *buf) | ||
32 | { | ||
33 | int i; | ||
34 | |||
35 | for (i = 0; i < buf->bytesused; i += 4) | ||
36 | swab32s((u32 *)(buf->buf + i)); | ||
37 | } | ||
38 | |||
39 | void _cx18_mdl_swap(struct cx18_mdl *mdl) | ||
40 | { | ||
41 | struct cx18_buffer *buf; | ||
42 | |||
43 | list_for_each_entry(buf, &mdl->buf_list, list) { | ||
44 | if (buf->bytesused == 0) | ||
45 | break; | ||
46 | cx18_buf_swap(buf); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | void cx18_queue_init(struct cx18_queue *q) | ||
51 | { | ||
52 | INIT_LIST_HEAD(&q->list); | ||
53 | atomic_set(&q->depth, 0); | ||
54 | q->bytesused = 0; | ||
55 | } | ||
56 | |||
57 | struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, | ||
58 | struct cx18_queue *q, int to_front) | ||
59 | { | ||
60 | /* clear the mdl if it is not to be enqueued to the full queue */ | ||
61 | if (q != &s->q_full) { | ||
62 | mdl->bytesused = 0; | ||
63 | mdl->readpos = 0; | ||
64 | mdl->m_flags = 0; | ||
65 | mdl->skipped = 0; | ||
66 | mdl->curr_buf = NULL; | ||
67 | } | ||
68 | |||
69 | /* q_busy is restricted to a max buffer count imposed by firmware */ | ||
70 | if (q == &s->q_busy && | ||
71 | atomic_read(&q->depth) >= CX18_MAX_FW_MDLS_PER_STREAM) | ||
72 | q = &s->q_free; | ||
73 | |||
74 | spin_lock(&q->lock); | ||
75 | |||
76 | if (to_front) | ||
77 | list_add(&mdl->list, &q->list); /* LIFO */ | ||
78 | else | ||
79 | list_add_tail(&mdl->list, &q->list); /* FIFO */ | ||
80 | q->bytesused += mdl->bytesused - mdl->readpos; | ||
81 | atomic_inc(&q->depth); | ||
82 | |||
83 | spin_unlock(&q->lock); | ||
84 | return q; | ||
85 | } | ||
86 | |||
87 | struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) | ||
88 | { | ||
89 | struct cx18_mdl *mdl = NULL; | ||
90 | |||
91 | spin_lock(&q->lock); | ||
92 | if (!list_empty(&q->list)) { | ||
93 | mdl = list_first_entry(&q->list, struct cx18_mdl, list); | ||
94 | list_del_init(&mdl->list); | ||
95 | q->bytesused -= mdl->bytesused - mdl->readpos; | ||
96 | mdl->skipped = 0; | ||
97 | atomic_dec(&q->depth); | ||
98 | } | ||
99 | spin_unlock(&q->lock); | ||
100 | return mdl; | ||
101 | } | ||
102 | |||
103 | static void _cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, | ||
104 | struct cx18_mdl *mdl) | ||
105 | { | ||
106 | struct cx18_buffer *buf; | ||
107 | u32 buf_size = s->buf_size; | ||
108 | u32 bytesused = mdl->bytesused; | ||
109 | |||
110 | list_for_each_entry(buf, &mdl->buf_list, list) { | ||
111 | buf->readpos = 0; | ||
112 | if (bytesused >= buf_size) { | ||
113 | buf->bytesused = buf_size; | ||
114 | bytesused -= buf_size; | ||
115 | } else { | ||
116 | buf->bytesused = bytesused; | ||
117 | bytesused = 0; | ||
118 | } | ||
119 | cx18_buf_sync_for_cpu(s, buf); | ||
120 | } | ||
121 | } | ||
122 | |||
123 | static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, | ||
124 | struct cx18_mdl *mdl) | ||
125 | { | ||
126 | struct cx18_buffer *buf; | ||
127 | |||
128 | if (list_is_singular(&mdl->buf_list)) { | ||
129 | buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, | ||
130 | list); | ||
131 | buf->bytesused = mdl->bytesused; | ||
132 | buf->readpos = 0; | ||
133 | cx18_buf_sync_for_cpu(s, buf); | ||
134 | } else { | ||
135 | _cx18_mdl_update_bufs_for_cpu(s, mdl); | ||
136 | } | ||
137 | } | ||
138 | |||
139 | struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id, | ||
140 | u32 bytesused) | ||
141 | { | ||
142 | struct cx18 *cx = s->cx; | ||
143 | struct cx18_mdl *mdl; | ||
144 | struct cx18_mdl *tmp; | ||
145 | struct cx18_mdl *ret = NULL; | ||
146 | LIST_HEAD(sweep_up); | ||
147 | |||
148 | /* | ||
149 | * We don't have to acquire multiple q locks here, because we are | ||
150 | * serialized by the single threaded work handler. | ||
151 | * MDLs from the firmware will thus remain in order as | ||
152 | * they are moved from q_busy to q_full or to the dvb ring buffer. | ||
153 | */ | ||
154 | spin_lock(&s->q_busy.lock); | ||
155 | list_for_each_entry_safe(mdl, tmp, &s->q_busy.list, list) { | ||
156 | /* | ||
157 | * We should find what the firmware told us is done, | ||
158 | * right at the front of the queue. If we don't, we likely have | ||
159 | * missed an mdl done message from the firmware. | ||
160 | * Once we skip an mdl repeatedly, relative to the size of | ||
161 | * q_busy, we have high confidence we've missed it. | ||
162 | */ | ||
163 | if (mdl->id != id) { | ||
164 | mdl->skipped++; | ||
165 | if (mdl->skipped >= atomic_read(&s->q_busy.depth)-1) { | ||
166 | /* mdl must have fallen out of rotation */ | ||
167 | CX18_WARN("Skipped %s, MDL %d, %d " | ||
168 | "times - it must have dropped out of " | ||
169 | "rotation\n", s->name, mdl->id, | ||
170 | mdl->skipped); | ||
171 | /* Sweep it up to put it back into rotation */ | ||
172 | list_move_tail(&mdl->list, &sweep_up); | ||
173 | atomic_dec(&s->q_busy.depth); | ||
174 | } | ||
175 | continue; | ||
176 | } | ||
177 | /* | ||
178 | * We pull the desired mdl off of the queue here. Something | ||
179 | * will have to put it back on a queue later. | ||
180 | */ | ||
181 | list_del_init(&mdl->list); | ||
182 | atomic_dec(&s->q_busy.depth); | ||
183 | ret = mdl; | ||
184 | break; | ||
185 | } | ||
186 | spin_unlock(&s->q_busy.lock); | ||
187 | |||
188 | /* | ||
189 | * We found the mdl for which we were looking. Get it ready for | ||
190 | * the caller to put on q_full or in the dvb ring buffer. | ||
191 | */ | ||
192 | if (ret != NULL) { | ||
193 | ret->bytesused = bytesused; | ||
194 | ret->skipped = 0; | ||
195 | /* 0'ed readpos, m_flags & curr_buf when mdl went on q_busy */ | ||
196 | cx18_mdl_update_bufs_for_cpu(s, ret); | ||
197 | if (s->type != CX18_ENC_STREAM_TYPE_TS) | ||
198 | set_bit(CX18_F_M_NEED_SWAP, &ret->m_flags); | ||
199 | } | ||
200 | |||
201 | /* Put any mdls the firmware is ignoring back into normal rotation */ | ||
202 | list_for_each_entry_safe(mdl, tmp, &sweep_up, list) { | ||
203 | list_del_init(&mdl->list); | ||
204 | cx18_enqueue(s, mdl, &s->q_free); | ||
205 | } | ||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | /* Move all mdls of a queue, while flushing the mdl */ | ||
210 | static void cx18_queue_flush(struct cx18_stream *s, | ||
211 | struct cx18_queue *q_src, struct cx18_queue *q_dst) | ||
212 | { | ||
213 | struct cx18_mdl *mdl; | ||
214 | |||
215 | /* It only makes sense to flush to q_free or q_idle */ | ||
216 | if (q_src == q_dst || q_dst == &s->q_full || q_dst == &s->q_busy) | ||
217 | return; | ||
218 | |||
219 | spin_lock(&q_src->lock); | ||
220 | spin_lock(&q_dst->lock); | ||
221 | while (!list_empty(&q_src->list)) { | ||
222 | mdl = list_first_entry(&q_src->list, struct cx18_mdl, list); | ||
223 | list_move_tail(&mdl->list, &q_dst->list); | ||
224 | mdl->bytesused = 0; | ||
225 | mdl->readpos = 0; | ||
226 | mdl->m_flags = 0; | ||
227 | mdl->skipped = 0; | ||
228 | mdl->curr_buf = NULL; | ||
229 | atomic_inc(&q_dst->depth); | ||
230 | } | ||
231 | cx18_queue_init(q_src); | ||
232 | spin_unlock(&q_src->lock); | ||
233 | spin_unlock(&q_dst->lock); | ||
234 | } | ||
235 | |||
236 | void cx18_flush_queues(struct cx18_stream *s) | ||
237 | { | ||
238 | cx18_queue_flush(s, &s->q_busy, &s->q_free); | ||
239 | cx18_queue_flush(s, &s->q_full, &s->q_free); | ||
240 | } | ||
241 | |||
242 | /* | ||
243 | * Note, s->buf_pool is not protected by a lock, | ||
244 | * the stream better not have *anything* going on when calling this | ||
245 | */ | ||
246 | void cx18_unload_queues(struct cx18_stream *s) | ||
247 | { | ||
248 | struct cx18_queue *q_idle = &s->q_idle; | ||
249 | struct cx18_mdl *mdl; | ||
250 | struct cx18_buffer *buf; | ||
251 | |||
252 | /* Move all MDLS to q_idle */ | ||
253 | cx18_queue_flush(s, &s->q_busy, q_idle); | ||
254 | cx18_queue_flush(s, &s->q_full, q_idle); | ||
255 | cx18_queue_flush(s, &s->q_free, q_idle); | ||
256 | |||
257 | /* Reset MDL id's and move all buffers back to the stream's buf_pool */ | ||
258 | spin_lock(&q_idle->lock); | ||
259 | list_for_each_entry(mdl, &q_idle->list, list) { | ||
260 | while (!list_empty(&mdl->buf_list)) { | ||
261 | buf = list_first_entry(&mdl->buf_list, | ||
262 | struct cx18_buffer, list); | ||
263 | list_move_tail(&buf->list, &s->buf_pool); | ||
264 | buf->bytesused = 0; | ||
265 | buf->readpos = 0; | ||
266 | } | ||
267 | mdl->id = s->mdl_base_idx; /* reset id to a "safe" value */ | ||
268 | /* all other mdl fields were cleared by cx18_queue_flush() */ | ||
269 | } | ||
270 | spin_unlock(&q_idle->lock); | ||
271 | } | ||
272 | |||
273 | /* | ||
274 | * Note, s->buf_pool is not protected by a lock, | ||
275 | * the stream better not have *anything* going on when calling this | ||
276 | */ | ||
277 | void cx18_load_queues(struct cx18_stream *s) | ||
278 | { | ||
279 | struct cx18 *cx = s->cx; | ||
280 | struct cx18_mdl *mdl; | ||
281 | struct cx18_buffer *buf; | ||
282 | int mdl_id; | ||
283 | int i; | ||
284 | u32 partial_buf_size; | ||
285 | |||
286 | /* | ||
287 | * Attach buffers to MDLs, give the MDLs ids, and add MDLs to q_free | ||
288 | * Excess MDLs are left on q_idle | ||
289 | * Excess buffers are left in buf_pool and/or on an MDL in q_idle | ||
290 | */ | ||
291 | mdl_id = s->mdl_base_idx; | ||
292 | for (mdl = cx18_dequeue(s, &s->q_idle), i = s->bufs_per_mdl; | ||
293 | mdl != NULL && i == s->bufs_per_mdl; | ||
294 | mdl = cx18_dequeue(s, &s->q_idle)) { | ||
295 | |||
296 | mdl->id = mdl_id; | ||
297 | |||
298 | for (i = 0; i < s->bufs_per_mdl; i++) { | ||
299 | if (list_empty(&s->buf_pool)) | ||
300 | break; | ||
301 | |||
302 | buf = list_first_entry(&s->buf_pool, struct cx18_buffer, | ||
303 | list); | ||
304 | list_move_tail(&buf->list, &mdl->buf_list); | ||
305 | |||
306 | /* update the firmware's MDL array with this buffer */ | ||
307 | cx18_writel(cx, buf->dma_handle, | ||
308 | &cx->scb->cpu_mdl[mdl_id + i].paddr); | ||
309 | cx18_writel(cx, s->buf_size, | ||
310 | &cx->scb->cpu_mdl[mdl_id + i].length); | ||
311 | } | ||
312 | |||
313 | if (i == s->bufs_per_mdl) { | ||
314 | /* | ||
315 | * The encoder doesn't honor s->mdl_size. So in the | ||
316 | * case of a non-integral number of buffers to meet | ||
317 | * mdl_size, we lie about the size of the last buffer | ||
318 | * in the MDL to get the encoder to really only send | ||
319 | * us mdl_size bytes per MDL transfer. | ||
320 | */ | ||
321 | partial_buf_size = s->mdl_size % s->buf_size; | ||
322 | if (partial_buf_size) { | ||
323 | cx18_writel(cx, partial_buf_size, | ||
324 | &cx->scb->cpu_mdl[mdl_id + i - 1].length); | ||
325 | } | ||
326 | cx18_enqueue(s, mdl, &s->q_free); | ||
327 | } else { | ||
328 | /* Not enough buffers for this MDL; we won't use it */ | ||
329 | cx18_push(s, mdl, &s->q_idle); | ||
330 | } | ||
331 | mdl_id += i; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl) | ||
336 | { | ||
337 | int dma = s->dma; | ||
338 | u32 buf_size = s->buf_size; | ||
339 | struct pci_dev *pci_dev = s->cx->pci_dev; | ||
340 | struct cx18_buffer *buf; | ||
341 | |||
342 | list_for_each_entry(buf, &mdl->buf_list, list) | ||
343 | pci_dma_sync_single_for_device(pci_dev, buf->dma_handle, | ||
344 | buf_size, dma); | ||
345 | } | ||
346 | |||
347 | int cx18_stream_alloc(struct cx18_stream *s) | ||
348 | { | ||
349 | struct cx18 *cx = s->cx; | ||
350 | int i; | ||
351 | |||
352 | if (s->buffers == 0) | ||
353 | return 0; | ||
354 | |||
355 | CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers " | ||
356 | "(%d.%02d kB total)\n", | ||
357 | s->name, s->buffers, s->buf_size, | ||
358 | s->buffers * s->buf_size / 1024, | ||
359 | (s->buffers * s->buf_size * 100 / 1024) % 100); | ||
360 | |||
361 | if (((char __iomem *)&cx->scb->cpu_mdl[cx->free_mdl_idx + s->buffers] - | ||
362 | (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) { | ||
363 | unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE - | ||
364 | ((char __iomem *)cx->scb->cpu_mdl)); | ||
365 | |||
366 | CX18_ERR("Too many buffers, cannot fit in SCB area\n"); | ||
367 | CX18_ERR("Max buffers = %zd\n", | ||
368 | bufsz / sizeof(struct cx18_mdl_ent)); | ||
369 | return -ENOMEM; | ||
370 | } | ||
371 | |||
372 | s->mdl_base_idx = cx->free_mdl_idx; | ||
373 | |||
374 | /* allocate stream buffers and MDLs */ | ||
375 | for (i = 0; i < s->buffers; i++) { | ||
376 | struct cx18_mdl *mdl; | ||
377 | struct cx18_buffer *buf; | ||
378 | |||
379 | /* 1 MDL per buffer to handle the worst & also default case */ | ||
380 | mdl = kzalloc(sizeof(struct cx18_mdl), GFP_KERNEL|__GFP_NOWARN); | ||
381 | if (mdl == NULL) | ||
382 | break; | ||
383 | |||
384 | buf = kzalloc(sizeof(struct cx18_buffer), | ||
385 | GFP_KERNEL|__GFP_NOWARN); | ||
386 | if (buf == NULL) { | ||
387 | kfree(mdl); | ||
388 | break; | ||
389 | } | ||
390 | |||
391 | buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN); | ||
392 | if (buf->buf == NULL) { | ||
393 | kfree(mdl); | ||
394 | kfree(buf); | ||
395 | break; | ||
396 | } | ||
397 | |||
398 | INIT_LIST_HEAD(&mdl->list); | ||
399 | INIT_LIST_HEAD(&mdl->buf_list); | ||
400 | mdl->id = s->mdl_base_idx; /* a somewhat safe value */ | ||
401 | cx18_enqueue(s, mdl, &s->q_idle); | ||
402 | |||
403 | INIT_LIST_HEAD(&buf->list); | ||
404 | buf->dma_handle = pci_map_single(s->cx->pci_dev, | ||
405 | buf->buf, s->buf_size, s->dma); | ||
406 | cx18_buf_sync_for_cpu(s, buf); | ||
407 | list_add_tail(&buf->list, &s->buf_pool); | ||
408 | } | ||
409 | if (i == s->buffers) { | ||
410 | cx->free_mdl_idx += s->buffers; | ||
411 | return 0; | ||
412 | } | ||
413 | CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name); | ||
414 | cx18_stream_free(s); | ||
415 | return -ENOMEM; | ||
416 | } | ||
417 | |||
418 | void cx18_stream_free(struct cx18_stream *s) | ||
419 | { | ||
420 | struct cx18_mdl *mdl; | ||
421 | struct cx18_buffer *buf; | ||
422 | struct cx18 *cx = s->cx; | ||
423 | |||
424 | CX18_DEBUG_INFO("Deallocating buffers for %s stream\n", s->name); | ||
425 | |||
426 | /* move all buffers to buf_pool and all MDLs to q_idle */ | ||
427 | cx18_unload_queues(s); | ||
428 | |||
429 | /* empty q_idle */ | ||
430 | while ((mdl = cx18_dequeue(s, &s->q_idle))) | ||
431 | kfree(mdl); | ||
432 | |||
433 | /* empty buf_pool */ | ||
434 | while (!list_empty(&s->buf_pool)) { | ||
435 | buf = list_first_entry(&s->buf_pool, struct cx18_buffer, list); | ||
436 | list_del_init(&buf->list); | ||
437 | |||
438 | pci_unmap_single(s->cx->pci_dev, buf->dma_handle, | ||
439 | s->buf_size, s->dma); | ||
440 | kfree(buf->buf); | ||
441 | kfree(buf); | ||
442 | } | ||
443 | } | ||
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h new file mode 100644 index 00000000000..4201ddc1609 --- /dev/null +++ b/drivers/media/video/cx18/cx18-queue.h | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * cx18 buffer queues | ||
3 | * | ||
4 | * Derived from ivtv-queue.h | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #define CX18_DMA_UNMAPPED ((u32) -1) | ||
26 | |||
27 | /* cx18_buffer utility functions */ | ||
28 | |||
29 | static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s, | ||
30 | struct cx18_buffer *buf) | ||
31 | { | ||
32 | pci_dma_sync_single_for_cpu(s->cx->pci_dev, buf->dma_handle, | ||
33 | s->buf_size, s->dma); | ||
34 | } | ||
35 | |||
36 | static inline void cx18_buf_sync_for_device(struct cx18_stream *s, | ||
37 | struct cx18_buffer *buf) | ||
38 | { | ||
39 | pci_dma_sync_single_for_device(s->cx->pci_dev, buf->dma_handle, | ||
40 | s->buf_size, s->dma); | ||
41 | } | ||
42 | |||
43 | void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl); | ||
44 | |||
45 | static inline void cx18_mdl_sync_for_device(struct cx18_stream *s, | ||
46 | struct cx18_mdl *mdl) | ||
47 | { | ||
48 | if (list_is_singular(&mdl->buf_list)) | ||
49 | cx18_buf_sync_for_device(s, list_first_entry(&mdl->buf_list, | ||
50 | struct cx18_buffer, | ||
51 | list)); | ||
52 | else | ||
53 | _cx18_mdl_sync_for_device(s, mdl); | ||
54 | } | ||
55 | |||
56 | void cx18_buf_swap(struct cx18_buffer *buf); | ||
57 | void _cx18_mdl_swap(struct cx18_mdl *mdl); | ||
58 | |||
59 | static inline void cx18_mdl_swap(struct cx18_mdl *mdl) | ||
60 | { | ||
61 | if (list_is_singular(&mdl->buf_list)) | ||
62 | cx18_buf_swap(list_first_entry(&mdl->buf_list, | ||
63 | struct cx18_buffer, list)); | ||
64 | else | ||
65 | _cx18_mdl_swap(mdl); | ||
66 | } | ||
67 | |||
68 | /* cx18_queue utility functions */ | ||
69 | struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, | ||
70 | struct cx18_queue *q, int to_front); | ||
71 | |||
72 | static inline | ||
73 | struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, | ||
74 | struct cx18_queue *q) | ||
75 | { | ||
76 | return _cx18_enqueue(s, mdl, q, 0); /* FIFO */ | ||
77 | } | ||
78 | |||
79 | static inline | ||
80 | struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_mdl *mdl, | ||
81 | struct cx18_queue *q) | ||
82 | { | ||
83 | return _cx18_enqueue(s, mdl, q, 1); /* LIFO */ | ||
84 | } | ||
85 | |||
86 | void cx18_queue_init(struct cx18_queue *q); | ||
87 | struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); | ||
88 | struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id, | ||
89 | u32 bytesused); | ||
90 | void cx18_flush_queues(struct cx18_stream *s); | ||
91 | |||
92 | /* queue MDL reconfiguration helpers */ | ||
93 | void cx18_unload_queues(struct cx18_stream *s); | ||
94 | void cx18_load_queues(struct cx18_stream *s); | ||
95 | |||
96 | /* cx18_stream utility functions */ | ||
97 | int cx18_stream_alloc(struct cx18_stream *s); | ||
98 | void cx18_stream_free(struct cx18_stream *s); | ||
diff --git a/drivers/media/video/cx18/cx18-scb.c b/drivers/media/video/cx18/cx18-scb.c new file mode 100644 index 00000000000..85cc59637e5 --- /dev/null +++ b/drivers/media/video/cx18/cx18-scb.c | |||
@@ -0,0 +1,122 @@ | |||
1 | /* | ||
2 | * cx18 System Control Block initialization | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #include "cx18-driver.h" | ||
24 | #include "cx18-io.h" | ||
25 | #include "cx18-scb.h" | ||
26 | |||
27 | void cx18_init_scb(struct cx18 *cx) | ||
28 | { | ||
29 | cx18_setup_page(cx, SCB_OFFSET); | ||
30 | cx18_memset_io(cx, cx->scb, 0, 0x10000); | ||
31 | |||
32 | cx18_writel(cx, IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq); | ||
33 | cx18_writel(cx, IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack); | ||
34 | cx18_writel(cx, IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq); | ||
35 | cx18_writel(cx, IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack); | ||
36 | cx18_writel(cx, IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq); | ||
37 | cx18_writel(cx, IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack); | ||
38 | cx18_writel(cx, IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq); | ||
39 | cx18_writel(cx, IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack); | ||
40 | |||
41 | cx18_writel(cx, IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq); | ||
42 | cx18_writel(cx, IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack); | ||
43 | cx18_writel(cx, IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq); | ||
44 | cx18_writel(cx, IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack); | ||
45 | cx18_writel(cx, IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq); | ||
46 | cx18_writel(cx, IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack); | ||
47 | cx18_writel(cx, IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq); | ||
48 | cx18_writel(cx, IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack); | ||
49 | |||
50 | cx18_writel(cx, IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq); | ||
51 | cx18_writel(cx, IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack); | ||
52 | cx18_writel(cx, IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq); | ||
53 | cx18_writel(cx, IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack); | ||
54 | cx18_writel(cx, IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq); | ||
55 | cx18_writel(cx, IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack); | ||
56 | cx18_writel(cx, IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq); | ||
57 | cx18_writel(cx, IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack); | ||
58 | |||
59 | cx18_writel(cx, IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq); | ||
60 | cx18_writel(cx, IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack); | ||
61 | cx18_writel(cx, IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq); | ||
62 | cx18_writel(cx, IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack); | ||
63 | cx18_writel(cx, IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq); | ||
64 | cx18_writel(cx, IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack); | ||
65 | cx18_writel(cx, IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq); | ||
66 | cx18_writel(cx, IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack); | ||
67 | |||
68 | cx18_writel(cx, IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq); | ||
69 | cx18_writel(cx, IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack); | ||
70 | cx18_writel(cx, IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq); | ||
71 | cx18_writel(cx, IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack); | ||
72 | cx18_writel(cx, IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq); | ||
73 | cx18_writel(cx, IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack); | ||
74 | cx18_writel(cx, IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq); | ||
75 | cx18_writel(cx, IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack); | ||
76 | |||
77 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb), | ||
78 | &cx->scb->apu2cpu_mb_offset); | ||
79 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb), | ||
80 | &cx->scb->hpu2cpu_mb_offset); | ||
81 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb), | ||
82 | &cx->scb->ppu2cpu_mb_offset); | ||
83 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb), | ||
84 | &cx->scb->epu2cpu_mb_offset); | ||
85 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb), | ||
86 | &cx->scb->cpu2apu_mb_offset); | ||
87 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb), | ||
88 | &cx->scb->hpu2apu_mb_offset); | ||
89 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb), | ||
90 | &cx->scb->ppu2apu_mb_offset); | ||
91 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb), | ||
92 | &cx->scb->epu2apu_mb_offset); | ||
93 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb), | ||
94 | &cx->scb->cpu2hpu_mb_offset); | ||
95 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb), | ||
96 | &cx->scb->apu2hpu_mb_offset); | ||
97 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb), | ||
98 | &cx->scb->ppu2hpu_mb_offset); | ||
99 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb), | ||
100 | &cx->scb->epu2hpu_mb_offset); | ||
101 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb), | ||
102 | &cx->scb->cpu2ppu_mb_offset); | ||
103 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb), | ||
104 | &cx->scb->apu2ppu_mb_offset); | ||
105 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb), | ||
106 | &cx->scb->hpu2ppu_mb_offset); | ||
107 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb), | ||
108 | &cx->scb->epu2ppu_mb_offset); | ||
109 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb), | ||
110 | &cx->scb->cpu2epu_mb_offset); | ||
111 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb), | ||
112 | &cx->scb->apu2epu_mb_offset); | ||
113 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb), | ||
114 | &cx->scb->hpu2epu_mb_offset); | ||
115 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb), | ||
116 | &cx->scb->ppu2epu_mb_offset); | ||
117 | |||
118 | cx18_writel(cx, SCB_OFFSET + offsetof(struct cx18_scb, cpu_state), | ||
119 | &cx->scb->ipc_offset); | ||
120 | |||
121 | cx18_writel(cx, 1, &cx->scb->epu_state); | ||
122 | } | ||
diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h new file mode 100644 index 00000000000..08877652e32 --- /dev/null +++ b/drivers/media/video/cx18/cx18-scb.h | |||
@@ -0,0 +1,280 @@ | |||
1 | /* | ||
2 | * cx18 System Control Block initialization | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
20 | * 02111-1307 USA | ||
21 | */ | ||
22 | |||
23 | #ifndef CX18_SCB_H | ||
24 | #define CX18_SCB_H | ||
25 | |||
26 | #include "cx18-mailbox.h" | ||
27 | |||
28 | /* NOTE: All ACK interrupts are in the SW2 register. All non-ACK interrupts | ||
29 | are in the SW1 register. */ | ||
30 | |||
31 | #define IRQ_APU_TO_CPU 0x00000001 | ||
32 | #define IRQ_CPU_TO_APU_ACK 0x00000001 | ||
33 | #define IRQ_HPU_TO_CPU 0x00000002 | ||
34 | #define IRQ_CPU_TO_HPU_ACK 0x00000002 | ||
35 | #define IRQ_PPU_TO_CPU 0x00000004 | ||
36 | #define IRQ_CPU_TO_PPU_ACK 0x00000004 | ||
37 | #define IRQ_EPU_TO_CPU 0x00000008 | ||
38 | #define IRQ_CPU_TO_EPU_ACK 0x00000008 | ||
39 | |||
40 | #define IRQ_CPU_TO_APU 0x00000010 | ||
41 | #define IRQ_APU_TO_CPU_ACK 0x00000010 | ||
42 | #define IRQ_HPU_TO_APU 0x00000020 | ||
43 | #define IRQ_APU_TO_HPU_ACK 0x00000020 | ||
44 | #define IRQ_PPU_TO_APU 0x00000040 | ||
45 | #define IRQ_APU_TO_PPU_ACK 0x00000040 | ||
46 | #define IRQ_EPU_TO_APU 0x00000080 | ||
47 | #define IRQ_APU_TO_EPU_ACK 0x00000080 | ||
48 | |||
49 | #define IRQ_CPU_TO_HPU 0x00000100 | ||
50 | #define IRQ_HPU_TO_CPU_ACK 0x00000100 | ||
51 | #define IRQ_APU_TO_HPU 0x00000200 | ||
52 | #define IRQ_HPU_TO_APU_ACK 0x00000200 | ||
53 | #define IRQ_PPU_TO_HPU 0x00000400 | ||
54 | #define IRQ_HPU_TO_PPU_ACK 0x00000400 | ||
55 | #define IRQ_EPU_TO_HPU 0x00000800 | ||
56 | #define IRQ_HPU_TO_EPU_ACK 0x00000800 | ||
57 | |||
58 | #define IRQ_CPU_TO_PPU 0x00001000 | ||
59 | #define IRQ_PPU_TO_CPU_ACK 0x00001000 | ||
60 | #define IRQ_APU_TO_PPU 0x00002000 | ||
61 | #define IRQ_PPU_TO_APU_ACK 0x00002000 | ||
62 | #define IRQ_HPU_TO_PPU 0x00004000 | ||
63 | #define IRQ_PPU_TO_HPU_ACK 0x00004000 | ||
64 | #define IRQ_EPU_TO_PPU 0x00008000 | ||
65 | #define IRQ_PPU_TO_EPU_ACK 0x00008000 | ||
66 | |||
67 | #define IRQ_CPU_TO_EPU 0x00010000 | ||
68 | #define IRQ_EPU_TO_CPU_ACK 0x00010000 | ||
69 | #define IRQ_APU_TO_EPU 0x00020000 | ||
70 | #define IRQ_EPU_TO_APU_ACK 0x00020000 | ||
71 | #define IRQ_HPU_TO_EPU 0x00040000 | ||
72 | #define IRQ_EPU_TO_HPU_ACK 0x00040000 | ||
73 | #define IRQ_PPU_TO_EPU 0x00080000 | ||
74 | #define IRQ_EPU_TO_PPU_ACK 0x00080000 | ||
75 | |||
76 | #define SCB_OFFSET 0xDC0000 | ||
77 | |||
78 | /* If Firmware uses fixed memory map, it shall not allocate the area | ||
79 | between SCB_OFFSET and SCB_OFFSET+SCB_RESERVED_SIZE-1 inclusive */ | ||
80 | #define SCB_RESERVED_SIZE 0x10000 | ||
81 | |||
82 | |||
83 | /* This structure is used by EPU to provide memory descriptors in its memory */ | ||
84 | struct cx18_mdl_ent { | ||
85 | u32 paddr; /* Physical address of a buffer segment */ | ||
86 | u32 length; /* Length of the buffer segment */ | ||
87 | }; | ||
88 | |||
89 | struct cx18_scb { | ||
90 | /* These fields form the System Control Block which is used at boot time | ||
91 | for localizing the IPC data as well as the code positions for all | ||
92 | processors. The offsets are from the start of this struct. */ | ||
93 | |||
94 | /* Offset where to find the Inter-Processor Communication data */ | ||
95 | u32 ipc_offset; | ||
96 | u32 reserved01[7]; | ||
97 | /* Offset where to find the start of the CPU code */ | ||
98 | u32 cpu_code_offset; | ||
99 | u32 reserved02[3]; | ||
100 | /* Offset where to find the start of the APU code */ | ||
101 | u32 apu_code_offset; | ||
102 | u32 reserved03[3]; | ||
103 | /* Offset where to find the start of the HPU code */ | ||
104 | u32 hpu_code_offset; | ||
105 | u32 reserved04[3]; | ||
106 | /* Offset where to find the start of the PPU code */ | ||
107 | u32 ppu_code_offset; | ||
108 | u32 reserved05[3]; | ||
109 | |||
110 | /* These fields form Inter-Processor Communication data which is used | ||
111 | by all processors to locate the information needed for communicating | ||
112 | with other processors */ | ||
113 | |||
114 | /* Fields for CPU: */ | ||
115 | |||
116 | /* bit 0: 1/0 processor ready/not ready. Set other bits to 0. */ | ||
117 | u32 cpu_state; | ||
118 | u32 reserved1[7]; | ||
119 | /* Offset to the mailbox used for sending commands from APU to CPU */ | ||
120 | u32 apu2cpu_mb_offset; | ||
121 | /* Value to write to register SW1 register set (0xC7003100) after the | ||
122 | command is ready */ | ||
123 | u32 apu2cpu_irq; | ||
124 | /* Value to write to register SW2 register set (0xC7003140) after the | ||
125 | command is cleared */ | ||
126 | u32 cpu2apu_irq_ack; | ||
127 | u32 reserved2[13]; | ||
128 | |||
129 | u32 hpu2cpu_mb_offset; | ||
130 | u32 hpu2cpu_irq; | ||
131 | u32 cpu2hpu_irq_ack; | ||
132 | u32 reserved3[13]; | ||
133 | |||
134 | u32 ppu2cpu_mb_offset; | ||
135 | u32 ppu2cpu_irq; | ||
136 | u32 cpu2ppu_irq_ack; | ||
137 | u32 reserved4[13]; | ||
138 | |||
139 | u32 epu2cpu_mb_offset; | ||
140 | u32 epu2cpu_irq; | ||
141 | u32 cpu2epu_irq_ack; | ||
142 | u32 reserved5[13]; | ||
143 | u32 reserved6[8]; | ||
144 | |||
145 | /* Fields for APU: */ | ||
146 | |||
147 | u32 apu_state; | ||
148 | u32 reserved11[7]; | ||
149 | u32 cpu2apu_mb_offset; | ||
150 | u32 cpu2apu_irq; | ||
151 | u32 apu2cpu_irq_ack; | ||
152 | u32 reserved12[13]; | ||
153 | |||
154 | u32 hpu2apu_mb_offset; | ||
155 | u32 hpu2apu_irq; | ||
156 | u32 apu2hpu_irq_ack; | ||
157 | u32 reserved13[13]; | ||
158 | |||
159 | u32 ppu2apu_mb_offset; | ||
160 | u32 ppu2apu_irq; | ||
161 | u32 apu2ppu_irq_ack; | ||
162 | u32 reserved14[13]; | ||
163 | |||
164 | u32 epu2apu_mb_offset; | ||
165 | u32 epu2apu_irq; | ||
166 | u32 apu2epu_irq_ack; | ||
167 | u32 reserved15[13]; | ||
168 | u32 reserved16[8]; | ||
169 | |||
170 | /* Fields for HPU: */ | ||
171 | |||
172 | u32 hpu_state; | ||
173 | u32 reserved21[7]; | ||
174 | u32 cpu2hpu_mb_offset; | ||
175 | u32 cpu2hpu_irq; | ||
176 | u32 hpu2cpu_irq_ack; | ||
177 | u32 reserved22[13]; | ||
178 | |||
179 | u32 apu2hpu_mb_offset; | ||
180 | u32 apu2hpu_irq; | ||
181 | u32 hpu2apu_irq_ack; | ||
182 | u32 reserved23[13]; | ||
183 | |||
184 | u32 ppu2hpu_mb_offset; | ||
185 | u32 ppu2hpu_irq; | ||
186 | u32 hpu2ppu_irq_ack; | ||
187 | u32 reserved24[13]; | ||
188 | |||
189 | u32 epu2hpu_mb_offset; | ||
190 | u32 epu2hpu_irq; | ||
191 | u32 hpu2epu_irq_ack; | ||
192 | u32 reserved25[13]; | ||
193 | u32 reserved26[8]; | ||
194 | |||
195 | /* Fields for PPU: */ | ||
196 | |||
197 | u32 ppu_state; | ||
198 | u32 reserved31[7]; | ||
199 | u32 cpu2ppu_mb_offset; | ||
200 | u32 cpu2ppu_irq; | ||
201 | u32 ppu2cpu_irq_ack; | ||
202 | u32 reserved32[13]; | ||
203 | |||
204 | u32 apu2ppu_mb_offset; | ||
205 | u32 apu2ppu_irq; | ||
206 | u32 ppu2apu_irq_ack; | ||
207 | u32 reserved33[13]; | ||
208 | |||
209 | u32 hpu2ppu_mb_offset; | ||
210 | u32 hpu2ppu_irq; | ||
211 | u32 ppu2hpu_irq_ack; | ||
212 | u32 reserved34[13]; | ||
213 | |||
214 | u32 epu2ppu_mb_offset; | ||
215 | u32 epu2ppu_irq; | ||
216 | u32 ppu2epu_irq_ack; | ||
217 | u32 reserved35[13]; | ||
218 | u32 reserved36[8]; | ||
219 | |||
220 | /* Fields for EPU: */ | ||
221 | |||
222 | u32 epu_state; | ||
223 | u32 reserved41[7]; | ||
224 | u32 cpu2epu_mb_offset; | ||
225 | u32 cpu2epu_irq; | ||
226 | u32 epu2cpu_irq_ack; | ||
227 | u32 reserved42[13]; | ||
228 | |||
229 | u32 apu2epu_mb_offset; | ||
230 | u32 apu2epu_irq; | ||
231 | u32 epu2apu_irq_ack; | ||
232 | u32 reserved43[13]; | ||
233 | |||
234 | u32 hpu2epu_mb_offset; | ||
235 | u32 hpu2epu_irq; | ||
236 | u32 epu2hpu_irq_ack; | ||
237 | u32 reserved44[13]; | ||
238 | |||
239 | u32 ppu2epu_mb_offset; | ||
240 | u32 ppu2epu_irq; | ||
241 | u32 epu2ppu_irq_ack; | ||
242 | u32 reserved45[13]; | ||
243 | u32 reserved46[8]; | ||
244 | |||
245 | u32 semaphores[8]; /* Semaphores */ | ||
246 | |||
247 | u32 reserved50[32]; /* Reserved for future use */ | ||
248 | |||
249 | struct cx18_mailbox apu2cpu_mb; | ||
250 | struct cx18_mailbox hpu2cpu_mb; | ||
251 | struct cx18_mailbox ppu2cpu_mb; | ||
252 | struct cx18_mailbox epu2cpu_mb; | ||
253 | |||
254 | struct cx18_mailbox cpu2apu_mb; | ||
255 | struct cx18_mailbox hpu2apu_mb; | ||
256 | struct cx18_mailbox ppu2apu_mb; | ||
257 | struct cx18_mailbox epu2apu_mb; | ||
258 | |||
259 | struct cx18_mailbox cpu2hpu_mb; | ||
260 | struct cx18_mailbox apu2hpu_mb; | ||
261 | struct cx18_mailbox ppu2hpu_mb; | ||
262 | struct cx18_mailbox epu2hpu_mb; | ||
263 | |||
264 | struct cx18_mailbox cpu2ppu_mb; | ||
265 | struct cx18_mailbox apu2ppu_mb; | ||
266 | struct cx18_mailbox hpu2ppu_mb; | ||
267 | struct cx18_mailbox epu2ppu_mb; | ||
268 | |||
269 | struct cx18_mailbox cpu2epu_mb; | ||
270 | struct cx18_mailbox apu2epu_mb; | ||
271 | struct cx18_mailbox hpu2epu_mb; | ||
272 | struct cx18_mailbox ppu2epu_mb; | ||
273 | |||
274 | struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS]; | ||
275 | struct cx18_mdl_ent cpu_mdl[1]; | ||
276 | }; | ||
277 | |||
278 | void cx18_init_scb(struct cx18 *cx); | ||
279 | |||
280 | #endif | ||
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c new file mode 100644 index 00000000000..852f420fd27 --- /dev/null +++ b/drivers/media/video/cx18/cx18-streams.c | |||
@@ -0,0 +1,1050 @@ | |||
1 | /* | ||
2 | * cx18 init/start/stop/exit stream functions | ||
3 | * | ||
4 | * Derived from ivtv-streams.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | #include "cx18-io.h" | ||
27 | #include "cx18-fileops.h" | ||
28 | #include "cx18-mailbox.h" | ||
29 | #include "cx18-i2c.h" | ||
30 | #include "cx18-queue.h" | ||
31 | #include "cx18-ioctl.h" | ||
32 | #include "cx18-streams.h" | ||
33 | #include "cx18-cards.h" | ||
34 | #include "cx18-scb.h" | ||
35 | #include "cx18-dvb.h" | ||
36 | |||
37 | #define CX18_DSP0_INTERRUPT_MASK 0xd0004C | ||
38 | |||
39 | static struct v4l2_file_operations cx18_v4l2_enc_fops = { | ||
40 | .owner = THIS_MODULE, | ||
41 | .read = cx18_v4l2_read, | ||
42 | .open = cx18_v4l2_open, | ||
43 | /* FIXME change to video_ioctl2 if serialization lock can be removed */ | ||
44 | .unlocked_ioctl = cx18_v4l2_ioctl, | ||
45 | .release = cx18_v4l2_close, | ||
46 | .poll = cx18_v4l2_enc_poll, | ||
47 | .mmap = cx18_v4l2_mmap, | ||
48 | }; | ||
49 | |||
50 | /* offset from 0 to register ts v4l2 minors on */ | ||
51 | #define CX18_V4L2_ENC_TS_OFFSET 16 | ||
52 | /* offset from 0 to register pcm v4l2 minors on */ | ||
53 | #define CX18_V4L2_ENC_PCM_OFFSET 24 | ||
54 | /* offset from 0 to register yuv v4l2 minors on */ | ||
55 | #define CX18_V4L2_ENC_YUV_OFFSET 32 | ||
56 | |||
57 | static struct { | ||
58 | const char *name; | ||
59 | int vfl_type; | ||
60 | int num_offset; | ||
61 | int dma; | ||
62 | enum v4l2_buf_type buf_type; | ||
63 | } cx18_stream_info[] = { | ||
64 | { /* CX18_ENC_STREAM_TYPE_MPG */ | ||
65 | "encoder MPEG", | ||
66 | VFL_TYPE_GRABBER, 0, | ||
67 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
68 | }, | ||
69 | { /* CX18_ENC_STREAM_TYPE_TS */ | ||
70 | "TS", | ||
71 | VFL_TYPE_GRABBER, -1, | ||
72 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
73 | }, | ||
74 | { /* CX18_ENC_STREAM_TYPE_YUV */ | ||
75 | "encoder YUV", | ||
76 | VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET, | ||
77 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
78 | }, | ||
79 | { /* CX18_ENC_STREAM_TYPE_VBI */ | ||
80 | "encoder VBI", | ||
81 | VFL_TYPE_VBI, 0, | ||
82 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE, | ||
83 | }, | ||
84 | { /* CX18_ENC_STREAM_TYPE_PCM */ | ||
85 | "encoder PCM audio", | ||
86 | VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET, | ||
87 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE, | ||
88 | }, | ||
89 | { /* CX18_ENC_STREAM_TYPE_IDX */ | ||
90 | "encoder IDX", | ||
91 | VFL_TYPE_GRABBER, -1, | ||
92 | PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
93 | }, | ||
94 | { /* CX18_ENC_STREAM_TYPE_RAD */ | ||
95 | "encoder radio", | ||
96 | VFL_TYPE_RADIO, 0, | ||
97 | PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE, | ||
98 | }, | ||
99 | }; | ||
100 | |||
101 | |||
102 | void cx18_dma_free(struct videobuf_queue *q, | ||
103 | struct cx18_stream *s, struct cx18_videobuf_buffer *buf) | ||
104 | { | ||
105 | videobuf_waiton(q, &buf->vb, 0, 0); | ||
106 | videobuf_vmalloc_free(&buf->vb); | ||
107 | buf->vb.state = VIDEOBUF_NEEDS_INIT; | ||
108 | } | ||
109 | |||
110 | static int cx18_prepare_buffer(struct videobuf_queue *q, | ||
111 | struct cx18_stream *s, | ||
112 | struct cx18_videobuf_buffer *buf, | ||
113 | u32 pixelformat, | ||
114 | unsigned int width, unsigned int height, | ||
115 | enum v4l2_field field) | ||
116 | { | ||
117 | struct cx18 *cx = s->cx; | ||
118 | int rc = 0; | ||
119 | |||
120 | /* check settings */ | ||
121 | buf->bytes_used = 0; | ||
122 | |||
123 | if ((width < 48) || (height < 32)) | ||
124 | return -EINVAL; | ||
125 | |||
126 | buf->vb.size = (width * height * 2); | ||
127 | if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size)) | ||
128 | return -EINVAL; | ||
129 | |||
130 | /* alloc + fill struct (if changed) */ | ||
131 | if (buf->vb.width != width || buf->vb.height != height || | ||
132 | buf->vb.field != field || s->pixelformat != pixelformat || | ||
133 | buf->tvnorm != cx->std) { | ||
134 | |||
135 | buf->vb.width = width; | ||
136 | buf->vb.height = height; | ||
137 | buf->vb.field = field; | ||
138 | buf->tvnorm = cx->std; | ||
139 | s->pixelformat = pixelformat; | ||
140 | |||
141 | cx18_dma_free(q, s, buf); | ||
142 | } | ||
143 | |||
144 | if ((buf->vb.baddr != 0) && (buf->vb.bsize < buf->vb.size)) | ||
145 | return -EINVAL; | ||
146 | |||
147 | if (buf->vb.field == 0) | ||
148 | buf->vb.field = V4L2_FIELD_INTERLACED; | ||
149 | |||
150 | if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { | ||
151 | buf->vb.width = width; | ||
152 | buf->vb.height = height; | ||
153 | buf->vb.field = field; | ||
154 | buf->tvnorm = cx->std; | ||
155 | s->pixelformat = pixelformat; | ||
156 | |||
157 | rc = videobuf_iolock(q, &buf->vb, NULL); | ||
158 | if (rc != 0) | ||
159 | goto fail; | ||
160 | } | ||
161 | buf->vb.state = VIDEOBUF_PREPARED; | ||
162 | return 0; | ||
163 | |||
164 | fail: | ||
165 | cx18_dma_free(q, s, buf); | ||
166 | return rc; | ||
167 | |||
168 | } | ||
169 | |||
170 | /* VB_MIN_BUFSIZE is lcm(1440 * 480, 1440 * 576) | ||
171 | 1440 is a single line of 4:2:2 YUV at 720 luma samples wide | ||
172 | */ | ||
173 | #define VB_MIN_BUFFERS 32 | ||
174 | #define VB_MIN_BUFSIZE 4147200 | ||
175 | |||
176 | static int buffer_setup(struct videobuf_queue *q, | ||
177 | unsigned int *count, unsigned int *size) | ||
178 | { | ||
179 | struct cx18_stream *s = q->priv_data; | ||
180 | struct cx18 *cx = s->cx; | ||
181 | |||
182 | *size = 2 * cx->cxhdl.width * cx->cxhdl.height; | ||
183 | if (*count == 0) | ||
184 | *count = VB_MIN_BUFFERS; | ||
185 | |||
186 | while (*size * *count > VB_MIN_BUFFERS * VB_MIN_BUFSIZE) | ||
187 | (*count)--; | ||
188 | |||
189 | q->field = V4L2_FIELD_INTERLACED; | ||
190 | q->last = V4L2_FIELD_INTERLACED; | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int buffer_prepare(struct videobuf_queue *q, | ||
196 | struct videobuf_buffer *vb, | ||
197 | enum v4l2_field field) | ||
198 | { | ||
199 | struct cx18_videobuf_buffer *buf = | ||
200 | container_of(vb, struct cx18_videobuf_buffer, vb); | ||
201 | struct cx18_stream *s = q->priv_data; | ||
202 | struct cx18 *cx = s->cx; | ||
203 | |||
204 | return cx18_prepare_buffer(q, s, buf, s->pixelformat, | ||
205 | cx->cxhdl.width, cx->cxhdl.height, field); | ||
206 | } | ||
207 | |||
208 | static void buffer_release(struct videobuf_queue *q, | ||
209 | struct videobuf_buffer *vb) | ||
210 | { | ||
211 | struct cx18_videobuf_buffer *buf = | ||
212 | container_of(vb, struct cx18_videobuf_buffer, vb); | ||
213 | struct cx18_stream *s = q->priv_data; | ||
214 | |||
215 | cx18_dma_free(q, s, buf); | ||
216 | } | ||
217 | |||
218 | static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) | ||
219 | { | ||
220 | struct cx18_videobuf_buffer *buf = | ||
221 | container_of(vb, struct cx18_videobuf_buffer, vb); | ||
222 | struct cx18_stream *s = q->priv_data; | ||
223 | |||
224 | buf->vb.state = VIDEOBUF_QUEUED; | ||
225 | |||
226 | list_add_tail(&buf->vb.queue, &s->vb_capture); | ||
227 | } | ||
228 | |||
229 | static struct videobuf_queue_ops cx18_videobuf_qops = { | ||
230 | .buf_setup = buffer_setup, | ||
231 | .buf_prepare = buffer_prepare, | ||
232 | .buf_queue = buffer_queue, | ||
233 | .buf_release = buffer_release, | ||
234 | }; | ||
235 | |||
236 | static void cx18_stream_init(struct cx18 *cx, int type) | ||
237 | { | ||
238 | struct cx18_stream *s = &cx->streams[type]; | ||
239 | struct video_device *video_dev = s->video_dev; | ||
240 | |||
241 | /* we need to keep video_dev, so restore it afterwards */ | ||
242 | memset(s, 0, sizeof(*s)); | ||
243 | s->video_dev = video_dev; | ||
244 | |||
245 | /* initialize cx18_stream fields */ | ||
246 | s->dvb = NULL; | ||
247 | s->cx = cx; | ||
248 | s->type = type; | ||
249 | s->name = cx18_stream_info[type].name; | ||
250 | s->handle = CX18_INVALID_TASK_HANDLE; | ||
251 | |||
252 | s->dma = cx18_stream_info[type].dma; | ||
253 | s->buffers = cx->stream_buffers[type]; | ||
254 | s->buf_size = cx->stream_buf_size[type]; | ||
255 | INIT_LIST_HEAD(&s->buf_pool); | ||
256 | s->bufs_per_mdl = 1; | ||
257 | s->mdl_size = s->buf_size * s->bufs_per_mdl; | ||
258 | |||
259 | init_waitqueue_head(&s->waitq); | ||
260 | s->id = -1; | ||
261 | spin_lock_init(&s->q_free.lock); | ||
262 | cx18_queue_init(&s->q_free); | ||
263 | spin_lock_init(&s->q_busy.lock); | ||
264 | cx18_queue_init(&s->q_busy); | ||
265 | spin_lock_init(&s->q_full.lock); | ||
266 | cx18_queue_init(&s->q_full); | ||
267 | spin_lock_init(&s->q_idle.lock); | ||
268 | cx18_queue_init(&s->q_idle); | ||
269 | |||
270 | INIT_WORK(&s->out_work_order, cx18_out_work_handler); | ||
271 | |||
272 | INIT_LIST_HEAD(&s->vb_capture); | ||
273 | s->vb_timeout.function = cx18_vb_timeout; | ||
274 | s->vb_timeout.data = (unsigned long)s; | ||
275 | init_timer(&s->vb_timeout); | ||
276 | spin_lock_init(&s->vb_lock); | ||
277 | if (type == CX18_ENC_STREAM_TYPE_YUV) { | ||
278 | spin_lock_init(&s->vbuf_q_lock); | ||
279 | |||
280 | s->vb_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
281 | videobuf_queue_vmalloc_init(&s->vbuf_q, &cx18_videobuf_qops, | ||
282 | &cx->pci_dev->dev, &s->vbuf_q_lock, | ||
283 | V4L2_BUF_TYPE_VIDEO_CAPTURE, | ||
284 | V4L2_FIELD_INTERLACED, | ||
285 | sizeof(struct cx18_videobuf_buffer), | ||
286 | s, &cx->serialize_lock); | ||
287 | |||
288 | /* Assume the previous pixel default */ | ||
289 | s->pixelformat = V4L2_PIX_FMT_HM12; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | static int cx18_prep_dev(struct cx18 *cx, int type) | ||
294 | { | ||
295 | struct cx18_stream *s = &cx->streams[type]; | ||
296 | u32 cap = cx->v4l2_cap; | ||
297 | int num_offset = cx18_stream_info[type].num_offset; | ||
298 | int num = cx->instance + cx18_first_minor + num_offset; | ||
299 | |||
300 | /* | ||
301 | * These five fields are always initialized. | ||
302 | * For analog capture related streams, if video_dev == NULL then the | ||
303 | * stream is not in use. | ||
304 | * For the TS stream, if dvb == NULL then the stream is not in use. | ||
305 | * In those cases no other fields but these four can be used. | ||
306 | */ | ||
307 | s->video_dev = NULL; | ||
308 | s->dvb = NULL; | ||
309 | s->cx = cx; | ||
310 | s->type = type; | ||
311 | s->name = cx18_stream_info[type].name; | ||
312 | |||
313 | /* Check whether the radio is supported */ | ||
314 | if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO)) | ||
315 | return 0; | ||
316 | |||
317 | /* Check whether VBI is supported */ | ||
318 | if (type == CX18_ENC_STREAM_TYPE_VBI && | ||
319 | !(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE))) | ||
320 | return 0; | ||
321 | |||
322 | /* User explicitly selected 0 buffers for these streams, so don't | ||
323 | create them. */ | ||
324 | if (cx18_stream_info[type].dma != PCI_DMA_NONE && | ||
325 | cx->stream_buffers[type] == 0) { | ||
326 | CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name); | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | cx18_stream_init(cx, type); | ||
331 | |||
332 | /* Allocate the cx18_dvb struct only for the TS on cards with DTV */ | ||
333 | if (type == CX18_ENC_STREAM_TYPE_TS) { | ||
334 | if (cx->card->hw_all & CX18_HW_DVB) { | ||
335 | s->dvb = kzalloc(sizeof(struct cx18_dvb), GFP_KERNEL); | ||
336 | if (s->dvb == NULL) { | ||
337 | CX18_ERR("Couldn't allocate cx18_dvb structure" | ||
338 | " for %s\n", s->name); | ||
339 | return -ENOMEM; | ||
340 | } | ||
341 | } else { | ||
342 | /* Don't need buffers for the TS, if there is no DVB */ | ||
343 | s->buffers = 0; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | if (num_offset == -1) | ||
348 | return 0; | ||
349 | |||
350 | /* allocate and initialize the v4l2 video device structure */ | ||
351 | s->video_dev = video_device_alloc(); | ||
352 | if (s->video_dev == NULL) { | ||
353 | CX18_ERR("Couldn't allocate v4l2 video_device for %s\n", | ||
354 | s->name); | ||
355 | return -ENOMEM; | ||
356 | } | ||
357 | |||
358 | snprintf(s->video_dev->name, sizeof(s->video_dev->name), "%s %s", | ||
359 | cx->v4l2_dev.name, s->name); | ||
360 | |||
361 | s->video_dev->num = num; | ||
362 | s->video_dev->v4l2_dev = &cx->v4l2_dev; | ||
363 | s->video_dev->fops = &cx18_v4l2_enc_fops; | ||
364 | s->video_dev->release = video_device_release; | ||
365 | s->video_dev->tvnorms = V4L2_STD_ALL; | ||
366 | set_bit(V4L2_FL_USE_FH_PRIO, &s->video_dev->flags); | ||
367 | cx18_set_funcs(s->video_dev); | ||
368 | return 0; | ||
369 | } | ||
370 | |||
371 | /* Initialize v4l2 variables and register v4l2 devices */ | ||
372 | int cx18_streams_setup(struct cx18 *cx) | ||
373 | { | ||
374 | int type, ret; | ||
375 | |||
376 | /* Setup V4L2 Devices */ | ||
377 | for (type = 0; type < CX18_MAX_STREAMS; type++) { | ||
378 | /* Prepare device */ | ||
379 | ret = cx18_prep_dev(cx, type); | ||
380 | if (ret < 0) | ||
381 | break; | ||
382 | |||
383 | /* Allocate Stream */ | ||
384 | ret = cx18_stream_alloc(&cx->streams[type]); | ||
385 | if (ret < 0) | ||
386 | break; | ||
387 | } | ||
388 | if (type == CX18_MAX_STREAMS) | ||
389 | return 0; | ||
390 | |||
391 | /* One or more streams could not be initialized. Clean 'em all up. */ | ||
392 | cx18_streams_cleanup(cx, 0); | ||
393 | return ret; | ||
394 | } | ||
395 | |||
396 | static int cx18_reg_dev(struct cx18 *cx, int type) | ||
397 | { | ||
398 | struct cx18_stream *s = &cx->streams[type]; | ||
399 | int vfl_type = cx18_stream_info[type].vfl_type; | ||
400 | const char *name; | ||
401 | int num, ret; | ||
402 | |||
403 | if (type == CX18_ENC_STREAM_TYPE_TS && s->dvb != NULL) { | ||
404 | ret = cx18_dvb_register(s); | ||
405 | if (ret < 0) { | ||
406 | CX18_ERR("DVB failed to register\n"); | ||
407 | return ret; | ||
408 | } | ||
409 | } | ||
410 | |||
411 | if (s->video_dev == NULL) | ||
412 | return 0; | ||
413 | |||
414 | num = s->video_dev->num; | ||
415 | /* card number + user defined offset + device offset */ | ||
416 | if (type != CX18_ENC_STREAM_TYPE_MPG) { | ||
417 | struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG]; | ||
418 | |||
419 | if (s_mpg->video_dev) | ||
420 | num = s_mpg->video_dev->num | ||
421 | + cx18_stream_info[type].num_offset; | ||
422 | } | ||
423 | video_set_drvdata(s->video_dev, s); | ||
424 | |||
425 | /* Register device. First try the desired minor, then any free one. */ | ||
426 | ret = video_register_device_no_warn(s->video_dev, vfl_type, num); | ||
427 | if (ret < 0) { | ||
428 | CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n", | ||
429 | s->name, num); | ||
430 | video_device_release(s->video_dev); | ||
431 | s->video_dev = NULL; | ||
432 | return ret; | ||
433 | } | ||
434 | |||
435 | name = video_device_node_name(s->video_dev); | ||
436 | |||
437 | switch (vfl_type) { | ||
438 | case VFL_TYPE_GRABBER: | ||
439 | CX18_INFO("Registered device %s for %s (%d x %d.%02d kB)\n", | ||
440 | name, s->name, cx->stream_buffers[type], | ||
441 | cx->stream_buf_size[type] / 1024, | ||
442 | (cx->stream_buf_size[type] * 100 / 1024) % 100); | ||
443 | break; | ||
444 | |||
445 | case VFL_TYPE_RADIO: | ||
446 | CX18_INFO("Registered device %s for %s\n", name, s->name); | ||
447 | break; | ||
448 | |||
449 | case VFL_TYPE_VBI: | ||
450 | if (cx->stream_buffers[type]) | ||
451 | CX18_INFO("Registered device %s for %s " | ||
452 | "(%d x %d bytes)\n", | ||
453 | name, s->name, cx->stream_buffers[type], | ||
454 | cx->stream_buf_size[type]); | ||
455 | else | ||
456 | CX18_INFO("Registered device %s for %s\n", | ||
457 | name, s->name); | ||
458 | break; | ||
459 | } | ||
460 | |||
461 | return 0; | ||
462 | } | ||
463 | |||
464 | /* Register v4l2 devices */ | ||
465 | int cx18_streams_register(struct cx18 *cx) | ||
466 | { | ||
467 | int type; | ||
468 | int err; | ||
469 | int ret = 0; | ||
470 | |||
471 | /* Register V4L2 devices */ | ||
472 | for (type = 0; type < CX18_MAX_STREAMS; type++) { | ||
473 | err = cx18_reg_dev(cx, type); | ||
474 | if (err && ret == 0) | ||
475 | ret = err; | ||
476 | } | ||
477 | |||
478 | if (ret == 0) | ||
479 | return 0; | ||
480 | |||
481 | /* One or more streams could not be initialized. Clean 'em all up. */ | ||
482 | cx18_streams_cleanup(cx, 1); | ||
483 | return ret; | ||
484 | } | ||
485 | |||
486 | /* Unregister v4l2 devices */ | ||
487 | void cx18_streams_cleanup(struct cx18 *cx, int unregister) | ||
488 | { | ||
489 | struct video_device *vdev; | ||
490 | int type; | ||
491 | |||
492 | /* Teardown all streams */ | ||
493 | for (type = 0; type < CX18_MAX_STREAMS; type++) { | ||
494 | |||
495 | /* The TS has a cx18_dvb structure, not a video_device */ | ||
496 | if (type == CX18_ENC_STREAM_TYPE_TS) { | ||
497 | if (cx->streams[type].dvb != NULL) { | ||
498 | if (unregister) | ||
499 | cx18_dvb_unregister(&cx->streams[type]); | ||
500 | kfree(cx->streams[type].dvb); | ||
501 | cx->streams[type].dvb = NULL; | ||
502 | cx18_stream_free(&cx->streams[type]); | ||
503 | } | ||
504 | continue; | ||
505 | } | ||
506 | |||
507 | /* No struct video_device, but can have buffers allocated */ | ||
508 | if (type == CX18_ENC_STREAM_TYPE_IDX) { | ||
509 | /* If the module params didn't inhibit IDX ... */ | ||
510 | if (cx->stream_buffers[type] != 0) { | ||
511 | cx->stream_buffers[type] = 0; | ||
512 | /* | ||
513 | * Before calling cx18_stream_free(), | ||
514 | * check if the IDX stream was actually set up. | ||
515 | * Needed, since the cx18_probe() error path | ||
516 | * exits through here as well as normal clean up | ||
517 | */ | ||
518 | if (cx->streams[type].buffers != 0) | ||
519 | cx18_stream_free(&cx->streams[type]); | ||
520 | } | ||
521 | continue; | ||
522 | } | ||
523 | |||
524 | /* If struct video_device exists, can have buffers allocated */ | ||
525 | vdev = cx->streams[type].video_dev; | ||
526 | |||
527 | cx->streams[type].video_dev = NULL; | ||
528 | if (vdev == NULL) | ||
529 | continue; | ||
530 | |||
531 | if (type == CX18_ENC_STREAM_TYPE_YUV) | ||
532 | videobuf_mmap_free(&cx->streams[type].vbuf_q); | ||
533 | |||
534 | cx18_stream_free(&cx->streams[type]); | ||
535 | |||
536 | /* Unregister or release device */ | ||
537 | if (unregister) | ||
538 | video_unregister_device(vdev); | ||
539 | else | ||
540 | video_device_release(vdev); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | static void cx18_vbi_setup(struct cx18_stream *s) | ||
545 | { | ||
546 | struct cx18 *cx = s->cx; | ||
547 | int raw = cx18_raw_vbi(cx); | ||
548 | u32 data[CX2341X_MBOX_MAX_DATA]; | ||
549 | int lines; | ||
550 | |||
551 | if (cx->is_60hz) { | ||
552 | cx->vbi.count = 12; | ||
553 | cx->vbi.start[0] = 10; | ||
554 | cx->vbi.start[1] = 273; | ||
555 | } else { /* PAL/SECAM */ | ||
556 | cx->vbi.count = 18; | ||
557 | cx->vbi.start[0] = 6; | ||
558 | cx->vbi.start[1] = 318; | ||
559 | } | ||
560 | |||
561 | /* setup VBI registers */ | ||
562 | if (raw) | ||
563 | v4l2_subdev_call(cx->sd_av, vbi, s_raw_fmt, &cx->vbi.in.fmt.vbi); | ||
564 | else | ||
565 | v4l2_subdev_call(cx->sd_av, vbi, s_sliced_fmt, &cx->vbi.in.fmt.sliced); | ||
566 | |||
567 | /* | ||
568 | * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw | ||
569 | * VBI when the first analog capture channel starts, as once it starts | ||
570 | * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup | ||
571 | * (i.e. for the VBI capture channels). We also send it for each | ||
572 | * analog capture channel anyway just to make sure we get the proper | ||
573 | * behavior | ||
574 | */ | ||
575 | if (raw) { | ||
576 | lines = cx->vbi.count * 2; | ||
577 | } else { | ||
578 | /* | ||
579 | * For 525/60 systems, according to the VIP 2 & BT.656 std: | ||
580 | * The EAV RP code's Field bit toggles on line 4, a few lines | ||
581 | * after the Vertcal Blank bit has already toggled. | ||
582 | * Tell the encoder to capture 21-4+1=18 lines per field, | ||
583 | * since we want lines 10 through 21. | ||
584 | * | ||
585 | * For 625/50 systems, according to the VIP 2 & BT.656 std: | ||
586 | * The EAV RP code's Field bit toggles on line 1, a few lines | ||
587 | * after the Vertcal Blank bit has already toggled. | ||
588 | * (We've actually set the digitizer so that the Field bit | ||
589 | * toggles on line 2.) Tell the encoder to capture 23-2+1=22 | ||
590 | * lines per field, since we want lines 6 through 23. | ||
591 | */ | ||
592 | lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2; | ||
593 | } | ||
594 | |||
595 | data[0] = s->handle; | ||
596 | /* Lines per field */ | ||
597 | data[1] = (lines / 2) | ((lines / 2) << 16); | ||
598 | /* bytes per line */ | ||
599 | data[2] = (raw ? vbi_active_samples | ||
600 | : (cx->is_60hz ? vbi_hblank_samples_60Hz | ||
601 | : vbi_hblank_samples_50Hz)); | ||
602 | /* Every X number of frames a VBI interrupt arrives | ||
603 | (frames as in 25 or 30 fps) */ | ||
604 | data[3] = 1; | ||
605 | /* | ||
606 | * Set the SAV/EAV RP codes to look for as start/stop points | ||
607 | * when in VIP-1.1 mode | ||
608 | */ | ||
609 | if (raw) { | ||
610 | /* | ||
611 | * Start codes for beginning of "active" line in vertical blank | ||
612 | * 0x20 ( VerticalBlank ) | ||
613 | * 0x60 ( EvenField VerticalBlank ) | ||
614 | */ | ||
615 | data[4] = 0x20602060; | ||
616 | /* | ||
617 | * End codes for end of "active" raw lines and regular lines | ||
618 | * 0x30 ( VerticalBlank HorizontalBlank) | ||
619 | * 0x70 ( EvenField VerticalBlank HorizontalBlank) | ||
620 | * 0x90 (Task HorizontalBlank) | ||
621 | * 0xd0 (Task EvenField HorizontalBlank) | ||
622 | */ | ||
623 | data[5] = 0x307090d0; | ||
624 | } else { | ||
625 | /* | ||
626 | * End codes for active video, we want data in the hblank region | ||
627 | * 0xb0 (Task 0 VerticalBlank HorizontalBlank) | ||
628 | * 0xf0 (Task EvenField VerticalBlank HorizontalBlank) | ||
629 | * | ||
630 | * Since the V bit is only allowed to toggle in the EAV RP code, | ||
631 | * just before the first active region line, these two | ||
632 | * are problematic: | ||
633 | * 0x90 (Task HorizontalBlank) | ||
634 | * 0xd0 (Task EvenField HorizontalBlank) | ||
635 | * | ||
636 | * We have set the digitzer such that we don't have to worry | ||
637 | * about these problem codes. | ||
638 | */ | ||
639 | data[4] = 0xB0F0B0F0; | ||
640 | /* | ||
641 | * Start codes for beginning of active line in vertical blank | ||
642 | * 0xa0 (Task VerticalBlank ) | ||
643 | * 0xe0 (Task EvenField VerticalBlank ) | ||
644 | */ | ||
645 | data[5] = 0xA0E0A0E0; | ||
646 | } | ||
647 | |||
648 | CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n", | ||
649 | data[0], data[1], data[2], data[3], data[4], data[5]); | ||
650 | |||
651 | cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); | ||
652 | } | ||
653 | |||
654 | void cx18_stream_rotate_idx_mdls(struct cx18 *cx) | ||
655 | { | ||
656 | struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; | ||
657 | struct cx18_mdl *mdl; | ||
658 | |||
659 | if (!cx18_stream_enabled(s)) | ||
660 | return; | ||
661 | |||
662 | /* Return if the firmware is not running low on MDLs */ | ||
663 | if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >= | ||
664 | CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN) | ||
665 | return; | ||
666 | |||
667 | /* Return if there are no MDLs to rotate back to the firmware */ | ||
668 | if (atomic_read(&s->q_full.depth) < 2) | ||
669 | return; | ||
670 | |||
671 | /* | ||
672 | * Take the oldest IDX MDL still holding data, and discard its index | ||
673 | * entries by scheduling the MDL to go back to the firmware | ||
674 | */ | ||
675 | mdl = cx18_dequeue(s, &s->q_full); | ||
676 | if (mdl != NULL) | ||
677 | cx18_enqueue(s, mdl, &s->q_free); | ||
678 | } | ||
679 | |||
680 | static | ||
681 | struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s, | ||
682 | struct cx18_mdl *mdl) | ||
683 | { | ||
684 | struct cx18 *cx = s->cx; | ||
685 | struct cx18_queue *q; | ||
686 | |||
687 | /* Don't give it to the firmware, if we're not running a capture */ | ||
688 | if (s->handle == CX18_INVALID_TASK_HANDLE || | ||
689 | test_bit(CX18_F_S_STOPPING, &s->s_flags) || | ||
690 | !test_bit(CX18_F_S_STREAMING, &s->s_flags)) | ||
691 | return cx18_enqueue(s, mdl, &s->q_free); | ||
692 | |||
693 | q = cx18_enqueue(s, mdl, &s->q_busy); | ||
694 | if (q != &s->q_busy) | ||
695 | return q; /* The firmware has the max MDLs it can handle */ | ||
696 | |||
697 | cx18_mdl_sync_for_device(s, mdl); | ||
698 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, | ||
699 | (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem, | ||
700 | s->bufs_per_mdl, mdl->id, s->mdl_size); | ||
701 | return q; | ||
702 | } | ||
703 | |||
704 | static | ||
705 | void _cx18_stream_load_fw_queue(struct cx18_stream *s) | ||
706 | { | ||
707 | struct cx18_queue *q; | ||
708 | struct cx18_mdl *mdl; | ||
709 | |||
710 | if (atomic_read(&s->q_free.depth) == 0 || | ||
711 | atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM) | ||
712 | return; | ||
713 | |||
714 | /* Move from q_free to q_busy notifying the firmware, until the limit */ | ||
715 | do { | ||
716 | mdl = cx18_dequeue(s, &s->q_free); | ||
717 | if (mdl == NULL) | ||
718 | break; | ||
719 | q = _cx18_stream_put_mdl_fw(s, mdl); | ||
720 | } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM | ||
721 | && q == &s->q_busy); | ||
722 | } | ||
723 | |||
724 | void cx18_out_work_handler(struct work_struct *work) | ||
725 | { | ||
726 | struct cx18_stream *s = | ||
727 | container_of(work, struct cx18_stream, out_work_order); | ||
728 | |||
729 | _cx18_stream_load_fw_queue(s); | ||
730 | } | ||
731 | |||
732 | static void cx18_stream_configure_mdls(struct cx18_stream *s) | ||
733 | { | ||
734 | cx18_unload_queues(s); | ||
735 | |||
736 | switch (s->type) { | ||
737 | case CX18_ENC_STREAM_TYPE_YUV: | ||
738 | /* | ||
739 | * Height should be a multiple of 32 lines. | ||
740 | * Set the MDL size to the exact size needed for one frame. | ||
741 | * Use enough buffers per MDL to cover the MDL size | ||
742 | */ | ||
743 | if (s->pixelformat == V4L2_PIX_FMT_HM12) | ||
744 | s->mdl_size = 720 * s->cx->cxhdl.height * 3 / 2; | ||
745 | else | ||
746 | s->mdl_size = 720 * s->cx->cxhdl.height * 2; | ||
747 | s->bufs_per_mdl = s->mdl_size / s->buf_size; | ||
748 | if (s->mdl_size % s->buf_size) | ||
749 | s->bufs_per_mdl++; | ||
750 | break; | ||
751 | case CX18_ENC_STREAM_TYPE_VBI: | ||
752 | s->bufs_per_mdl = 1; | ||
753 | if (cx18_raw_vbi(s->cx)) { | ||
754 | s->mdl_size = (s->cx->is_60hz ? 12 : 18) | ||
755 | * 2 * vbi_active_samples; | ||
756 | } else { | ||
757 | /* | ||
758 | * See comment in cx18_vbi_setup() below about the | ||
759 | * extra lines we capture in sliced VBI mode due to | ||
760 | * the lines on which EAV RP codes toggle. | ||
761 | */ | ||
762 | s->mdl_size = s->cx->is_60hz | ||
763 | ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz | ||
764 | : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz; | ||
765 | } | ||
766 | break; | ||
767 | default: | ||
768 | s->bufs_per_mdl = 1; | ||
769 | s->mdl_size = s->buf_size * s->bufs_per_mdl; | ||
770 | break; | ||
771 | } | ||
772 | |||
773 | cx18_load_queues(s); | ||
774 | } | ||
775 | |||
776 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s) | ||
777 | { | ||
778 | u32 data[MAX_MB_ARGUMENTS]; | ||
779 | struct cx18 *cx = s->cx; | ||
780 | int captype = 0; | ||
781 | struct cx18_stream *s_idx; | ||
782 | |||
783 | if (!cx18_stream_enabled(s)) | ||
784 | return -EINVAL; | ||
785 | |||
786 | CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); | ||
787 | |||
788 | switch (s->type) { | ||
789 | case CX18_ENC_STREAM_TYPE_MPG: | ||
790 | captype = CAPTURE_CHANNEL_TYPE_MPEG; | ||
791 | cx->mpg_data_received = cx->vbi_data_inserted = 0; | ||
792 | cx->dualwatch_jiffies = jiffies; | ||
793 | cx->dualwatch_stereo_mode = v4l2_ctrl_g_ctrl(cx->cxhdl.audio_mode); | ||
794 | cx->search_pack_header = 0; | ||
795 | break; | ||
796 | |||
797 | case CX18_ENC_STREAM_TYPE_IDX: | ||
798 | captype = CAPTURE_CHANNEL_TYPE_INDEX; | ||
799 | break; | ||
800 | case CX18_ENC_STREAM_TYPE_TS: | ||
801 | captype = CAPTURE_CHANNEL_TYPE_TS; | ||
802 | break; | ||
803 | case CX18_ENC_STREAM_TYPE_YUV: | ||
804 | captype = CAPTURE_CHANNEL_TYPE_YUV; | ||
805 | break; | ||
806 | case CX18_ENC_STREAM_TYPE_PCM: | ||
807 | captype = CAPTURE_CHANNEL_TYPE_PCM; | ||
808 | break; | ||
809 | case CX18_ENC_STREAM_TYPE_VBI: | ||
810 | #ifdef CX18_ENCODER_PARSES_SLICED | ||
811 | captype = cx18_raw_vbi(cx) ? | ||
812 | CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI; | ||
813 | #else | ||
814 | /* | ||
815 | * Currently we set things up so that Sliced VBI from the | ||
816 | * digitizer is handled as Raw VBI by the encoder | ||
817 | */ | ||
818 | captype = CAPTURE_CHANNEL_TYPE_VBI; | ||
819 | #endif | ||
820 | cx->vbi.frame = 0; | ||
821 | cx->vbi.inserted_frame = 0; | ||
822 | memset(cx->vbi.sliced_mpeg_size, | ||
823 | 0, sizeof(cx->vbi.sliced_mpeg_size)); | ||
824 | break; | ||
825 | default: | ||
826 | return -EINVAL; | ||
827 | } | ||
828 | |||
829 | /* Clear Streamoff flags in case left from last capture */ | ||
830 | clear_bit(CX18_F_S_STREAMOFF, &s->s_flags); | ||
831 | |||
832 | cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE); | ||
833 | s->handle = data[0]; | ||
834 | cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype); | ||
835 | |||
836 | /* | ||
837 | * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and | ||
838 | * set up all the parameters, as it is not obvious which parameters the | ||
839 | * firmware shares across capture channel types and which it does not. | ||
840 | * | ||
841 | * Some of the cx18_vapi() calls below apply to only certain capture | ||
842 | * channel types. We're hoping there's no harm in calling most of them | ||
843 | * anyway, as long as the values are all consistent. Setting some | ||
844 | * shared parameters will have no effect once an analog capture channel | ||
845 | * has started streaming. | ||
846 | */ | ||
847 | if (captype != CAPTURE_CHANNEL_TYPE_TS) { | ||
848 | cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0); | ||
849 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1); | ||
850 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0); | ||
851 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1); | ||
852 | |||
853 | /* | ||
854 | * Audio related reset according to | ||
855 | * Documentation/video4linux/cx2341x/fw-encoder-api.txt | ||
856 | */ | ||
857 | if (atomic_read(&cx->ana_capturing) == 0) | ||
858 | cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, | ||
859 | s->handle, 12); | ||
860 | |||
861 | /* | ||
862 | * Number of lines for Field 1 & Field 2 according to | ||
863 | * Documentation/video4linux/cx2341x/fw-encoder-api.txt | ||
864 | * Field 1 is 312 for 625 line systems in BT.656 | ||
865 | * Field 2 is 313 for 625 line systems in BT.656 | ||
866 | */ | ||
867 | cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3, | ||
868 | s->handle, 312, 313); | ||
869 | |||
870 | if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE) | ||
871 | cx18_vbi_setup(s); | ||
872 | |||
873 | /* | ||
874 | * Select to receive I, P, and B frame index entries, if the | ||
875 | * index stream is enabled. Otherwise disable index entry | ||
876 | * generation. | ||
877 | */ | ||
878 | s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; | ||
879 | cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2, | ||
880 | s->handle, cx18_stream_enabled(s_idx) ? 7 : 0); | ||
881 | |||
882 | /* Call out to the common CX2341x API setup for user controls */ | ||
883 | cx->cxhdl.priv = s; | ||
884 | cx2341x_handler_setup(&cx->cxhdl); | ||
885 | |||
886 | /* | ||
887 | * When starting a capture and we're set for radio, | ||
888 | * ensure the video is muted, despite the user control. | ||
889 | */ | ||
890 | if (!cx->cxhdl.video_mute && | ||
891 | test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) | ||
892 | cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle, | ||
893 | (v4l2_ctrl_g_ctrl(cx->cxhdl.video_mute_yuv) << 8) | 1); | ||
894 | |||
895 | /* Enable the Video Format Converter for UYVY 4:2:2 support, | ||
896 | * rather than the default HM12 Macroblovk 4:2:0 support. | ||
897 | */ | ||
898 | if (captype == CAPTURE_CHANNEL_TYPE_YUV) { | ||
899 | if (s->pixelformat == V4L2_PIX_FMT_UYVY) | ||
900 | cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, | ||
901 | s->handle, 1); | ||
902 | else | ||
903 | /* If in doubt, default to HM12 */ | ||
904 | cx18_vapi(cx, CX18_CPU_SET_VFC_PARAM, 2, | ||
905 | s->handle, 0); | ||
906 | } | ||
907 | } | ||
908 | |||
909 | if (atomic_read(&cx->tot_capturing) == 0) { | ||
910 | cx2341x_handler_set_busy(&cx->cxhdl, 1); | ||
911 | clear_bit(CX18_F_I_EOS, &cx->i_flags); | ||
912 | cx18_write_reg(cx, 7, CX18_DSP0_INTERRUPT_MASK); | ||
913 | } | ||
914 | |||
915 | cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle, | ||
916 | (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem, | ||
917 | (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); | ||
918 | |||
919 | /* Init all the cpu_mdls for this stream */ | ||
920 | cx18_stream_configure_mdls(s); | ||
921 | _cx18_stream_load_fw_queue(s); | ||
922 | |||
923 | /* begin_capture */ | ||
924 | if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { | ||
925 | CX18_DEBUG_WARN("Error starting capture!\n"); | ||
926 | /* Ensure we're really not capturing before releasing MDLs */ | ||
927 | set_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
928 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) | ||
929 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); | ||
930 | else | ||
931 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); | ||
932 | clear_bit(CX18_F_S_STREAMING, &s->s_flags); | ||
933 | /* FIXME - CX18_F_S_STREAMOFF as well? */ | ||
934 | cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); | ||
935 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); | ||
936 | s->handle = CX18_INVALID_TASK_HANDLE; | ||
937 | clear_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
938 | if (atomic_read(&cx->tot_capturing) == 0) { | ||
939 | set_bit(CX18_F_I_EOS, &cx->i_flags); | ||
940 | cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); | ||
941 | } | ||
942 | return -EINVAL; | ||
943 | } | ||
944 | |||
945 | /* you're live! sit back and await interrupts :) */ | ||
946 | if (captype != CAPTURE_CHANNEL_TYPE_TS) | ||
947 | atomic_inc(&cx->ana_capturing); | ||
948 | atomic_inc(&cx->tot_capturing); | ||
949 | return 0; | ||
950 | } | ||
951 | EXPORT_SYMBOL(cx18_start_v4l2_encode_stream); | ||
952 | |||
953 | void cx18_stop_all_captures(struct cx18 *cx) | ||
954 | { | ||
955 | int i; | ||
956 | |||
957 | for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) { | ||
958 | struct cx18_stream *s = &cx->streams[i]; | ||
959 | |||
960 | if (!cx18_stream_enabled(s)) | ||
961 | continue; | ||
962 | if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) | ||
963 | cx18_stop_v4l2_encode_stream(s, 0); | ||
964 | } | ||
965 | } | ||
966 | |||
967 | int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) | ||
968 | { | ||
969 | struct cx18 *cx = s->cx; | ||
970 | unsigned long then; | ||
971 | |||
972 | if (!cx18_stream_enabled(s)) | ||
973 | return -EINVAL; | ||
974 | |||
975 | /* This function assumes that you are allowed to stop the capture | ||
976 | and that we are actually capturing */ | ||
977 | |||
978 | CX18_DEBUG_INFO("Stop Capture\n"); | ||
979 | |||
980 | if (atomic_read(&cx->tot_capturing) == 0) | ||
981 | return 0; | ||
982 | |||
983 | set_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
984 | if (s->type == CX18_ENC_STREAM_TYPE_MPG) | ||
985 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); | ||
986 | else | ||
987 | cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); | ||
988 | |||
989 | then = jiffies; | ||
990 | |||
991 | if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) { | ||
992 | CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); | ||
993 | } | ||
994 | |||
995 | if (s->type != CX18_ENC_STREAM_TYPE_TS) | ||
996 | atomic_dec(&cx->ana_capturing); | ||
997 | atomic_dec(&cx->tot_capturing); | ||
998 | |||
999 | /* Clear capture and no-read bits */ | ||
1000 | clear_bit(CX18_F_S_STREAMING, &s->s_flags); | ||
1001 | |||
1002 | /* Tell the CX23418 it can't use our buffers anymore */ | ||
1003 | cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); | ||
1004 | |||
1005 | cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); | ||
1006 | s->handle = CX18_INVALID_TASK_HANDLE; | ||
1007 | clear_bit(CX18_F_S_STOPPING, &s->s_flags); | ||
1008 | |||
1009 | if (atomic_read(&cx->tot_capturing) > 0) | ||
1010 | return 0; | ||
1011 | |||
1012 | cx2341x_handler_set_busy(&cx->cxhdl, 0); | ||
1013 | cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); | ||
1014 | wake_up(&s->waitq); | ||
1015 | |||
1016 | return 0; | ||
1017 | } | ||
1018 | EXPORT_SYMBOL(cx18_stop_v4l2_encode_stream); | ||
1019 | |||
1020 | u32 cx18_find_handle(struct cx18 *cx) | ||
1021 | { | ||
1022 | int i; | ||
1023 | |||
1024 | /* find first available handle to be used for global settings */ | ||
1025 | for (i = 0; i < CX18_MAX_STREAMS; i++) { | ||
1026 | struct cx18_stream *s = &cx->streams[i]; | ||
1027 | |||
1028 | if (s->video_dev && (s->handle != CX18_INVALID_TASK_HANDLE)) | ||
1029 | return s->handle; | ||
1030 | } | ||
1031 | return CX18_INVALID_TASK_HANDLE; | ||
1032 | } | ||
1033 | |||
1034 | struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle) | ||
1035 | { | ||
1036 | int i; | ||
1037 | struct cx18_stream *s; | ||
1038 | |||
1039 | if (handle == CX18_INVALID_TASK_HANDLE) | ||
1040 | return NULL; | ||
1041 | |||
1042 | for (i = 0; i < CX18_MAX_STREAMS; i++) { | ||
1043 | s = &cx->streams[i]; | ||
1044 | if (s->handle != handle) | ||
1045 | continue; | ||
1046 | if (cx18_stream_enabled(s)) | ||
1047 | return s; | ||
1048 | } | ||
1049 | return NULL; | ||
1050 | } | ||
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h new file mode 100644 index 00000000000..713b0e61536 --- /dev/null +++ b/drivers/media/video/cx18/cx18-streams.h | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * cx18 init/start/stop/exit stream functions | ||
3 | * | ||
4 | * Derived from ivtv-streams.h | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
22 | * 02111-1307 USA | ||
23 | */ | ||
24 | |||
25 | u32 cx18_find_handle(struct cx18 *cx); | ||
26 | struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle); | ||
27 | int cx18_streams_setup(struct cx18 *cx); | ||
28 | int cx18_streams_register(struct cx18 *cx); | ||
29 | void cx18_streams_cleanup(struct cx18 *cx, int unregister); | ||
30 | |||
31 | #define CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN (3) | ||
32 | void cx18_stream_rotate_idx_mdls(struct cx18 *cx); | ||
33 | |||
34 | static inline bool cx18_stream_enabled(struct cx18_stream *s) | ||
35 | { | ||
36 | return s->video_dev || | ||
37 | (s->dvb && s->dvb->enabled) || | ||
38 | (s->type == CX18_ENC_STREAM_TYPE_IDX && | ||
39 | s->cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] != 0); | ||
40 | } | ||
41 | |||
42 | /* Related to submission of mdls to firmware */ | ||
43 | static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) | ||
44 | { | ||
45 | schedule_work(&s->out_work_order); | ||
46 | } | ||
47 | |||
48 | static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s, | ||
49 | struct cx18_mdl *mdl) | ||
50 | { | ||
51 | /* Put mdl on q_free; the out work handler will move mdl(s) to q_busy */ | ||
52 | cx18_enqueue(s, mdl, &s->q_free); | ||
53 | cx18_stream_load_fw_queue(s); | ||
54 | } | ||
55 | |||
56 | void cx18_out_work_handler(struct work_struct *work); | ||
57 | |||
58 | /* Capture related */ | ||
59 | int cx18_start_v4l2_encode_stream(struct cx18_stream *s); | ||
60 | int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end); | ||
61 | |||
62 | void cx18_stop_all_captures(struct cx18 *cx); | ||
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c new file mode 100644 index 00000000000..6d3121ff45a --- /dev/null +++ b/drivers/media/video/cx18/cx18-vbi.c | |||
@@ -0,0 +1,277 @@ | |||
1 | /* | ||
2 | * cx18 Vertical Blank Interval support functions | ||
3 | * | ||
4 | * Derived from ivtv-vbi.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
21 | * 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | #include "cx18-driver.h" | ||
25 | #include "cx18-vbi.h" | ||
26 | #include "cx18-ioctl.h" | ||
27 | #include "cx18-queue.h" | ||
28 | |||
29 | /* | ||
30 | * Raster Reference/Protection (RP) bytes, used in Start/End Active | ||
31 | * Video codes emitted from the digitzer in VIP 1.x mode, that flag the start | ||
32 | * of VBI sample or VBI ancillary data regions in the digitial ratser line. | ||
33 | * | ||
34 | * Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0 | ||
35 | */ | ||
36 | static const u8 raw_vbi_sav_rp[2] = { 0x20, 0x60 }; /* __V_, _FV_ */ | ||
37 | static const u8 sliced_vbi_eav_rp[2] = { 0xb0, 0xf0 }; /* T_VH, TFVH */ | ||
38 | |||
39 | static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) | ||
40 | { | ||
41 | int line = 0; | ||
42 | int i; | ||
43 | u32 linemask[2] = { 0, 0 }; | ||
44 | unsigned short size; | ||
45 | static const u8 mpeg_hdr_data[] = { | ||
46 | /* MPEG-2 Program Pack */ | ||
47 | 0x00, 0x00, 0x01, 0xba, /* Prog Pack start code */ | ||
48 | 0x44, 0x00, 0x0c, 0x66, 0x24, 0x01, /* SCR, SCR Ext, markers */ | ||
49 | 0x01, 0xd1, 0xd3, /* Mux Rate, markers */ | ||
50 | 0xfa, 0xff, 0xff, /* Res, Suff cnt, Stuff */ | ||
51 | /* MPEG-2 Private Stream 1 PES Packet */ | ||
52 | 0x00, 0x00, 0x01, 0xbd, /* Priv Stream 1 start */ | ||
53 | 0x00, 0x1a, /* length */ | ||
54 | 0x84, 0x80, 0x07, /* flags, hdr data len */ | ||
55 | 0x21, 0x00, 0x5d, 0x63, 0xa7, /* PTS, markers */ | ||
56 | 0xff, 0xff /* stuffing */ | ||
57 | }; | ||
58 | const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */ | ||
59 | int idx = cx->vbi.frame % CX18_VBI_FRAMES; | ||
60 | u8 *dst = &cx->vbi.sliced_mpeg_data[idx][0]; | ||
61 | |||
62 | for (i = 0; i < lines; i++) { | ||
63 | struct v4l2_sliced_vbi_data *sdata = cx->vbi.sliced_data + i; | ||
64 | int f, l; | ||
65 | |||
66 | if (sdata->id == 0) | ||
67 | continue; | ||
68 | |||
69 | l = sdata->line - 6; | ||
70 | f = sdata->field; | ||
71 | if (f) | ||
72 | l += 18; | ||
73 | if (l < 32) | ||
74 | linemask[0] |= (1 << l); | ||
75 | else | ||
76 | linemask[1] |= (1 << (l - 32)); | ||
77 | dst[sd + 12 + line * 43] = cx18_service2vbi(sdata->id); | ||
78 | memcpy(dst + sd + 12 + line * 43 + 1, sdata->data, 42); | ||
79 | line++; | ||
80 | } | ||
81 | memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data)); | ||
82 | if (line == 36) { | ||
83 | /* All lines are used, so there is no space for the linemask | ||
84 | (the max size of the VBI data is 36 * 43 + 4 bytes). | ||
85 | So in this case we use the magic number 'ITV0'. */ | ||
86 | memcpy(dst + sd, "ITV0", 4); | ||
87 | memcpy(dst + sd + 4, dst + sd + 12, line * 43); | ||
88 | size = 4 + ((43 * line + 3) & ~3); | ||
89 | } else { | ||
90 | memcpy(dst + sd, "itv0", 4); | ||
91 | cpu_to_le32s(&linemask[0]); | ||
92 | cpu_to_le32s(&linemask[1]); | ||
93 | memcpy(dst + sd + 4, &linemask[0], 8); | ||
94 | size = 12 + ((43 * line + 3) & ~3); | ||
95 | } | ||
96 | dst[4+16] = (size + 10) >> 8; | ||
97 | dst[5+16] = (size + 10) & 0xff; | ||
98 | dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6); | ||
99 | dst[10+16] = (pts_stamp >> 22) & 0xff; | ||
100 | dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff); | ||
101 | dst[12+16] = (pts_stamp >> 7) & 0xff; | ||
102 | dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1); | ||
103 | cx->vbi.sliced_mpeg_size[idx] = sd + size; | ||
104 | } | ||
105 | |||
106 | /* Compress raw VBI format, removes leading SAV codes and surplus space | ||
107 | after the frame. Returns new compressed size. */ | ||
108 | /* FIXME - this function ignores the input size. */ | ||
109 | static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size) | ||
110 | { | ||
111 | u32 line_size = vbi_active_samples; | ||
112 | u32 lines = cx->vbi.count * 2; | ||
113 | u8 *q = buf; | ||
114 | u8 *p; | ||
115 | int i; | ||
116 | |||
117 | /* Skip the header */ | ||
118 | buf += hdr_size; | ||
119 | |||
120 | for (i = 0; i < lines; i++) { | ||
121 | p = buf + i * line_size; | ||
122 | |||
123 | /* Look for SAV code */ | ||
124 | if (p[0] != 0xff || p[1] || p[2] || | ||
125 | (p[3] != raw_vbi_sav_rp[0] && | ||
126 | p[3] != raw_vbi_sav_rp[1])) | ||
127 | break; | ||
128 | if (i == lines - 1) { | ||
129 | /* last line is hdr_size bytes short - extrapolate it */ | ||
130 | memcpy(q, p + 4, line_size - 4 - hdr_size); | ||
131 | q += line_size - 4 - hdr_size; | ||
132 | p += line_size - hdr_size - 1; | ||
133 | memset(q, (int) *p, hdr_size); | ||
134 | } else { | ||
135 | memcpy(q, p + 4, line_size - 4); | ||
136 | q += line_size - 4; | ||
137 | } | ||
138 | } | ||
139 | return lines * (line_size - 4); | ||
140 | } | ||
141 | |||
142 | static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, | ||
143 | const u32 hdr_size) | ||
144 | { | ||
145 | struct v4l2_decode_vbi_line vbi; | ||
146 | int i; | ||
147 | u32 line = 0; | ||
148 | u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz | ||
149 | : vbi_hblank_samples_50Hz; | ||
150 | |||
151 | /* find the first valid line */ | ||
152 | for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) { | ||
153 | if (buf[0] == 0xff && !buf[1] && !buf[2] && | ||
154 | (buf[3] == sliced_vbi_eav_rp[0] || | ||
155 | buf[3] == sliced_vbi_eav_rp[1])) | ||
156 | break; | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * The last line is short by hdr_size bytes, but for the remaining | ||
161 | * checks against size, we pretend that it is not, by counting the | ||
162 | * header bytes we knowingly skipped | ||
163 | */ | ||
164 | size -= (i - hdr_size); | ||
165 | if (size < line_size) | ||
166 | return line; | ||
167 | |||
168 | for (i = 0; i < size / line_size; i++) { | ||
169 | u8 *p = buf + i * line_size; | ||
170 | |||
171 | /* Look for EAV code */ | ||
172 | if (p[0] != 0xff || p[1] || p[2] || | ||
173 | (p[3] != sliced_vbi_eav_rp[0] && | ||
174 | p[3] != sliced_vbi_eav_rp[1])) | ||
175 | continue; | ||
176 | vbi.p = p + 4; | ||
177 | v4l2_subdev_call(cx->sd_av, vbi, decode_vbi_line, &vbi); | ||
178 | if (vbi.type) { | ||
179 | cx->vbi.sliced_data[line].id = vbi.type; | ||
180 | cx->vbi.sliced_data[line].field = vbi.is_second_field; | ||
181 | cx->vbi.sliced_data[line].line = vbi.line; | ||
182 | memcpy(cx->vbi.sliced_data[line].data, vbi.p, 42); | ||
183 | line++; | ||
184 | } | ||
185 | } | ||
186 | return line; | ||
187 | } | ||
188 | |||
189 | static void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf) | ||
190 | { | ||
191 | /* | ||
192 | * The CX23418 provides a 12 byte header in its raw VBI buffers to us: | ||
193 | * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp] | ||
194 | */ | ||
195 | struct vbi_data_hdr { | ||
196 | __be32 magic; | ||
197 | __be32 unknown; | ||
198 | __be32 pts; | ||
199 | } *hdr = (struct vbi_data_hdr *) buf->buf; | ||
200 | |||
201 | u8 *p = (u8 *) buf->buf; | ||
202 | u32 size = buf->bytesused; | ||
203 | u32 pts; | ||
204 | int lines; | ||
205 | |||
206 | /* | ||
207 | * The CX23418 sends us data that is 32 bit little-endian swapped, | ||
208 | * but we want the raw VBI bytes in the order they were in the raster | ||
209 | * line. This has a side effect of making the header big endian | ||
210 | */ | ||
211 | cx18_buf_swap(buf); | ||
212 | |||
213 | /* Raw VBI data */ | ||
214 | if (cx18_raw_vbi(cx)) { | ||
215 | |||
216 | size = buf->bytesused = | ||
217 | compress_raw_buf(cx, p, size, sizeof(struct vbi_data_hdr)); | ||
218 | |||
219 | /* | ||
220 | * Hack needed for compatibility with old VBI software. | ||
221 | * Write the frame # at the last 4 bytes of the frame | ||
222 | */ | ||
223 | p += size - 4; | ||
224 | memcpy(p, &cx->vbi.frame, 4); | ||
225 | cx->vbi.frame++; | ||
226 | return; | ||
227 | } | ||
228 | |||
229 | /* Sliced VBI data with data insertion */ | ||
230 | |||
231 | pts = (be32_to_cpu(hdr->magic) == 0x3fffffff) ? be32_to_cpu(hdr->pts) | ||
232 | : 0; | ||
233 | |||
234 | lines = compress_sliced_buf(cx, p, size, sizeof(struct vbi_data_hdr)); | ||
235 | |||
236 | /* always return at least one empty line */ | ||
237 | if (lines == 0) { | ||
238 | cx->vbi.sliced_data[0].id = 0; | ||
239 | cx->vbi.sliced_data[0].line = 0; | ||
240 | cx->vbi.sliced_data[0].field = 0; | ||
241 | lines = 1; | ||
242 | } | ||
243 | buf->bytesused = size = lines * sizeof(cx->vbi.sliced_data[0]); | ||
244 | memcpy(p, &cx->vbi.sliced_data[0], size); | ||
245 | |||
246 | if (cx->vbi.insert_mpeg) | ||
247 | copy_vbi_data(cx, lines, pts); | ||
248 | cx->vbi.frame++; | ||
249 | } | ||
250 | |||
251 | void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl, | ||
252 | int streamtype) | ||
253 | { | ||
254 | struct cx18_buffer *buf; | ||
255 | u32 orig_used; | ||
256 | |||
257 | if (streamtype != CX18_ENC_STREAM_TYPE_VBI) | ||
258 | return; | ||
259 | |||
260 | /* | ||
261 | * Big assumption here: | ||
262 | * Every buffer hooked to the MDL's buf_list is a complete VBI frame | ||
263 | * that ends at the end of the buffer. | ||
264 | * | ||
265 | * To assume anything else would make the code in this file | ||
266 | * more complex, or require extra memcpy()'s to make the | ||
267 | * buffers satisfy the above assumption. It's just simpler to set | ||
268 | * up the encoder buffer transfers to make the assumption true. | ||
269 | */ | ||
270 | list_for_each_entry(buf, &mdl->buf_list, list) { | ||
271 | orig_used = buf->bytesused; | ||
272 | if (orig_used == 0) | ||
273 | break; | ||
274 | _cx18_process_vbi_data(cx, buf); | ||
275 | mdl->bytesused -= (orig_used - buf->bytesused); | ||
276 | } | ||
277 | } | ||
diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h new file mode 100644 index 00000000000..b365cf4b466 --- /dev/null +++ b/drivers/media/video/cx18/cx18-vbi.h | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * cx18 Vertical Blank Interval support functions | ||
3 | * | ||
4 | * Derived from ivtv-vbi.h | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
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., 59 Temple Place, Suite 330, Boston, MA | ||
21 | * 02111-1307 USA | ||
22 | */ | ||
23 | |||
24 | void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl, | ||
25 | int streamtype); | ||
26 | int cx18_used_line(struct cx18 *cx, int line, int field); | ||
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h new file mode 100644 index 00000000000..fed48b6bb67 --- /dev/null +++ b/drivers/media/video/cx18/cx18-version.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * cx18 driver version information | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
19 | * 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #ifndef CX18_VERSION_H | ||
23 | #define CX18_VERSION_H | ||
24 | |||
25 | #define CX18_DRIVER_NAME "cx18" | ||
26 | #define CX18_VERSION "1.5.1" | ||
27 | |||
28 | #endif | ||
diff --git a/drivers/media/video/cx18/cx18-video.c b/drivers/media/video/cx18/cx18-video.c new file mode 100644 index 00000000000..6dc84aac8f4 --- /dev/null +++ b/drivers/media/video/cx18/cx18-video.c | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * cx18 video interface functions | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
19 | * 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #include "cx18-driver.h" | ||
23 | #include "cx18-video.h" | ||
24 | #include "cx18-cards.h" | ||
25 | |||
26 | void cx18_video_set_io(struct cx18 *cx) | ||
27 | { | ||
28 | int inp = cx->active_input; | ||
29 | |||
30 | v4l2_subdev_call(cx->sd_av, video, s_routing, | ||
31 | cx->card->video_inputs[inp].video_input, 0, 0); | ||
32 | } | ||
diff --git a/drivers/media/video/cx18/cx18-video.h b/drivers/media/video/cx18/cx18-video.h new file mode 100644 index 00000000000..529006a06e5 --- /dev/null +++ b/drivers/media/video/cx18/cx18-video.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * cx18 video interface functions | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
19 | * 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | void cx18_video_set_io(struct cx18 *cx); | ||
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h new file mode 100644 index 00000000000..767a8d23e3f --- /dev/null +++ b/drivers/media/video/cx18/cx23418.h | |||
@@ -0,0 +1,492 @@ | |||
1 | /* | ||
2 | * cx18 header containing common defines. | ||
3 | * | ||
4 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
19 | * 02111-1307 USA | ||
20 | */ | ||
21 | |||
22 | #ifndef CX23418_H | ||
23 | #define CX23418_H | ||
24 | |||
25 | #include <media/cx2341x.h> | ||
26 | |||
27 | #define MGR_CMD_MASK 0x40000000 | ||
28 | /* The MSB of the command code indicates that this is the completion of a | ||
29 | command */ | ||
30 | #define MGR_CMD_MASK_ACK (MGR_CMD_MASK | 0x80000000) | ||
31 | |||
32 | /* Description: This command creates a new instance of a certain task | ||
33 | IN[0] - Task ID. This is one of the XPU_CMD_MASK_YYY where XPU is | ||
34 | the processor on which the task YYY will be created | ||
35 | OUT[0] - Task handle. This handle is passed along with commands to | ||
36 | dispatch to the right instance of the task | ||
37 | ReturnCode - One of the ERR_SYS_... */ | ||
38 | #define CX18_CREATE_TASK (MGR_CMD_MASK | 0x0001) | ||
39 | |||
40 | /* Description: This command destroys an instance of a task | ||
41 | IN[0] - Task handle. Hanlde of the task to destroy | ||
42 | ReturnCode - One of the ERR_SYS_... */ | ||
43 | #define CX18_DESTROY_TASK (MGR_CMD_MASK | 0x0002) | ||
44 | |||
45 | /* All commands for CPU have the following mask set */ | ||
46 | #define CPU_CMD_MASK 0x20000000 | ||
47 | #define CPU_CMD_MASK_DEBUG (CPU_CMD_MASK | 0x00000000) | ||
48 | #define CPU_CMD_MASK_ACK (CPU_CMD_MASK | 0x80000000) | ||
49 | #define CPU_CMD_MASK_CAPTURE (CPU_CMD_MASK | 0x00020000) | ||
50 | #define CPU_CMD_MASK_TS (CPU_CMD_MASK | 0x00040000) | ||
51 | |||
52 | #define EPU_CMD_MASK 0x02000000 | ||
53 | #define EPU_CMD_MASK_DEBUG (EPU_CMD_MASK | 0x000000) | ||
54 | #define EPU_CMD_MASK_DE (EPU_CMD_MASK | 0x040000) | ||
55 | |||
56 | #define APU_CMD_MASK 0x10000000 | ||
57 | #define APU_CMD_MASK_ACK (APU_CMD_MASK | 0x80000000) | ||
58 | |||
59 | #define CX18_APU_ENCODING_METHOD_MPEG (0 << 28) | ||
60 | #define CX18_APU_ENCODING_METHOD_AC3 (1 << 28) | ||
61 | |||
62 | /* Description: Command APU to start audio | ||
63 | IN[0] - audio parameters (same as CX18_CPU_SET_AUDIO_PARAMETERS?) | ||
64 | IN[1] - caller buffer address, or 0 | ||
65 | ReturnCode - ??? */ | ||
66 | #define CX18_APU_START (APU_CMD_MASK | 0x01) | ||
67 | |||
68 | /* Description: Command APU to stop audio | ||
69 | IN[0] - encoding method to stop | ||
70 | ReturnCode - ??? */ | ||
71 | #define CX18_APU_STOP (APU_CMD_MASK | 0x02) | ||
72 | |||
73 | /* Description: Command APU to reset the AI | ||
74 | ReturnCode - ??? */ | ||
75 | #define CX18_APU_RESETAI (APU_CMD_MASK | 0x05) | ||
76 | |||
77 | /* Description: This command indicates that a Memory Descriptor List has been | ||
78 | filled with the requested channel type | ||
79 | IN[0] - Task handle. Handle of the task | ||
80 | IN[1] - Offset of the MDL_ACK from the beginning of the local DDR. | ||
81 | IN[2] - Number of CNXT_MDL_ACK structures in the array pointed to by IN[1] | ||
82 | ReturnCode - One of the ERR_DE_... */ | ||
83 | #define CX18_EPU_DMA_DONE (EPU_CMD_MASK_DE | 0x0001) | ||
84 | |||
85 | /* Something interesting happened | ||
86 | IN[0] - A value to log | ||
87 | IN[1] - An offset of a string in the MiniMe memory; | ||
88 | 0/zero/NULL means "I have nothing to say" */ | ||
89 | #define CX18_EPU_DEBUG (EPU_CMD_MASK_DEBUG | 0x0003) | ||
90 | |||
91 | /* Reads memory/registers (32-bit) | ||
92 | IN[0] - Address | ||
93 | OUT[1] - Value */ | ||
94 | #define CX18_CPU_DEBUG_PEEK32 (CPU_CMD_MASK_DEBUG | 0x0003) | ||
95 | |||
96 | /* Description: This command starts streaming with the set channel type | ||
97 | IN[0] - Task handle. Handle of the task to start | ||
98 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
99 | #define CX18_CPU_CAPTURE_START (CPU_CMD_MASK_CAPTURE | 0x0002) | ||
100 | |||
101 | /* Description: This command stops streaming with the set channel type | ||
102 | IN[0] - Task handle. Handle of the task to stop | ||
103 | IN[1] - 0 = stop at end of GOP, 1 = stop at end of frame (MPEG only) | ||
104 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
105 | #define CX18_CPU_CAPTURE_STOP (CPU_CMD_MASK_CAPTURE | 0x0003) | ||
106 | |||
107 | /* Description: This command pauses streaming with the set channel type | ||
108 | IN[0] - Task handle. Handle of the task to pause | ||
109 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
110 | #define CX18_CPU_CAPTURE_PAUSE (CPU_CMD_MASK_CAPTURE | 0x0007) | ||
111 | |||
112 | /* Description: This command resumes streaming with the set channel type | ||
113 | IN[0] - Task handle. Handle of the task to resume | ||
114 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
115 | #define CX18_CPU_CAPTURE_RESUME (CPU_CMD_MASK_CAPTURE | 0x0008) | ||
116 | |||
117 | #define CAPTURE_CHANNEL_TYPE_NONE 0 | ||
118 | #define CAPTURE_CHANNEL_TYPE_MPEG 1 | ||
119 | #define CAPTURE_CHANNEL_TYPE_INDEX 2 | ||
120 | #define CAPTURE_CHANNEL_TYPE_YUV 3 | ||
121 | #define CAPTURE_CHANNEL_TYPE_PCM 4 | ||
122 | #define CAPTURE_CHANNEL_TYPE_VBI 5 | ||
123 | #define CAPTURE_CHANNEL_TYPE_SLICED_VBI 6 | ||
124 | #define CAPTURE_CHANNEL_TYPE_TS 7 | ||
125 | #define CAPTURE_CHANNEL_TYPE_MAX 15 | ||
126 | |||
127 | /* Description: This command sets the channel type. This can only be done | ||
128 | when stopped. | ||
129 | IN[0] - Task handle. Handle of the task to start | ||
130 | IN[1] - Channel Type. See Below. | ||
131 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
132 | #define CX18_CPU_SET_CHANNEL_TYPE (CPU_CMD_MASK_CAPTURE + 1) | ||
133 | |||
134 | /* Description: Set stream output type | ||
135 | IN[0] - task handle. Handle of the task to start | ||
136 | IN[1] - type | ||
137 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
138 | #define CX18_CPU_SET_STREAM_OUTPUT_TYPE (CPU_CMD_MASK_CAPTURE | 0x0012) | ||
139 | |||
140 | /* Description: Set video input resolution and frame rate | ||
141 | IN[0] - task handle | ||
142 | IN[1] - reserved | ||
143 | IN[2] - reserved | ||
144 | IN[3] - reserved | ||
145 | IN[4] - reserved | ||
146 | IN[5] - frame rate, 0 - 29.97f/s, 1 - 25f/s | ||
147 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
148 | #define CX18_CPU_SET_VIDEO_IN (CPU_CMD_MASK_CAPTURE | 0x0004) | ||
149 | |||
150 | /* Description: Set video frame rate | ||
151 | IN[0] - task handle. Handle of the task to start | ||
152 | IN[1] - video bit rate mode | ||
153 | IN[2] - video average rate | ||
154 | IN[3] - video peak rate | ||
155 | IN[4] - system mux rate | ||
156 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
157 | #define CX18_CPU_SET_VIDEO_RATE (CPU_CMD_MASK_CAPTURE | 0x0005) | ||
158 | |||
159 | /* Description: Set video output resolution | ||
160 | IN[0] - task handle | ||
161 | IN[1] - horizontal size | ||
162 | IN[2] - vertical size | ||
163 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
164 | #define CX18_CPU_SET_VIDEO_RESOLUTION (CPU_CMD_MASK_CAPTURE | 0x0006) | ||
165 | |||
166 | /* Description: This command set filter parameters | ||
167 | IN[0] - Task handle. Handle of the task | ||
168 | IN[1] - type, 0 - temporal, 1 - spatial, 2 - median | ||
169 | IN[2] - mode, temporal/spatial: 0 - disable, 1 - static, 2 - dynamic | ||
170 | median: 0 = disable, 1 = horizontal, 2 = vertical, | ||
171 | 3 = horizontal/vertical, 4 = diagonal | ||
172 | IN[3] - strength, temporal 0 - 31, spatial 0 - 15 | ||
173 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
174 | #define CX18_CPU_SET_FILTER_PARAM (CPU_CMD_MASK_CAPTURE | 0x0009) | ||
175 | |||
176 | /* Description: This command set spatial filter type | ||
177 | IN[0] - Task handle. | ||
178 | IN[1] - luma type: 0 = disable, 1 = 1D horizontal only, 2 = 1D vertical only, | ||
179 | 3 = 2D H/V separable, 4 = 2D symmetric non-separable | ||
180 | IN[2] - chroma type: 0 - disable, 1 = 1D horizontal | ||
181 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
182 | #define CX18_CPU_SET_SPATIAL_FILTER_TYPE (CPU_CMD_MASK_CAPTURE | 0x000C) | ||
183 | |||
184 | /* Description: This command set coring levels for median filter | ||
185 | IN[0] - Task handle. | ||
186 | IN[1] - luma_high | ||
187 | IN[2] - luma_low | ||
188 | IN[3] - chroma_high | ||
189 | IN[4] - chroma_low | ||
190 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
191 | #define CX18_CPU_SET_MEDIAN_CORING (CPU_CMD_MASK_CAPTURE | 0x000E) | ||
192 | |||
193 | /* Description: This command set the picture type mask for index file | ||
194 | IN[0] - Task handle (ignored by firmware) | ||
195 | IN[1] - 0 = disable index file output | ||
196 | 1 = output I picture | ||
197 | 2 = P picture | ||
198 | 4 = B picture | ||
199 | other = illegal */ | ||
200 | #define CX18_CPU_SET_INDEXTABLE (CPU_CMD_MASK_CAPTURE | 0x0010) | ||
201 | |||
202 | /* Description: Set audio parameters | ||
203 | IN[0] - task handle. Handle of the task to start | ||
204 | IN[1] - audio parameter | ||
205 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
206 | #define CX18_CPU_SET_AUDIO_PARAMETERS (CPU_CMD_MASK_CAPTURE | 0x0011) | ||
207 | |||
208 | /* Description: Set video mute | ||
209 | IN[0] - task handle. Handle of the task to start | ||
210 | IN[1] - bit31-24: muteYvalue | ||
211 | bit23-16: muteUvalue | ||
212 | bit15-8: muteVvalue | ||
213 | bit0: 1:mute, 0: unmute | ||
214 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
215 | #define CX18_CPU_SET_VIDEO_MUTE (CPU_CMD_MASK_CAPTURE | 0x0013) | ||
216 | |||
217 | /* Description: Set audio mute | ||
218 | IN[0] - task handle. Handle of the task to start | ||
219 | IN[1] - mute/unmute | ||
220 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
221 | #define CX18_CPU_SET_AUDIO_MUTE (CPU_CMD_MASK_CAPTURE | 0x0014) | ||
222 | |||
223 | /* Description: Set stream output type | ||
224 | IN[0] - task handle. Handle of the task to start | ||
225 | IN[1] - subType | ||
226 | SET_INITIAL_SCR 1 | ||
227 | SET_QUALITY_MODE 2 | ||
228 | SET_VIM_PROTECT_MODE 3 | ||
229 | SET_PTS_CORRECTION 4 | ||
230 | SET_USB_FLUSH_MODE 5 | ||
231 | SET_MERAQPAR_ENABLE 6 | ||
232 | SET_NAV_PACK_INSERTION 7 | ||
233 | SET_SCENE_CHANGE_ENABLE 8 | ||
234 | IN[2] - parameter 1 | ||
235 | IN[3] - parameter 2 | ||
236 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
237 | #define CX18_CPU_SET_MISC_PARAMETERS (CPU_CMD_MASK_CAPTURE | 0x0015) | ||
238 | |||
239 | /* Description: Set raw VBI parameters | ||
240 | IN[0] - Task handle | ||
241 | IN[1] - No. of input lines per field: | ||
242 | bit[15:0]: field 1, | ||
243 | bit[31:16]: field 2 | ||
244 | IN[2] - No. of input bytes per line | ||
245 | IN[3] - No. of output frames per transfer | ||
246 | IN[4] - start code | ||
247 | IN[5] - stop code | ||
248 | ReturnCode */ | ||
249 | #define CX18_CPU_SET_RAW_VBI_PARAM (CPU_CMD_MASK_CAPTURE | 0x0016) | ||
250 | |||
251 | /* Description: Set capture line No. | ||
252 | IN[0] - task handle. Handle of the task to start | ||
253 | IN[1] - height1 | ||
254 | IN[2] - height2 | ||
255 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
256 | #define CX18_CPU_SET_CAPTURE_LINE_NO (CPU_CMD_MASK_CAPTURE | 0x0017) | ||
257 | |||
258 | /* Description: Set copyright | ||
259 | IN[0] - task handle. Handle of the task to start | ||
260 | IN[1] - copyright | ||
261 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
262 | #define CX18_CPU_SET_COPYRIGHT (CPU_CMD_MASK_CAPTURE | 0x0018) | ||
263 | |||
264 | /* Description: Set audio PID | ||
265 | IN[0] - task handle. Handle of the task to start | ||
266 | IN[1] - PID | ||
267 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
268 | #define CX18_CPU_SET_AUDIO_PID (CPU_CMD_MASK_CAPTURE | 0x0019) | ||
269 | |||
270 | /* Description: Set video PID | ||
271 | IN[0] - task handle. Handle of the task to start | ||
272 | IN[1] - PID | ||
273 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
274 | #define CX18_CPU_SET_VIDEO_PID (CPU_CMD_MASK_CAPTURE | 0x001A) | ||
275 | |||
276 | /* Description: Set Vertical Crop Line | ||
277 | IN[0] - task handle. Handle of the task to start | ||
278 | IN[1] - Line | ||
279 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
280 | #define CX18_CPU_SET_VER_CROP_LINE (CPU_CMD_MASK_CAPTURE | 0x001B) | ||
281 | |||
282 | /* Description: Set COP structure | ||
283 | IN[0] - task handle. Handle of the task to start | ||
284 | IN[1] - M | ||
285 | IN[2] - N | ||
286 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
287 | #define CX18_CPU_SET_GOP_STRUCTURE (CPU_CMD_MASK_CAPTURE | 0x001C) | ||
288 | |||
289 | /* Description: Set Scene Change Detection | ||
290 | IN[0] - task handle. Handle of the task to start | ||
291 | IN[1] - scene change | ||
292 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
293 | #define CX18_CPU_SET_SCENE_CHANGE_DETECTION (CPU_CMD_MASK_CAPTURE | 0x001D) | ||
294 | |||
295 | /* Description: Set Aspect Ratio | ||
296 | IN[0] - task handle. Handle of the task to start | ||
297 | IN[1] - AspectRatio | ||
298 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
299 | #define CX18_CPU_SET_ASPECT_RATIO (CPU_CMD_MASK_CAPTURE | 0x001E) | ||
300 | |||
301 | /* Description: Set Skip Input Frame | ||
302 | IN[0] - task handle. Handle of the task to start | ||
303 | IN[1] - skip input frames | ||
304 | ReturnCode - One of the ERR_CAPTURE_... */ | ||
305 | #define CX18_CPU_SET_SKIP_INPUT_FRAME (CPU_CMD_MASK_CAPTURE | 0x001F) | ||
306 | |||
307 | /* Description: Set sliced VBI parameters - | ||
308 | Note This API will only apply to MPEG and Sliced VBI Channels | ||
309 | IN[0] - Task handle | ||
310 | IN[1] - output type, 0 - CC, 1 - Moji, 2 - Teletext | ||
311 | IN[2] - start / stop line | ||
312 | bit[15:0] start line number | ||
313 | bit[31:16] stop line number | ||
314 | IN[3] - number of output frames per interrupt | ||
315 | IN[4] - VBI insertion mode | ||
316 | bit 0: output user data, 1 - enable | ||
317 | bit 1: output private stream, 1 - enable | ||
318 | bit 2: mux option, 0 - in GOP, 1 - in picture | ||
319 | bit[7:0] private stream ID | ||
320 | IN[5] - insertion period while mux option is in picture | ||
321 | ReturnCode - VBI data offset */ | ||
322 | #define CX18_CPU_SET_SLICED_VBI_PARAM (CPU_CMD_MASK_CAPTURE | 0x0020) | ||
323 | |||
324 | /* Description: Set the user data place holder | ||
325 | IN[0] - type of data (0 for user) | ||
326 | IN[1] - Stuffing period | ||
327 | IN[2] - ID data size in word (less than 10) | ||
328 | IN[3] - Pointer to ID buffer */ | ||
329 | #define CX18_CPU_SET_USERDATA_PLACE_HOLDER (CPU_CMD_MASK_CAPTURE | 0x0021) | ||
330 | |||
331 | |||
332 | /* Description: | ||
333 | In[0] Task Handle | ||
334 | return parameter: | ||
335 | Out[0] Reserved | ||
336 | Out[1] Video PTS bit[32:2] of last output video frame. | ||
337 | Out[2] Video PTS bit[ 1:0] of last output video frame. | ||
338 | Out[3] Hardware Video PTS counter bit[31:0], | ||
339 | these bits get incremented on every 90kHz clock tick. | ||
340 | Out[4] Hardware Video PTS counter bit32, | ||
341 | these bits get incremented on every 90kHz clock tick. | ||
342 | ReturnCode */ | ||
343 | #define CX18_CPU_GET_ENC_PTS (CPU_CMD_MASK_CAPTURE | 0x0022) | ||
344 | |||
345 | /* Description: Set VFC parameters | ||
346 | IN[0] - task handle | ||
347 | IN[1] - VFC enable flag, 1 - enable, 0 - disable | ||
348 | */ | ||
349 | #define CX18_CPU_SET_VFC_PARAM (CPU_CMD_MASK_CAPTURE | 0x0023) | ||
350 | |||
351 | /* Below is the list of commands related to the data exchange */ | ||
352 | #define CPU_CMD_MASK_DE (CPU_CMD_MASK | 0x040000) | ||
353 | |||
354 | /* Description: This command provides the physical base address of the local | ||
355 | DDR as viewed by EPU | ||
356 | IN[0] - Physical offset where EPU has the local DDR mapped | ||
357 | ReturnCode - One of the ERR_DE_... */ | ||
358 | #define CPU_CMD_DE_SetBase (CPU_CMD_MASK_DE | 0x0001) | ||
359 | |||
360 | /* Description: This command provides the offsets in the device memory where | ||
361 | the 2 cx18_mdl_ack blocks reside | ||
362 | IN[0] - Task handle. Handle of the task to start | ||
363 | IN[1] - Offset of the first cx18_mdl_ack from the beginning of the | ||
364 | local DDR. | ||
365 | IN[2] - Offset of the second cx18_mdl_ack from the beginning of the | ||
366 | local DDR. | ||
367 | ReturnCode - One of the ERR_DE_... */ | ||
368 | #define CX18_CPU_DE_SET_MDL_ACK (CPU_CMD_MASK_DE | 0x0002) | ||
369 | |||
370 | /* Description: This command provides the offset to a Memory Descriptor List | ||
371 | IN[0] - Task handle. Handle of the task to start | ||
372 | IN[1] - Offset of the MDL from the beginning of the local DDR. | ||
373 | IN[2] - Number of cx18_mdl_ent structures in the array pointed to by IN[1] | ||
374 | IN[3] - Buffer ID | ||
375 | IN[4] - Total buffer length | ||
376 | ReturnCode - One of the ERR_DE_... */ | ||
377 | #define CX18_CPU_DE_SET_MDL (CPU_CMD_MASK_DE | 0x0005) | ||
378 | |||
379 | /* Description: This command requests return of all current Memory | ||
380 | Descriptor Lists to the driver | ||
381 | IN[0] - Task handle. Handle of the task to start | ||
382 | ReturnCode - One of the ERR_DE_... */ | ||
383 | #define CX18_CPU_DE_RELEASE_MDL (CPU_CMD_MASK_DE | 0x0006) | ||
384 | |||
385 | /* Description: This command signals the cpu that the dat buffer has been | ||
386 | consumed and ready for re-use. | ||
387 | IN[0] - Task handle. Handle of the task | ||
388 | IN[1] - Offset of the data block from the beginning of the local DDR. | ||
389 | IN[2] - Number of bytes in the data block | ||
390 | ReturnCode - One of the ERR_DE_... */ | ||
391 | /* #define CX18_CPU_DE_RELEASE_BUFFER (CPU_CMD_MASK_DE | 0x0007) */ | ||
392 | |||
393 | /* No Error / Success */ | ||
394 | #define CNXT_OK 0x000000 | ||
395 | |||
396 | /* Received unknown command */ | ||
397 | #define CXERR_UNK_CMD 0x000001 | ||
398 | |||
399 | /* First parameter in the command is invalid */ | ||
400 | #define CXERR_INVALID_PARAM1 0x000002 | ||
401 | |||
402 | /* Second parameter in the command is invalid */ | ||
403 | #define CXERR_INVALID_PARAM2 0x000003 | ||
404 | |||
405 | /* Device interface is not open/found */ | ||
406 | #define CXERR_DEV_NOT_FOUND 0x000004 | ||
407 | |||
408 | /* Requested function is not implemented/available */ | ||
409 | #define CXERR_NOTSUPPORTED 0x000005 | ||
410 | |||
411 | /* Invalid pointer is provided */ | ||
412 | #define CXERR_BADPTR 0x000006 | ||
413 | |||
414 | /* Unable to allocate memory */ | ||
415 | #define CXERR_NOMEM 0x000007 | ||
416 | |||
417 | /* Object/Link not found */ | ||
418 | #define CXERR_LINK 0x000008 | ||
419 | |||
420 | /* Device busy, command cannot be executed */ | ||
421 | #define CXERR_BUSY 0x000009 | ||
422 | |||
423 | /* File/device/handle is not open. */ | ||
424 | #define CXERR_NOT_OPEN 0x00000A | ||
425 | |||
426 | /* Value is out of range */ | ||
427 | #define CXERR_OUTOFRANGE 0x00000B | ||
428 | |||
429 | /* Buffer overflow */ | ||
430 | #define CXERR_OVERFLOW 0x00000C | ||
431 | |||
432 | /* Version mismatch */ | ||
433 | #define CXERR_BADVER 0x00000D | ||
434 | |||
435 | /* Operation timed out */ | ||
436 | #define CXERR_TIMEOUT 0x00000E | ||
437 | |||
438 | /* Operation aborted */ | ||
439 | #define CXERR_ABORT 0x00000F | ||
440 | |||
441 | /* Specified I2C device not found for read/write */ | ||
442 | #define CXERR_I2CDEV_NOTFOUND 0x000010 | ||
443 | |||
444 | /* Error in I2C data xfer (but I2C device is present) */ | ||
445 | #define CXERR_I2CDEV_XFERERR 0x000011 | ||
446 | |||
447 | /* Chanel changing component not ready */ | ||
448 | #define CXERR_CHANNELNOTREADY 0x000012 | ||
449 | |||
450 | /* PPU (Presensation/Decoder) mail box is corrupted */ | ||
451 | #define CXERR_PPU_MB_CORRUPT 0x000013 | ||
452 | |||
453 | /* CPU (Capture/Encoder) mail box is corrupted */ | ||
454 | #define CXERR_CPU_MB_CORRUPT 0x000014 | ||
455 | |||
456 | /* APU (Audio) mail box is corrupted */ | ||
457 | #define CXERR_APU_MB_CORRUPT 0x000015 | ||
458 | |||
459 | /* Unable to open file for reading */ | ||
460 | #define CXERR_FILE_OPEN_READ 0x000016 | ||
461 | |||
462 | /* Unable to open file for writing */ | ||
463 | #define CXERR_FILE_OPEN_WRITE 0x000017 | ||
464 | |||
465 | /* Unable to find the I2C section specified */ | ||
466 | #define CXERR_I2C_BADSECTION 0x000018 | ||
467 | |||
468 | /* Error in I2C data xfer (but I2C device is present) */ | ||
469 | #define CXERR_I2CDEV_DATALOW 0x000019 | ||
470 | |||
471 | /* Error in I2C data xfer (but I2C device is present) */ | ||
472 | #define CXERR_I2CDEV_CLOCKLOW 0x00001A | ||
473 | |||
474 | /* No Interrupt received from HW (for I2C access) */ | ||
475 | #define CXERR_NO_HW_I2C_INTR 0x00001B | ||
476 | |||
477 | /* RPU is not ready to accept commands! */ | ||
478 | #define CXERR_RPU_NOT_READY 0x00001C | ||
479 | |||
480 | /* RPU is not ready to accept commands! */ | ||
481 | #define CXERR_RPU_NO_ACK 0x00001D | ||
482 | |||
483 | /* The are no buffers ready. Try again soon! */ | ||
484 | #define CXERR_NODATA_AGAIN 0x00001E | ||
485 | |||
486 | /* The stream is stopping. Function not allowed now! */ | ||
487 | #define CXERR_STOPPING_STATUS 0x00001F | ||
488 | |||
489 | /* Trying to access hardware when the power is turned OFF */ | ||
490 | #define CXERR_DEVPOWER_OFF 0x000020 | ||
491 | |||
492 | #endif /* CX23418_H */ | ||