aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/cx18')
-rw-r--r--drivers/media/video/cx18/Kconfig11
-rw-r--r--drivers/media/video/cx18/Makefile2
-rw-r--r--drivers/media/video/cx18/cx18-alsa-main.c293
-rw-r--r--drivers/media/video/cx18/cx18-alsa-mixer.c175
-rw-r--r--drivers/media/video/cx18/cx18-alsa-mixer.h23
-rw-r--r--drivers/media/video/cx18/cx18-alsa-pcm.c354
-rw-r--r--drivers/media/video/cx18/cx18-alsa-pcm.h27
-rw-r--r--drivers/media/video/cx18/cx18-alsa.h75
-rw-r--r--drivers/media/video/cx18/cx18-cards.c2
-rw-r--r--drivers/media/video/cx18/cx18-driver.c70
-rw-r--r--drivers/media/video/cx18/cx18-driver.h50
-rw-r--r--drivers/media/video/cx18/cx18-dvb.c22
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c205
-rw-r--r--drivers/media/video/cx18/cx18-fileops.h3
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c135
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c45
-rw-r--r--drivers/media/video/cx18/cx18-queue.c3
-rw-r--r--drivers/media/video/cx18/cx18-streams.c72
-rw-r--r--drivers/media/video/cx18/cx18-streams.h10
-rw-r--r--drivers/media/video/cx18/cx18-version.h2
-rw-r--r--drivers/media/video/cx18/cx23418.h3
21 files changed, 1449 insertions, 133 deletions
diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
index e8a50a611ebc..baf7e91ee0f5 100644
--- a/drivers/media/video/cx18/Kconfig
+++ b/drivers/media/video/cx18/Kconfig
@@ -19,3 +19,14 @@ config VIDEO_CX18
19 19
20 To compile this driver as a module, choose M here: the 20 To compile this driver as a module, choose M here: the
21 module will be called cx18. 21 module will be called cx18.
22
23config VIDEO_CX18_ALSA
24 tristate "Conexant 23418 DMA audio support"
25 depends on VIDEO_CX18 && SND && EXPERIMENTAL
26 select SND_PCM
27 ---help---
28 This is a video4linux driver for direct (DMA) audio on
29 Conexant 23418 based TV cards using ALSA.
30
31 To compile this driver as a module, choose M here: the
32 module will be called cx18-alsa.
diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile
index f7bf0edf93f9..2fadd9ded340 100644
--- a/drivers/media/video/cx18/Makefile
+++ b/drivers/media/video/cx18/Makefile
@@ -3,8 +3,10 @@ cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.
3 cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.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 \ 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 5 cx18-dvb.o cx18-io.o
6cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o
6 7
7obj-$(CONFIG_VIDEO_CX18) += cx18.o 8obj-$(CONFIG_VIDEO_CX18) += cx18.o
9obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o
8 10
9EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core 11EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
10EXTRA_CFLAGS += -Idrivers/media/dvb/frontends 12EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
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 000000000000..eb41d7ec65b9
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-main.c
@@ -0,0 +1,293 @@
1/*
2 * ALSA interface to cx18 PCM capture streams
3 *
4 * Copyright (C) 2009 Andy Walls <awalls@radix.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/module.h>
27#include <linux/kernel.h>
28#include <linux/device.h>
29#include <linux/spinlock.h>
30
31#include <media/v4l2-device.h>
32
33#include <sound/core.h>
34#include <sound/initval.h>
35
36#include "cx18-driver.h"
37#include "cx18-version.h"
38#include "cx18-alsa.h"
39#include "cx18-alsa-mixer.h"
40#include "cx18-alsa-pcm.h"
41
42int cx18_alsa_debug;
43
44#define CX18_DEBUG_ALSA_INFO(fmt, arg...) \
45 do { \
46 if (cx18_alsa_debug & 2) \
47 printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg); \
48 } while (0);
49
50module_param_named(debug, cx18_alsa_debug, int, 0644);
51MODULE_PARM_DESC(debug,
52 "Debug level (bitmask). Default: 0\n"
53 "\t\t\t 1/0x0001: warning\n"
54 "\t\t\t 2/0x0002: info\n");
55
56MODULE_AUTHOR("Andy Walls");
57MODULE_DESCRIPTION("CX23418 ALSA Interface");
58MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
59MODULE_LICENSE("GPL");
60
61MODULE_VERSION(CX18_VERSION);
62
63static inline
64struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev)
65{
66 return to_cx18(v4l2_dev)->alsa;
67}
68
69static inline
70struct snd_cx18_card *p_to_snd_cx18_card(struct v4l2_device **v4l2_dev)
71{
72 return container_of(v4l2_dev, struct snd_cx18_card, v4l2_dev);
73}
74
75static void snd_cx18_card_free(struct snd_cx18_card *cxsc)
76{
77 if (cxsc == NULL)
78 return;
79
80 if (cxsc->v4l2_dev != NULL)
81 to_cx18(cxsc->v4l2_dev)->alsa = NULL;
82
83 /* FIXME - take any other stopping actions needed */
84
85 kfree(cxsc);
86}
87
88static void snd_cx18_card_private_free(struct snd_card *sc)
89{
90 if (sc == NULL)
91 return;
92 snd_cx18_card_free(sc->private_data);
93 sc->private_data = NULL;
94 sc->private_free = NULL;
95}
96
97static int snd_cx18_card_create(struct v4l2_device *v4l2_dev,
98 struct snd_card *sc,
99 struct snd_cx18_card **cxsc)
100{
101 *cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL);
102 if (*cxsc == NULL)
103 return -ENOMEM;
104
105 (*cxsc)->v4l2_dev = v4l2_dev;
106 (*cxsc)->sc = sc;
107
108 sc->private_data = *cxsc;
109 sc->private_free = snd_cx18_card_private_free;
110
111 return 0;
112}
113
114static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc)
115{
116 struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
117 struct snd_card *sc = cxsc->sc;
118
119 /* sc->driver is used by alsa-lib's configurator: simple, unique */
120 strlcpy(sc->driver, "CX23418", sizeof(sc->driver));
121
122 /* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */
123 snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d",
124 cx->instance);
125
126 /* sc->longname is read from /proc/asound/cards */
127 snprintf(sc->longname, sizeof(sc->longname),
128 "CX23418 #%d %s TV/FM Radio/Line-In Capture",
129 cx->instance, cx->card_name);
130
131 return 0;
132}
133
134static int snd_cx18_init(struct v4l2_device *v4l2_dev)
135{
136 struct cx18 *cx = to_cx18(v4l2_dev);
137 struct snd_card *sc = NULL;
138 struct snd_cx18_card *cxsc;
139 int ret;
140
141 /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */
142
143 /* (1) Check and increment the device index */
144 /* This is a no-op for us. We'll use the cx->instance */
145
146 /* (2) Create a card instance */
147 ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */
148 SNDRV_DEFAULT_STR1, /* xid from end of shortname*/
149 THIS_MODULE, 0, &sc);
150 if (ret) {
151 CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n",
152 __func__, ret);
153 goto err_exit;
154 }
155
156 /* (3) Create a main component */
157 ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc);
158 if (ret) {
159 CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n",
160 __func__, ret);
161 goto err_exit_free;
162 }
163
164 /* (4) Set the driver ID and name strings */
165 snd_cx18_card_set_names(cxsc);
166
167
168 ret = snd_cx18_pcm_create(cxsc);
169 if (ret) {
170 CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
171 __func__, ret);
172 goto err_exit_free;
173 }
174 /* FIXME - proc files */
175
176 /* (7) Set the driver data and return 0 */
177 /* We do this out of normal order for PCI drivers to avoid races */
178 cx->alsa = cxsc;
179
180 /* (6) Register the card instance */
181 ret = snd_card_register(sc);
182 if (ret) {
183 cx->alsa = NULL;
184 CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n",
185 __func__, ret);
186 goto err_exit_free;
187 }
188
189 return 0;
190
191err_exit_free:
192 if (sc != NULL)
193 snd_card_free(sc);
194err_exit:
195 return ret;
196}
197
198int cx18_alsa_load(struct cx18 *cx)
199{
200 struct v4l2_device *v4l2_dev = &cx->v4l2_dev;
201 struct cx18_stream *s;
202
203 if (v4l2_dev == NULL) {
204 printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",
205 __func__);
206 return 0;
207 }
208
209 cx = to_cx18(v4l2_dev);
210 if (cx == NULL) {
211 printk(KERN_ERR "cx18-alsa cx is NULL\n");
212 return 0;
213 }
214
215 s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
216 if (s->video_dev == NULL) {
217 CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - "
218 "skipping\n", __func__);
219 return 0;
220 }
221
222 if (cx->alsa != NULL) {
223 CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n",
224 __func__);
225 return 0;
226 }
227
228 if (snd_cx18_init(v4l2_dev)) {
229 CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n",
230 __func__);
231 } else {
232 CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance "
233 "\n", __func__);
234 }
235 return 0;
236}
237
238static int __init cx18_alsa_init(void)
239{
240 printk(KERN_INFO "cx18-alsa: module loading...\n");
241 cx18_ext_init = &cx18_alsa_load;
242 return 0;
243}
244
245static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc)
246{
247 struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
248
249 /* FIXME - pointer checks & shutdown cxsc */
250
251 snd_card_free(cxsc->sc);
252 cx->alsa = NULL;
253}
254
255static int __exit cx18_alsa_exit_callback(struct device *dev, void *data)
256{
257 struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
258 struct snd_cx18_card *cxsc;
259
260 if (v4l2_dev == NULL) {
261 printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",
262 __func__);
263 return 0;
264 }
265
266 cxsc = to_snd_cx18_card(v4l2_dev);
267 if (cxsc == NULL) {
268 CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n",
269 __func__);
270 return 0;
271 }
272
273 snd_cx18_exit(cxsc);
274 return 0;
275}
276
277static void __exit cx18_alsa_exit(void)
278{
279 struct device_driver *drv;
280 int ret;
281
282 printk(KERN_INFO "cx18-alsa: module unloading...\n");
283
284 drv = driver_find("cx18", &pci_bus_type);
285 ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback);
286 put_driver(drv);
287
288 cx18_ext_init = NULL;
289 printk(KERN_INFO "cx18-alsa: module unload complete\n");
290}
291
292module_init(cx18_alsa_init);
293module_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 000000000000..ef21114309fe
--- /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@radix.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 */
54static 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
63static 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
72static 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
84static 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
104static 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 */
136static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0);
137
138static 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
161int __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 000000000000..2d418db000fe
--- /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@radix.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
23int __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 000000000000..2bd312daeb1e
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa-pcm.c
@@ -0,0 +1,354 @@
1/*
2 * ALSA PCM device for the
3 * ALSA interface to cx18 PCM capture streams
4 *
5 * Copyright (C) 2009 Andy Walls <awalls@radix.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
41static unsigned int pcm_debug;
42module_param(pcm_debug, int, 0644);
43MODULE_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
51static 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
72void 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
148static 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
196static 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
218static int snd_cx18_pcm_ioctl(struct snd_pcm_substream *substream,
219 unsigned int cmd, void *arg)
220{
221 return snd_pcm_lib_ioctl(substream, cmd, arg);
222}
223
224
225static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
226 size_t size)
227{
228 struct snd_pcm_runtime *runtime = subs->runtime;
229
230 dprintk("Allocating vbuffer\n");
231 if (runtime->dma_area) {
232 if (runtime->dma_bytes > size)
233 return 0;
234
235 vfree(runtime->dma_area);
236 }
237 runtime->dma_area = vmalloc(size);
238 if (!runtime->dma_area)
239 return -ENOMEM;
240
241 runtime->dma_bytes = size;
242
243 return 0;
244}
245
246static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream,
247 struct snd_pcm_hw_params *params)
248{
249 int ret;
250
251 dprintk("%s called\n", __func__);
252
253 ret = snd_pcm_alloc_vmalloc_buffer(substream,
254 params_buffer_bytes(params));
255 return 0;
256}
257
258static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream)
259{
260 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
261 unsigned long flags;
262
263 spin_lock_irqsave(&cxsc->slock, flags);
264 if (substream->runtime->dma_area) {
265 dprintk("freeing pcm capture region\n");
266 vfree(substream->runtime->dma_area);
267 substream->runtime->dma_area = NULL;
268 }
269 spin_unlock_irqrestore(&cxsc->slock, flags);
270
271 return 0;
272}
273
274static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream)
275{
276 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
277
278 cxsc->hwptr_done_capture = 0;
279 cxsc->capture_transfer_done = 0;
280
281 return 0;
282}
283
284static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
285{
286 return 0;
287}
288
289static
290snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream)
291{
292 unsigned long flags;
293 snd_pcm_uframes_t hwptr_done;
294 struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream);
295
296 spin_lock_irqsave(&cxsc->slock, flags);
297 hwptr_done = cxsc->hwptr_done_capture;
298 spin_unlock_irqrestore(&cxsc->slock, flags);
299
300 return hwptr_done;
301}
302
303static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
304 unsigned long offset)
305{
306 void *pageptr = subs->runtime->dma_area + offset;
307
308 return vmalloc_to_page(pageptr);
309}
310
311static struct snd_pcm_ops snd_cx18_pcm_capture_ops = {
312 .open = snd_cx18_pcm_capture_open,
313 .close = snd_cx18_pcm_capture_close,
314 .ioctl = snd_cx18_pcm_ioctl,
315 .hw_params = snd_cx18_pcm_hw_params,
316 .hw_free = snd_cx18_pcm_hw_free,
317 .prepare = snd_cx18_pcm_prepare,
318 .trigger = snd_cx18_pcm_trigger,
319 .pointer = snd_cx18_pcm_pointer,
320 .page = snd_pcm_get_vmalloc_page,
321};
322
323int snd_cx18_pcm_create(struct snd_cx18_card *cxsc)
324{
325 struct snd_pcm *sp;
326 struct snd_card *sc = cxsc->sc;
327 struct v4l2_device *v4l2_dev = cxsc->v4l2_dev;
328 struct cx18 *cx = to_cx18(v4l2_dev);
329 int ret;
330
331 ret = snd_pcm_new(sc, "CX23418 PCM",
332 0, /* PCM device 0, the only one for this card */
333 0, /* 0 playback substreams */
334 1, /* 1 capture substream */
335 &sp);
336 if (ret) {
337 CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
338 __func__, ret);
339 goto err_exit;
340 }
341
342 spin_lock_init(&cxsc->slock);
343
344 snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
345 &snd_cx18_pcm_capture_ops);
346 sp->info_flags = 0;
347 sp->private_data = cxsc;
348 strlcpy(sp->name, cx->card_name, sizeof(sp->name));
349
350 return 0;
351
352err_exit:
353 return ret;
354}
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 000000000000..325662c647a0
--- /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@radix.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
23int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc);
24
25/* Used by cx18-mailbox to announce the PCM data to the module */
26void 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 000000000000..88a1cde7540b
--- /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@radix.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
22struct snd_card;
23
24struct 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
33extern 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 */
39static 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
45static 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-cards.c b/drivers/media/video/cx18/cx18-cards.c
index f11e47a58286..f808fb6fc1c1 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -393,7 +393,7 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = {
393 .gpio_init.direction = 0x7, 393 .gpio_init.direction = 0x7,
394 .gpio_audio_input = { .mask = 0x7, 394 .gpio_audio_input = { .mask = 0x7,
395 .tuner = 0x6, .linein = 0x2, .radio = 0x2 }, 395 .tuner = 0x6, .linein = 0x2, .radio = 0x2 },
396 .xceive_pin = 15, 396 .xceive_pin = 1,
397 .pci_list = cx18_pci_leadtek_pvr2100, 397 .pci_list = cx18_pci_leadtek_pvr2100,
398 .i2c = &cx18_i2c_std, 398 .i2c = &cx18_i2c_std,
399}; 399};
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 7f65a47f12e1..c95a86ba33b0 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -47,6 +47,10 @@
47 setting this to 1 you ensure that radio0 is now also radio1. */ 47 setting this to 1 you ensure that radio0 is now also radio1. */
48int cx18_first_minor; 48int cx18_first_minor;
49 49
50/* Callback for registering extensions */
51int (*cx18_ext_init)(struct cx18 *);
52EXPORT_SYMBOL(cx18_ext_init);
53
50/* add your revision and whatnot here */ 54/* add your revision and whatnot here */
51static struct pci_device_id cx18_pci_tbl[] __devinitdata = { 55static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
52 {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418, 56 {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
@@ -91,7 +95,7 @@ static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
91 95
92static int enc_ts_bufs = -1; 96static int enc_ts_bufs = -1;
93static int enc_mpg_bufs = -1; 97static int enc_mpg_bufs = -1;
94static int enc_idx_bufs = -1; 98static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM;
95static int enc_yuv_bufs = -1; 99static int enc_yuv_bufs = -1;
96static int enc_vbi_bufs = -1; 100static int enc_vbi_bufs = -1;
97static int enc_pcm_bufs = -1; 101static int enc_pcm_bufs = -1;
@@ -196,14 +200,17 @@ MODULE_PARM_DESC(enc_mpg_bufs,
196 "Number of encoder MPG buffers\n" 200 "Number of encoder MPG buffers\n"
197 "\t\t\tDefault is computed from other enc_mpg_* parameters"); 201 "\t\t\tDefault is computed from other enc_mpg_* parameters");
198MODULE_PARM_DESC(enc_idx_buffers, 202MODULE_PARM_DESC(enc_idx_buffers,
199 "Encoder IDX buffer memory (MB). (enc_idx_bufs can override)\n" 203 "(Deprecated) Encoder IDX buffer memory (MB)\n"
200 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFFERS)); 204 "\t\t\tIgnored, except 0 disables IDX buffer allocations\n"
205 "\t\t\tDefault: 1 [Enabled]");
201MODULE_PARM_DESC(enc_idx_bufsize, 206MODULE_PARM_DESC(enc_idx_bufsize,
202 "Size of an encoder IDX buffer (kB)\n" 207 "Size of an encoder IDX buffer (kB)\n"
203 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFSIZE)); 208 "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n"
209 "\t\t\t(multiples of size required for 64 index entries)\n"
210 "\t\t\tDefault: 2");
204MODULE_PARM_DESC(enc_idx_bufs, 211MODULE_PARM_DESC(enc_idx_bufs,
205 "Number of encoder IDX buffers\n" 212 "Number of encoder IDX buffers\n"
206 "\t\t\tDefault is computed from other enc_idx_* parameters"); 213 "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM));
207MODULE_PARM_DESC(enc_yuv_buffers, 214MODULE_PARM_DESC(enc_yuv_buffers,
208 "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n" 215 "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
209 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); 216 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
@@ -231,7 +238,8 @@ MODULE_PARM_DESC(enc_pcm_bufs,
231 "Number of encoder PCM buffers\n" 238 "Number of encoder PCM buffers\n"
232 "\t\t\tDefault is computed from other enc_pcm_* parameters"); 239 "\t\t\tDefault is computed from other enc_pcm_* parameters");
233 240
234MODULE_PARM_DESC(cx18_first_minor, "Set device node number assigned to first card"); 241MODULE_PARM_DESC(cx18_first_minor,
242 "Set device node number assigned to first card");
235 243
236MODULE_AUTHOR("Hans Verkuil"); 244MODULE_AUTHOR("Hans Verkuil");
237MODULE_DESCRIPTION("CX23418 driver"); 245MODULE_DESCRIPTION("CX23418 driver");
@@ -240,6 +248,28 @@ MODULE_LICENSE("GPL");
240 248
241MODULE_VERSION(CX18_VERSION); 249MODULE_VERSION(CX18_VERSION);
242 250
251#if defined(CONFIG_MODULES) && defined(MODULE)
252static void request_module_async(struct work_struct *work)
253{
254 struct cx18 *dev = container_of(work, struct cx18, request_module_wk);
255
256 /* Make sure cx18-alsa module is loaded */
257 request_module("cx18-alsa");
258
259 /* Initialize cx18-alsa for this instance of the cx18 device */
260 if (cx18_ext_init != NULL)
261 cx18_ext_init(dev);
262}
263
264static void request_modules(struct cx18 *dev)
265{
266 INIT_WORK(&dev->request_module_wk, request_module_async);
267 schedule_work(&dev->request_module_wk);
268}
269#else
270#define request_modules(dev)
271#endif /* CONFIG_MODULES */
272
243/* Generic utility functions */ 273/* Generic utility functions */
244int cx18_msleep_timeout(unsigned int msecs, int intr) 274int cx18_msleep_timeout(unsigned int msecs, int intr)
245{ 275{
@@ -501,7 +531,12 @@ static void cx18_process_options(struct cx18 *cx)
501 /* 531 /*
502 * YUV is a special case where the stream_buf_size needs to be 532 * YUV is a special case where the stream_buf_size needs to be
503 * an integral multiple of 33.75 kB (storage for 32 screens 533 * an integral multiple of 33.75 kB (storage for 32 screens
504 * lines to maintain alignment in case of lost buffers 534 * lines to maintain alignment in case of lost buffers).
535 *
536 * IDX is a special case where the stream_buf_size should be
537 * an integral multiple of 1.5 kB (storage for 64 index entries
538 * to maintain alignment in case of lost buffers).
539 *
505 */ 540 */
506 if (i == CX18_ENC_STREAM_TYPE_YUV) { 541 if (i == CX18_ENC_STREAM_TYPE_YUV) {
507 cx->stream_buf_size[i] *= 1024; 542 cx->stream_buf_size[i] *= 1024;
@@ -511,15 +546,24 @@ static void cx18_process_options(struct cx18 *cx)
511 if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE) 546 if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
512 cx->stream_buf_size[i] = 547 cx->stream_buf_size[i] =
513 CX18_UNIT_ENC_YUV_BUFSIZE; 548 CX18_UNIT_ENC_YUV_BUFSIZE;
549 } else if (i == CX18_ENC_STREAM_TYPE_IDX) {
550 cx->stream_buf_size[i] *= 1024;
551 cx->stream_buf_size[i] -=
552 (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE);
553
554 if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE)
555 cx->stream_buf_size[i] =
556 CX18_UNIT_ENC_IDX_BUFSIZE;
514 } 557 }
515 /* 558 /*
516 * YUV is a special case where the stream_buf_size is 559 * YUV and IDX are special cases where the stream_buf_size is
517 * now in bytes. 560 * now in bytes.
518 * VBI is a special case where the stream_buf_size is fixed 561 * VBI is a special case where the stream_buf_size is fixed
519 * and already in bytes 562 * and already in bytes
520 */ 563 */
521 if (i == CX18_ENC_STREAM_TYPE_VBI || 564 if (i == CX18_ENC_STREAM_TYPE_VBI ||
522 i == CX18_ENC_STREAM_TYPE_YUV) { 565 i == CX18_ENC_STREAM_TYPE_YUV ||
566 i == CX18_ENC_STREAM_TYPE_IDX) {
523 if (cx->stream_buffers[i] < 0) { 567 if (cx->stream_buffers[i] < 0) {
524 cx->stream_buffers[i] = 568 cx->stream_buffers[i] =
525 cx->options.megabytes[i] * 1024 * 1024 569 cx->options.megabytes[i] * 1024 * 1024
@@ -1032,6 +1076,10 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev,
1032 } 1076 }
1033 1077
1034 CX18_INFO("Initialized card: %s\n", cx->card_name); 1078 CX18_INFO("Initialized card: %s\n", cx->card_name);
1079
1080 /* Load cx18 submodules (cx18-alsa) */
1081 request_modules(cx);
1082
1035 return 0; 1083 return 0;
1036 1084
1037free_streams: 1085free_streams:
@@ -1220,6 +1268,7 @@ static void cx18_remove(struct pci_dev *pci_dev)
1220 kfree(cx); 1268 kfree(cx);
1221} 1269}
1222 1270
1271
1223/* define a pci_driver for card detection */ 1272/* define a pci_driver for card detection */
1224static struct pci_driver cx18_pci_driver = { 1273static struct pci_driver cx18_pci_driver = {
1225 .name = "cx18", 1274 .name = "cx18",
@@ -1230,7 +1279,8 @@ static struct pci_driver cx18_pci_driver = {
1230 1279
1231static int __init module_start(void) 1280static int __init module_start(void)
1232{ 1281{
1233 printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION); 1282 printk(KERN_INFO "cx18: Start initialization, version %s\n",
1283 CX18_VERSION);
1234 1284
1235 /* Validate parameters */ 1285 /* Validate parameters */
1236 if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) { 1286 if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index e3f7911a7385..23ad6d548dc5 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -126,10 +126,22 @@
126#define CX18_625_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 576/32) 126#define CX18_625_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 576/32)
127#define CX18_525_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 480/32) 127#define CX18_525_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 480/32)
128 128
129/* IDX buffer size should be a multiple of the index entry size from the chip */
130struct cx18_enc_idx_entry {
131 __le32 length;
132 __le32 offset_low;
133 __le32 offset_high;
134 __le32 flags;
135 __le32 pts_low;
136 __le32 pts_high;
137} __attribute__ ((packed));
138#define CX18_UNIT_ENC_IDX_BUFSIZE \
139 (sizeof(struct cx18_enc_idx_entry) * V4L2_ENC_IDX_ENTRIES)
140
129/* DMA buffer, default size in kB allocated */ 141/* DMA buffer, default size in kB allocated */
130#define CX18_DEFAULT_ENC_TS_BUFSIZE 32 142#define CX18_DEFAULT_ENC_TS_BUFSIZE 32
131#define CX18_DEFAULT_ENC_MPG_BUFSIZE 32 143#define CX18_DEFAULT_ENC_MPG_BUFSIZE 32
132#define CX18_DEFAULT_ENC_IDX_BUFSIZE 32 144#define CX18_DEFAULT_ENC_IDX_BUFSIZE (CX18_UNIT_ENC_IDX_BUFSIZE * 1 / 1024 + 1)
133#define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1) 145#define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1)
134#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4 146#define CX18_DEFAULT_ENC_PCM_BUFSIZE 4
135 147
@@ -234,16 +246,8 @@
234#define CX18_WARN_DEV(dev, fmt, args...) v4l2_warn(dev, fmt , ## args) 246#define CX18_WARN_DEV(dev, fmt, args...) v4l2_warn(dev, fmt , ## args)
235#define CX18_INFO_DEV(dev, fmt, args...) v4l2_info(dev, fmt , ## args) 247#define CX18_INFO_DEV(dev, fmt, args...) v4l2_info(dev, fmt , ## args)
236 248
237/* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
238#define MPEG_FRAME_TYPE_IFRAME 1
239#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3
240#define MPEG_FRAME_TYPE_ALL 7
241
242#define CX18_MAX_PGM_INDEX (400)
243
244extern int cx18_debug; 249extern int cx18_debug;
245 250
246
247struct cx18_options { 251struct cx18_options {
248 int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */ 252 int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */
249 int cardtype; /* force card type on load */ 253 int cardtype; /* force card type on load */
@@ -276,6 +280,18 @@ struct cx18_options {
276#define CX18_SLICED_TYPE_WSS_625 (5) 280#define CX18_SLICED_TYPE_WSS_625 (5)
277#define CX18_SLICED_TYPE_VPS (7) 281#define CX18_SLICED_TYPE_VPS (7)
278 282
283/**
284 * list_entry_is_past_end - check if a previous loop cursor is off list end
285 * @pos: the type * previously used as a loop cursor.
286 * @head: the head for your list.
287 * @member: the name of the list_struct within the struct.
288 *
289 * Check if the entry's list_head is the head of the list, thus it's not a
290 * real entry but was the loop cursor that walked past the end
291 */
292#define list_entry_is_past_end(pos, head, member) \
293 (&pos->member == (head))
294
279struct cx18_buffer { 295struct cx18_buffer {
280 struct list_head list; 296 struct list_head list;
281 dma_addr_t dma_handle; 297 dma_addr_t dma_handle;
@@ -558,6 +574,10 @@ struct cx18 {
558 int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */ 574 int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */
559 int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */ 575 int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
560 struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */ 576 struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */
577 struct snd_cx18_card *alsa; /* ALSA interface for PCM capture stream */
578 void (*pcm_announce_callback)(struct snd_cx18_card *card, u8 *pcm_data,
579 size_t num_bytes);
580
561 unsigned long i_flags; /* global cx18 flags */ 581 unsigned long i_flags; /* global cx18 flags */
562 atomic_t ana_capturing; /* count number of active analog capture streams */ 582 atomic_t ana_capturing; /* count number of active analog capture streams */
563 atomic_t tot_capturing; /* total count number of active capture streams */ 583 atomic_t tot_capturing; /* total count number of active capture streams */
@@ -575,12 +595,6 @@ struct cx18 {
575 595
576 struct vbi_info vbi; 596 struct vbi_info vbi;
577 597
578 u32 pgm_info_offset;
579 u32 pgm_info_num;
580 u32 pgm_info_write_idx;
581 u32 pgm_info_read_idx;
582 struct v4l2_enc_idx_entry pgm_info[CX18_MAX_PGM_INDEX];
583
584 u64 mpg_data_received; 598 u64 mpg_data_received;
585 u64 vbi_data_inserted; 599 u64 vbi_data_inserted;
586 600
@@ -623,6 +637,9 @@ struct cx18 {
623 u32 active_input; 637 u32 active_input;
624 v4l2_std_id std; 638 v4l2_std_id std;
625 v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ 639 v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */
640
641 /* Used for cx18-alsa module loading */
642 struct work_struct request_module_wk;
626}; 643};
627 644
628static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev) 645static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev)
@@ -630,6 +647,9 @@ static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev)
630 return container_of(v4l2_dev, struct cx18, v4l2_dev); 647 return container_of(v4l2_dev, struct cx18, v4l2_dev);
631} 648}
632 649
650/* cx18 extensions to be loaded */
651extern int (*cx18_ext_init)(struct cx18 *);
652
633/* Globals */ 653/* Globals */
634extern int cx18_first_minor; 654extern int cx18_first_minor;
635 655
diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
index 71ad2d1b4c2c..0ae2c2e1eab5 100644
--- a/drivers/media/video/cx18/cx18-dvb.c
+++ b/drivers/media/video/cx18/cx18-dvb.c
@@ -213,10 +213,14 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
213{ 213{
214 struct dvb_demux *demux = feed->demux; 214 struct dvb_demux *demux = feed->demux;
215 struct cx18_stream *stream = (struct cx18_stream *) demux->priv; 215 struct cx18_stream *stream = (struct cx18_stream *) demux->priv;
216 struct cx18 *cx = stream->cx; 216 struct cx18 *cx;
217 int ret; 217 int ret;
218 u32 v; 218 u32 v;
219 219
220 if (!stream)
221 return -EINVAL;
222
223 cx = stream->cx;
220 CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", 224 CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n",
221 feed->pid, feed->index); 225 feed->pid, feed->index);
222 226
@@ -253,12 +257,10 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
253 if (!demux->dmx.frontend) 257 if (!demux->dmx.frontend)
254 return -EINVAL; 258 return -EINVAL;
255 259
256 if (!stream)
257 return -EINVAL;
258
259 mutex_lock(&stream->dvb.feedlock); 260 mutex_lock(&stream->dvb.feedlock);
260 if (stream->dvb.feeding++ == 0) { 261 if (stream->dvb.feeding++ == 0) {
261 CX18_DEBUG_INFO("Starting Transport DMA\n"); 262 CX18_DEBUG_INFO("Starting Transport DMA\n");
263 mutex_lock(&cx->serialize_lock);
262 set_bit(CX18_F_S_STREAMING, &stream->s_flags); 264 set_bit(CX18_F_S_STREAMING, &stream->s_flags);
263 ret = cx18_start_v4l2_encode_stream(stream); 265 ret = cx18_start_v4l2_encode_stream(stream);
264 if (ret < 0) { 266 if (ret < 0) {
@@ -267,6 +269,7 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
267 if (stream->dvb.feeding == 0) 269 if (stream->dvb.feeding == 0)
268 clear_bit(CX18_F_S_STREAMING, &stream->s_flags); 270 clear_bit(CX18_F_S_STREAMING, &stream->s_flags);
269 } 271 }
272 mutex_unlock(&cx->serialize_lock);
270 } else 273 } else
271 ret = 0; 274 ret = 0;
272 mutex_unlock(&stream->dvb.feedlock); 275 mutex_unlock(&stream->dvb.feedlock);
@@ -279,17 +282,20 @@ static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed)
279{ 282{
280 struct dvb_demux *demux = feed->demux; 283 struct dvb_demux *demux = feed->demux;
281 struct cx18_stream *stream = (struct cx18_stream *)demux->priv; 284 struct cx18_stream *stream = (struct cx18_stream *)demux->priv;
282 struct cx18 *cx = stream->cx; 285 struct cx18 *cx;
283 int ret = -EINVAL; 286 int ret = -EINVAL;
284 287
285 CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
286 feed->pid, feed->index);
287
288 if (stream) { 288 if (stream) {
289 cx = stream->cx;
290 CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
291 feed->pid, feed->index);
292
289 mutex_lock(&stream->dvb.feedlock); 293 mutex_lock(&stream->dvb.feedlock);
290 if (--stream->dvb.feeding == 0) { 294 if (--stream->dvb.feeding == 0) {
291 CX18_DEBUG_INFO("Stopping Transport DMA\n"); 295 CX18_DEBUG_INFO("Stopping Transport DMA\n");
296 mutex_lock(&cx->serialize_lock);
292 ret = cx18_stop_v4l2_encode_stream(stream, 0); 297 ret = cx18_stop_v4l2_encode_stream(stream, 0);
298 mutex_unlock(&cx->serialize_lock);
293 } else 299 } else
294 ret = 0; 300 ret = 0;
295 mutex_unlock(&stream->dvb.feedlock); 301 mutex_unlock(&stream->dvb.feedlock);
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index c0885c69fd89..863ce7758239 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -37,15 +37,21 @@
37 37
38/* This function tries to claim the stream for a specific file descriptor. 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 39 If no one else is using this stream then the stream is claimed and
40 associated VBI streams are also automatically claimed. 40 associated VBI and IDX streams are also automatically claimed.
41 Possible error returns: -EBUSY if someone else has claimed 41 Possible error returns: -EBUSY if someone else has claimed
42 the stream or 0 on success. */ 42 the stream or 0 on success. */
43static int cx18_claim_stream(struct cx18_open_id *id, int type) 43int cx18_claim_stream(struct cx18_open_id *id, int type)
44{ 44{
45 struct cx18 *cx = id->cx; 45 struct cx18 *cx = id->cx;
46 struct cx18_stream *s = &cx->streams[type]; 46 struct cx18_stream *s = &cx->streams[type];
47 struct cx18_stream *s_vbi; 47 struct cx18_stream *s_assoc;
48 int vbi_type; 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 }
49 55
50 if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) { 56 if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
51 /* someone already claimed this stream */ 57 /* someone already claimed this stream */
@@ -67,32 +73,47 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type)
67 } 73 }
68 s->id = id->open_id; 74 s->id = id->open_id;
69 75
70 /* CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI 76 /*
71 (provided VBI insertion is on and sliced VBI is selected), for all 77 * CX18_ENC_STREAM_TYPE_MPG needs to claim:
72 other streams we're done */ 78 * CX18_ENC_STREAM_TYPE_VBI, if VBI insertion is on for sliced VBI, or
73 if (type == CX18_ENC_STREAM_TYPE_MPG && 79 * CX18_ENC_STREAM_TYPE_IDX, if VBI insertion is off for sliced VBI
74 cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) { 80 * (We don't yet fix up MPEG Index entries for our inserted packets).
75 vbi_type = CX18_ENC_STREAM_TYPE_VBI; 81 *
76 } else { 82 * For all other streams we're done.
83 */
84 if (type != CX18_ENC_STREAM_TYPE_MPG)
77 return 0; 85 return 0;
78 }
79 s_vbi = &cx->streams[vbi_type];
80 86
81 set_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags); 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);
82 94
83 /* mark that it is used internally */ 95 /* mark that it is used internally */
84 set_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags); 96 set_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags);
85 return 0; 97 return 0;
86} 98}
99EXPORT_SYMBOL(cx18_claim_stream);
87 100
88/* This function releases a previously claimed stream. It will take into 101/* This function releases a previously claimed stream. It will take into
89 account associated VBI streams. */ 102 account associated VBI streams. */
90static void cx18_release_stream(struct cx18_stream *s) 103void cx18_release_stream(struct cx18_stream *s)
91{ 104{
92 struct cx18 *cx = s->cx; 105 struct cx18 *cx = s->cx;
93 struct cx18_stream *s_vbi; 106 struct cx18_stream *s_assoc;
94 107
95 s->id = -1; 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
96 if (s->type == CX18_ENC_STREAM_TYPE_VBI && 117 if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
97 test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) { 118 test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) {
98 /* this stream is still in use internally */ 119 /* this stream is still in use internally */
@@ -105,25 +126,36 @@ static void cx18_release_stream(struct cx18_stream *s)
105 126
106 cx18_flush_queues(s); 127 cx18_flush_queues(s);
107 128
108 /* CX18_ENC_STREAM_TYPE_MPG needs to release CX18_ENC_STREAM_TYPE_VBI, 129 /*
109 for all other streams we're done */ 130 * CX18_ENC_STREAM_TYPE_MPG needs to release the
110 if (s->type == CX18_ENC_STREAM_TYPE_MPG) 131 * CX18_ENC_STREAM_TYPE_VBI and/or CX18_ENC_STREAM_TYPE_IDX streams.
111 s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; 132 *
112 else 133 * For all other streams we're done.
134 */
135 if (s->type != CX18_ENC_STREAM_TYPE_MPG)
113 return; 136 return;
114 137
115 /* clear internal use flag */ 138 /* Unclaim the associated MPEG Index stream */
116 if (!test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags)) { 139 s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
117 /* was already cleared */ 140 if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) {
118 return; 141 clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags);
142 cx18_flush_queues(s_assoc);
119 } 143 }
120 if (s_vbi->id != -1) { 144
121 /* VBI stream still claimed by a file descriptor */ 145 /* Unclaim the associated VBI stream */
122 return; 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 }
123 } 156 }
124 clear_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
125 cx18_flush_queues(s_vbi);
126} 157}
158EXPORT_SYMBOL(cx18_release_stream);
127 159
128static void cx18_dualwatch(struct cx18 *cx) 160static void cx18_dualwatch(struct cx18 *cx)
129{ 161{
@@ -177,9 +209,7 @@ static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block,
177 *err = 0; 209 *err = 0;
178 while (1) { 210 while (1) {
179 if (s->type == CX18_ENC_STREAM_TYPE_MPG) { 211 if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
180 /* Process pending program info updates and pending 212 /* Process pending program updates and VBI data */
181 VBI data */
182
183 if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) { 213 if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
184 cx->dualwatch_jiffies = jiffies; 214 cx->dualwatch_jiffies = jiffies;
185 cx18_dualwatch(cx); 215 cx18_dualwatch(cx);
@@ -362,18 +392,6 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
362 return len; 392 return len;
363} 393}
364 394
365/**
366 * list_entry_is_past_end - check if a previous loop cursor is off list end
367 * @pos: the type * previously used as a loop cursor.
368 * @head: the head for your list.
369 * @member: the name of the list_struct within the struct.
370 *
371 * Check if the entry's list_head is the head of the list, thus it's not a
372 * real entry but was the loop cursor that walked past the end
373 */
374#define list_entry_is_past_end(pos, head, member) \
375 (&pos->member == (head))
376
377static size_t cx18_copy_mdl_to_user(struct cx18_stream *s, 395static size_t cx18_copy_mdl_to_user(struct cx18_stream *s,
378 struct cx18_mdl *mdl, char __user *ubuf, size_t ucount) 396 struct cx18_mdl *mdl, char __user *ubuf, size_t ucount)
379{ 397{
@@ -498,6 +516,7 @@ int cx18_start_capture(struct cx18_open_id *id)
498 struct cx18 *cx = id->cx; 516 struct cx18 *cx = id->cx;
499 struct cx18_stream *s = &cx->streams[id->type]; 517 struct cx18_stream *s = &cx->streams[id->type];
500 struct cx18_stream *s_vbi; 518 struct cx18_stream *s_vbi;
519 struct cx18_stream *s_idx;
501 520
502 if (s->type == CX18_ENC_STREAM_TYPE_RAD) { 521 if (s->type == CX18_ENC_STREAM_TYPE_RAD) {
503 /* you cannot read from these stream types. */ 522 /* you cannot read from these stream types. */
@@ -516,25 +535,33 @@ int cx18_start_capture(struct cx18_open_id *id)
516 return 0; 535 return 0;
517 } 536 }
518 537
519 /* Start VBI capture if required */ 538 /* Start associated VBI or IDX stream capture if required */
520 s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; 539 s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
521 if (s->type == CX18_ENC_STREAM_TYPE_MPG && 540 s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
522 test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && 541 if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
523 !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { 542 /*
524 /* Note: the CX18_ENC_STREAM_TYPE_VBI is claimed 543 * The VBI and IDX streams should have been claimed
525 automatically when the MPG stream is claimed. 544 * automatically, if for internal use, when the MPG stream was
526 We only need to start the VBI capturing. */ 545 * claimed. We only need to start these streams capturing.
527 if (cx18_start_v4l2_encode_stream(s_vbi)) { 546 */
528 CX18_DEBUG_WARN("VBI capture start failed\n"); 547 if (test_bit(CX18_F_S_INTERNAL_USE, &s_idx->s_flags) &&
529 548 !test_and_set_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
530 /* Failure, clean up and return an error */ 549 if (cx18_start_v4l2_encode_stream(s_idx)) {
531 clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); 550 CX18_DEBUG_WARN("IDX capture start failed\n");
532 clear_bit(CX18_F_S_STREAMING, &s->s_flags); 551 clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags);
533 /* also releases the associated VBI stream */ 552 goto start_failed;
534 cx18_release_stream(s); 553 }
535 return -EIO; 554 CX18_DEBUG_INFO("IDX capture started\n");
555 }
556 if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
557 !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
558 if (cx18_start_v4l2_encode_stream(s_vbi)) {
559 CX18_DEBUG_WARN("VBI capture start failed\n");
560 clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
561 goto start_failed;
562 }
563 CX18_DEBUG_INFO("VBI insertion started\n");
536 } 564 }
537 CX18_DEBUG_INFO("VBI insertion started\n");
538 } 565 }
539 566
540 /* Tell the card to start capturing */ 567 /* Tell the card to start capturing */
@@ -547,19 +574,29 @@ int cx18_start_capture(struct cx18_open_id *id)
547 return 0; 574 return 0;
548 } 575 }
549 576
550 /* failure, clean up */ 577start_failed:
551 CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name); 578 CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
552 579
553 /* Note: the CX18_ENC_STREAM_TYPE_VBI is released 580 /*
554 automatically when the MPG stream is released. 581 * The associated VBI and IDX streams for internal use are released
555 We only need to stop the VBI capturing. */ 582 * automatically when the MPG stream is released. We only need to stop
556 if (s->type == CX18_ENC_STREAM_TYPE_MPG && 583 * the associated stream.
557 test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { 584 */
558 cx18_stop_v4l2_encode_stream(s_vbi, 0); 585 if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
559 clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); 586 /* Stop the IDX stream which is always for internal use */
587 if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
588 cx18_stop_v4l2_encode_stream(s_idx, 0);
589 clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags);
590 }
591 /* Stop the VBI stream, if only running for internal use */
592 if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
593 !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
594 cx18_stop_v4l2_encode_stream(s_vbi, 0);
595 clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
596 }
560 } 597 }
561 clear_bit(CX18_F_S_STREAMING, &s->s_flags); 598 clear_bit(CX18_F_S_STREAMING, &s->s_flags);
562 cx18_release_stream(s); 599 cx18_release_stream(s); /* Also releases associated streams */
563 return -EIO; 600 return -EIO;
564} 601}
565 602
@@ -618,6 +655,8 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
618{ 655{
619 struct cx18 *cx = id->cx; 656 struct cx18 *cx = id->cx;
620 struct cx18_stream *s = &cx->streams[id->type]; 657 struct cx18_stream *s = &cx->streams[id->type];
658 struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
659 struct cx18_stream *s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
621 660
622 CX18_DEBUG_IOCTL("close() of %s\n", s->name); 661 CX18_DEBUG_IOCTL("close() of %s\n", s->name);
623 662
@@ -625,17 +664,19 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
625 664
626 /* Stop capturing */ 665 /* Stop capturing */
627 if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) { 666 if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
628 struct cx18_stream *s_vbi =
629 &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
630
631 CX18_DEBUG_INFO("close stopping capture\n"); 667 CX18_DEBUG_INFO("close stopping capture\n");
632 /* Special case: a running VBI capture for VBI insertion 668 if (id->type == CX18_ENC_STREAM_TYPE_MPG) {
633 in the mpeg stream. Need to stop that too. */ 669 /* Stop internal use associated VBI and IDX streams */
634 if (id->type == CX18_ENC_STREAM_TYPE_MPG && 670 if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
635 test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) && 671 !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
636 !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { 672 CX18_DEBUG_INFO("close stopping embedded VBI "
637 CX18_DEBUG_INFO("close stopping embedded VBI capture\n"); 673 "capture\n");
638 cx18_stop_v4l2_encode_stream(s_vbi, 0); 674 cx18_stop_v4l2_encode_stream(s_vbi, 0);
675 }
676 if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) {
677 CX18_DEBUG_INFO("close stopping IDX capture\n");
678 cx18_stop_v4l2_encode_stream(s_idx, 0);
679 }
639 } 680 }
640 if (id->type == CX18_ENC_STREAM_TYPE_VBI && 681 if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
641 test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) 682 test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags))
diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h
index 92e2d5dab936..5c8fcb884f0a 100644
--- a/drivers/media/video/cx18/cx18-fileops.h
+++ b/drivers/media/video/cx18/cx18-fileops.h
@@ -34,3 +34,6 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end);
34void cx18_mute(struct cx18 *cx); 34void cx18_mute(struct cx18 *cx);
35void cx18_unmute(struct cx18 *cx); 35void cx18_unmute(struct cx18 *cx);
36 36
37/* Shared with cx18-alsa module */
38int cx18_claim_stream(struct cx18_open_id *id, int type);
39void cx18_release_stream(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 3e4fc192fdec..b81dd0ea8eb9 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -775,10 +775,143 @@ static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
775 return 0; 775 return 0;
776} 776}
777 777
778static int _cx18_process_idx_data(struct cx18_buffer *buf,
779 struct v4l2_enc_idx *idx)
780{
781 int consumed, remaining;
782 struct v4l2_enc_idx_entry *e_idx;
783 struct cx18_enc_idx_entry *e_buf;
784
785 /* Frame type lookup: 1=I, 2=P, 4=B */
786 const int mapping[8] = {
787 -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P,
788 -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1
789 };
790
791 /*
792 * Assumption here is that a buf holds an integral number of
793 * struct cx18_enc_idx_entry objects and is properly aligned.
794 * This is enforced by the module options on IDX buffer sizes.
795 */
796 remaining = buf->bytesused - buf->readpos;
797 consumed = 0;
798 e_idx = &idx->entry[idx->entries];
799 e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos];
800
801 while (remaining >= sizeof(struct cx18_enc_idx_entry) &&
802 idx->entries < V4L2_ENC_IDX_ENTRIES) {
803
804 e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32)
805 | le32_to_cpu(e_buf->offset_low);
806
807 e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32)
808 | le32_to_cpu(e_buf->pts_low);
809
810 e_idx->length = le32_to_cpu(e_buf->length);
811
812 e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7];
813
814 e_idx->reserved[0] = 0;
815 e_idx->reserved[1] = 0;
816
817 idx->entries++;
818 e_idx = &idx->entry[idx->entries];
819 e_buf++;
820
821 remaining -= sizeof(struct cx18_enc_idx_entry);
822 consumed += sizeof(struct cx18_enc_idx_entry);
823 }
824
825 /* Swallow any partial entries at the end, if there are any */
826 if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry))
827 consumed += remaining;
828
829 buf->readpos += consumed;
830 return consumed;
831}
832
833static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
834 struct v4l2_enc_idx *idx)
835{
836 if (s->type != CX18_ENC_STREAM_TYPE_IDX)
837 return -EINVAL;
838
839 if (mdl->curr_buf == NULL)
840 mdl->curr_buf = list_first_entry(&mdl->buf_list,
841 struct cx18_buffer, list);
842
843 if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
844 /*
845 * For some reason we've exhausted the buffers, but the MDL
846 * object still said some data was unread.
847 * Fix that and bail out.
848 */
849 mdl->readpos = mdl->bytesused;
850 return 0;
851 }
852
853 list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
854
855 /* Skip any empty buffers in the MDL */
856 if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
857 continue;
858
859 mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx);
860
861 /* exit when MDL drained or request satisfied */
862 if (idx->entries >= V4L2_ENC_IDX_ENTRIES ||
863 mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
864 mdl->readpos >= mdl->bytesused)
865 break;
866 }
867 return 0;
868}
869
778static int cx18_g_enc_index(struct file *file, void *fh, 870static int cx18_g_enc_index(struct file *file, void *fh,
779 struct v4l2_enc_idx *idx) 871 struct v4l2_enc_idx *idx)
780{ 872{
781 return -EINVAL; 873 struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
874 struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
875 s32 tmp;
876 struct cx18_mdl *mdl;
877
878 if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */
879 return -EINVAL;
880
881 /* Compute the best case number of entries we can buffer */
882 tmp = s->buffers -
883 s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN;
884 if (tmp <= 0)
885 tmp = 1;
886 tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry);
887
888 /* Fill out the header of the return structure */
889 idx->entries = 0;
890 idx->entries_cap = tmp;
891 memset(idx->reserved, 0, sizeof(idx->reserved));
892
893 /* Pull IDX MDLs and buffers from q_full and populate the entries */
894 do {
895 mdl = cx18_dequeue(s, &s->q_full);
896 if (mdl == NULL) /* No more IDX data right now */
897 break;
898
899 /* Extract the Index entry data from the MDL and buffers */
900 cx18_process_idx_data(s, mdl, idx);
901 if (mdl->readpos < mdl->bytesused) {
902 /* We finished with data remaining, push the MDL back */
903 cx18_push(s, mdl, &s->q_full);
904 break;
905 }
906
907 /* We drained this MDL, schedule it to go to the firmware */
908 cx18_enqueue(s, mdl, &s->q_free);
909
910 } while (idx->entries < V4L2_ENC_IDX_ENTRIES);
911
912 /* Tell the work handler to send free IDX MDLs to the firmware */
913 cx18_stream_load_fw_queue(s);
914 return 0;
782} 915}
783 916
784static int cx18_encoder_cmd(struct file *file, void *fh, 917static int cx18_encoder_cmd(struct file *file, void *fh,
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index f231dd09c720..6dcce297752f 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -29,6 +29,7 @@
29#include "cx18-mailbox.h" 29#include "cx18-mailbox.h"
30#include "cx18-queue.h" 30#include "cx18-queue.h"
31#include "cx18-streams.h" 31#include "cx18-streams.h"
32#include "cx18-alsa-pcm.h" /* FIXME make configurable */
32 33
33static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" }; 34static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" };
34 35
@@ -157,6 +158,34 @@ static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl)
157 } 158 }
158} 159}
159 160
161
162static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s,
163 struct cx18_mdl *mdl)
164{
165 struct cx18_buffer *buf;
166
167 if (mdl->bytesused == 0)
168 return;
169
170 /* We ignore mdl and buf readpos accounting here - it doesn't matter */
171
172 /* The likely case */
173 if (list_is_singular(&mdl->buf_list)) {
174 buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
175 list);
176 if (buf->bytesused)
177 cx->pcm_announce_callback(cx->alsa, buf->buf,
178 buf->bytesused);
179 return;
180 }
181
182 list_for_each_entry(buf, &mdl->buf_list, list) {
183 if (buf->bytesused == 0)
184 break;
185 cx->pcm_announce_callback(cx->alsa, buf->buf, buf->bytesused);
186 }
187}
188
160static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) 189static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
161{ 190{
162 u32 handle, mdl_ack_count, id; 191 u32 handle, mdl_ack_count, id;
@@ -223,11 +252,21 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
223 CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", 252 CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n",
224 s->name, mdl->bytesused); 253 s->name, mdl->bytesused);
225 254
226 if (s->type != CX18_ENC_STREAM_TYPE_TS) 255 if (s->type == CX18_ENC_STREAM_TYPE_TS) {
227 cx18_enqueue(s, mdl, &s->q_full);
228 else {
229 cx18_mdl_send_to_dvb(s, mdl); 256 cx18_mdl_send_to_dvb(s, mdl);
230 cx18_enqueue(s, mdl, &s->q_free); 257 cx18_enqueue(s, mdl, &s->q_free);
258 } else if (s->type == CX18_ENC_STREAM_TYPE_PCM) {
259 /* Pass the data to cx18-alsa */
260 if (cx->pcm_announce_callback != NULL) {
261 cx18_mdl_send_to_alsa(cx, s, mdl);
262 cx18_enqueue(s, mdl, &s->q_free);
263 } else {
264 cx18_enqueue(s, mdl, &s->q_full);
265 }
266 } else {
267 cx18_enqueue(s, mdl, &s->q_full);
268 if (s->type == CX18_ENC_STREAM_TYPE_IDX)
269 cx18_stream_rotate_idx_mdls(cx);
231 } 270 }
232 } 271 }
233 /* Put as many MDLs as possible back into fw use */ 272 /* Put as many MDLs as possible back into fw use */
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index 63304823cef5..aefc8c8cf3c1 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -419,6 +419,9 @@ void cx18_stream_free(struct cx18_stream *s)
419{ 419{
420 struct cx18_mdl *mdl; 420 struct cx18_mdl *mdl;
421 struct cx18_buffer *buf; 421 struct cx18_buffer *buf;
422 struct cx18 *cx = s->cx;
423
424 CX18_DEBUG_INFO("Deallocating buffers for %s stream\n", s->name);
422 425
423 /* move all buffers to buf_pool and all MDLs to q_idle */ 426 /* move all buffers to buf_pool and all MDLs to q_idle */
424 cx18_unload_queues(s); 427 cx18_unload_queues(s);
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 987a9308d938..054450f65a60 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -319,11 +319,27 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister)
319 319
320 /* Teardown all streams */ 320 /* Teardown all streams */
321 for (type = 0; type < CX18_MAX_STREAMS; type++) { 321 for (type = 0; type < CX18_MAX_STREAMS; type++) {
322 if (cx->streams[type].dvb.enabled) { 322
323 cx18_dvb_unregister(&cx->streams[type]); 323 /* No struct video_device, but can have buffers allocated */
324 cx->streams[type].dvb.enabled = false; 324 if (type == CX18_ENC_STREAM_TYPE_TS) {
325 if (cx->streams[type].dvb.enabled) {
326 cx18_dvb_unregister(&cx->streams[type]);
327 cx->streams[type].dvb.enabled = false;
328 cx18_stream_free(&cx->streams[type]);
329 }
330 continue;
331 }
332
333 /* No struct video_device, but can have buffers allocated */
334 if (type == CX18_ENC_STREAM_TYPE_IDX) {
335 if (cx->stream_buffers[type] != 0) {
336 cx->stream_buffers[type] = 0;
337 cx18_stream_free(&cx->streams[type]);
338 }
339 continue;
325 } 340 }
326 341
342 /* If struct video_device exists, can have buffers allocated */
327 vdev = cx->streams[type].video_dev; 343 vdev = cx->streams[type].video_dev;
328 344
329 cx->streams[type].video_dev = NULL; 345 cx->streams[type].video_dev = NULL;
@@ -447,6 +463,32 @@ static void cx18_vbi_setup(struct cx18_stream *s)
447 cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); 463 cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
448} 464}
449 465
466void cx18_stream_rotate_idx_mdls(struct cx18 *cx)
467{
468 struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
469 struct cx18_mdl *mdl;
470
471 if (!cx18_stream_enabled(s))
472 return;
473
474 /* Return if the firmware is not running low on MDLs */
475 if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >=
476 CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN)
477 return;
478
479 /* Return if there are no MDLs to rotate back to the firmware */
480 if (atomic_read(&s->q_full.depth) < 2)
481 return;
482
483 /*
484 * Take the oldest IDX MDL still holding data, and discard its index
485 * entries by scheduling the MDL to go back to the firmware
486 */
487 mdl = cx18_dequeue(s, &s->q_full);
488 if (mdl != NULL)
489 cx18_enqueue(s, mdl, &s->q_free);
490}
491
450static 492static
451struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s, 493struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
452 struct cx18_mdl *mdl) 494 struct cx18_mdl *mdl)
@@ -546,8 +588,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
546 struct cx18 *cx = s->cx; 588 struct cx18 *cx = s->cx;
547 int captype = 0; 589 int captype = 0;
548 struct cx18_api_func_private priv; 590 struct cx18_api_func_private priv;
591 struct cx18_stream *s_idx;
549 592
550 if (s->video_dev == NULL && s->dvb.enabled == 0) 593 if (!cx18_stream_enabled(s))
551 return -EINVAL; 594 return -EINVAL;
552 595
553 CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); 596 CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
@@ -561,6 +604,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
561 cx->search_pack_header = 0; 604 cx->search_pack_header = 0;
562 break; 605 break;
563 606
607 case CX18_ENC_STREAM_TYPE_IDX:
608 captype = CAPTURE_CHANNEL_TYPE_INDEX;
609 break;
564 case CX18_ENC_STREAM_TYPE_TS: 610 case CX18_ENC_STREAM_TYPE_TS:
565 captype = CAPTURE_CHANNEL_TYPE_TS; 611 captype = CAPTURE_CHANNEL_TYPE_TS;
566 break; 612 break;
@@ -635,11 +681,13 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
635 cx18_vbi_setup(s); 681 cx18_vbi_setup(s);
636 682
637 /* 683 /*
638 * assign program index info. 684 * Select to receive I, P, and B frame index entries, if the
639 * Mask 7: select I/P/B, Num_req: 400 max 685 * index stream is enabled. Otherwise disable index entry
640 * FIXME - currently we have this hardcoded as disabled 686 * generation.
641 */ 687 */
642 cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0); 688 s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX];
689 cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2,
690 s->handle, cx18_stream_enabled(s_idx) ? 7 : 0);
643 691
644 /* Call out to the common CX2341x API setup for user controls */ 692 /* Call out to the common CX2341x API setup for user controls */
645 priv.cx = cx; 693 priv.cx = cx;
@@ -697,6 +745,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
697 atomic_inc(&cx->tot_capturing); 745 atomic_inc(&cx->tot_capturing);
698 return 0; 746 return 0;
699} 747}
748EXPORT_SYMBOL(cx18_start_v4l2_encode_stream);
700 749
701void cx18_stop_all_captures(struct cx18 *cx) 750void cx18_stop_all_captures(struct cx18 *cx)
702{ 751{
@@ -705,7 +754,7 @@ void cx18_stop_all_captures(struct cx18 *cx)
705 for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) { 754 for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
706 struct cx18_stream *s = &cx->streams[i]; 755 struct cx18_stream *s = &cx->streams[i];
707 756
708 if (s->video_dev == NULL && s->dvb.enabled == 0) 757 if (!cx18_stream_enabled(s))
709 continue; 758 continue;
710 if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) 759 if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
711 cx18_stop_v4l2_encode_stream(s, 0); 760 cx18_stop_v4l2_encode_stream(s, 0);
@@ -717,7 +766,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
717 struct cx18 *cx = s->cx; 766 struct cx18 *cx = s->cx;
718 unsigned long then; 767 unsigned long then;
719 768
720 if (s->video_dev == NULL && s->dvb.enabled == 0) 769 if (!cx18_stream_enabled(s))
721 return -EINVAL; 770 return -EINVAL;
722 771
723 /* This function assumes that you are allowed to stop the capture 772 /* This function assumes that you are allowed to stop the capture
@@ -762,6 +811,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
762 811
763 return 0; 812 return 0;
764} 813}
814EXPORT_SYMBOL(cx18_stop_v4l2_encode_stream);
765 815
766u32 cx18_find_handle(struct cx18 *cx) 816u32 cx18_find_handle(struct cx18 *cx)
767{ 817{
@@ -789,7 +839,7 @@ struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
789 s = &cx->streams[i]; 839 s = &cx->streams[i];
790 if (s->handle != handle) 840 if (s->handle != handle)
791 continue; 841 continue;
792 if (s->video_dev || s->dvb.enabled) 842 if (cx18_stream_enabled(s))
793 return s; 843 return s;
794 } 844 }
795 return NULL; 845 return NULL;
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
index 4a01db5e5a35..0bff0fa29763 100644
--- a/drivers/media/video/cx18/cx18-streams.h
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -28,6 +28,16 @@ int cx18_streams_setup(struct cx18 *cx);
28int cx18_streams_register(struct cx18 *cx); 28int cx18_streams_register(struct cx18 *cx);
29void cx18_streams_cleanup(struct cx18 *cx, int unregister); 29void cx18_streams_cleanup(struct cx18 *cx, int unregister);
30 30
31#define CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN (3)
32void cx18_stream_rotate_idx_mdls(struct cx18 *cx);
33
34static inline bool cx18_stream_enabled(struct cx18_stream *s)
35{
36 return s->video_dev || s->dvb.enabled ||
37 (s->type == CX18_ENC_STREAM_TYPE_IDX &&
38 s->cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] != 0);
39}
40
31/* Related to submission of mdls to firmware */ 41/* Related to submission of mdls to firmware */
32static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) 42static inline void cx18_stream_load_fw_queue(struct cx18_stream *s)
33{ 43{
diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
index 9c0b5bb1b019..3e1aec4bcfde 100644
--- a/drivers/media/video/cx18/cx18-version.h
+++ b/drivers/media/video/cx18/cx18-version.h
@@ -24,7 +24,7 @@
24 24
25#define CX18_DRIVER_NAME "cx18" 25#define CX18_DRIVER_NAME "cx18"
26#define CX18_DRIVER_VERSION_MAJOR 1 26#define CX18_DRIVER_VERSION_MAJOR 1
27#define CX18_DRIVER_VERSION_MINOR 3 27#define CX18_DRIVER_VERSION_MINOR 4
28#define CX18_DRIVER_VERSION_PATCHLEVEL 0 28#define CX18_DRIVER_VERSION_PATCHLEVEL 0
29 29
30#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) 30#define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
index 868806effdcf..2c00980acfcb 100644
--- a/drivers/media/video/cx18/cx23418.h
+++ b/drivers/media/video/cx18/cx23418.h
@@ -191,7 +191,8 @@
191#define CX18_CPU_SET_MEDIAN_CORING (CPU_CMD_MASK_CAPTURE | 0x000E) 191#define CX18_CPU_SET_MEDIAN_CORING (CPU_CMD_MASK_CAPTURE | 0x000E)
192 192
193/* Description: This command set the picture type mask for index file 193/* Description: This command set the picture type mask for index file
194 IN[0] - 0 = disable index file output 194 IN[0] - Task handle (ignored by firmware)
195 IN[1] - 0 = disable index file output
195 1 = output I picture 196 1 = output I picture
196 2 = P picture 197 2 = P picture
197 4 = B picture 198 4 = B picture