aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18/cx18-alsa.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/cx18/cx18-alsa.c')
-rw-r--r--drivers/media/video/cx18/cx18-alsa.c303
1 files changed, 303 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/cx18-alsa.c b/drivers/media/video/cx18/cx18-alsa.c
new file mode 100644
index 000000000000..dde2b0482f34
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-alsa.c
@@ -0,0 +1,303 @@
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
22#include <linux/init.h>
23#include <linux/module.h>
24#include <linux/kernel.h>
25#include <linux/device.h>
26#include <linux/spinlock.h>
27
28#include <media/v4l2-device.h>
29
30#include <sound/core.h>
31#include <sound/initval.h>
32
33#include "cx18-driver.h"
34#include "cx18-alsa-mixer.h"
35#include "cx18-alsa-pcm.h"
36
37int cx18_alsa_debug;
38
39module_param_named(debug, cx18_alsa_debug, int, 0644);
40MODULE_PARM_DESC(debug,
41 "Debug level (bitmask). Default: 0\n"
42 "\t\t\t 1/0x0001: warning\n"
43 "\t\t\t 2/0x0002: info\n");
44
45MODULE_AUTHOR("Andy Walls");
46MODULE_DESCRIPTION("CX23418 ALSA Interface");
47MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
48MODULE_LICENSE("GPL");
49
50MODULE_VERSION(CX18_VERSION);
51
52static inline
53struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev)
54{
55 return to_cx18(v4l2_dev)->alsa;
56}
57
58static inline
59struct snd_cx18_card *p_to_snd_cx18_card(struct v4l2_device **v4l2_dev)
60{
61 return container_of(v4l2_dev, struct snd_cx18_card, v4l2_dev);
62}
63
64static void snd_cx18_card_free(struct snd_cx18_card *cxsc)
65{
66 if (cxsc == NULL)
67 return;
68
69 if (cxsc->v4l2_dev != NULL)
70 to_cx18(cxsc->v4l2_dev)->alsa = NULL;
71
72 /* FIXME - take any other stopping actions needed */
73
74 kfree(cxsc);
75}
76
77static void snd_cx18_card_private_free(struct snd_card *sc)
78{
79 if (sc == NULL)
80 return;
81 snd_cx18_card_free(sc->private_data);
82 sc->private_data = NULL;
83 sc->private_free = NULL;
84}
85
86static int __init snd_cx18_card_create(struct v4l2_device *v4l2_dev,
87 struct snd_card *sc,
88 struct snd_cx18_card **cxsc)
89{
90 *cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL);
91 if (*cxsc == NULL)
92 return -ENOMEM;
93
94 (*cxsc)->v4l2_dev = v4l2_dev;
95 (*cxsc)->sc = sc;
96
97 sc->private_data = *cxsc;
98 sc->private_free = snd_cx18_card_private_free;
99
100 return 0;
101}
102
103static int __init snd_cx18_card_set_names(struct snd_cx18_card *cxsc)
104{
105 struct cx18 *cx = to_cx18(cxsc->v4l2_dev);
106 struct snd_card *sc = cxsc->sc;
107
108 /* sc->driver is used by alsa-lib's configurator: simple, unique */
109 strlcpy(sc->driver, "CX23418", sizeof(sc->driver));
110
111 /* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */
112 snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d",
113 cx->instance);
114
115 /* sc->longname is read from /proc/asound/cards */
116 snprintf(sc->longname, sizeof(sc->longname),
117 "CX23418 #%d %s TV/FM Radio/Line-In Capture",
118 cx->instance, cx->card_name);
119}
120
121static int __init snd_cx18_init(struct v4l2_device *v4l2_dev)
122{
123 struct cx18 *cx = to_cx18(v4l2_dev);
124 struct snd_card *sc;
125 struct snd_cx18_card *cxsc;
126 int ret;
127
128 /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */
129
130 /* (1) Check and increment the device index */
131 /* This is a no-op for us. We'll use the cx->instance */
132
133 /* (2) Create a card instance */
134 ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */
135 SNDRV_DEFAULT_STR1, /* xid from end of shortname*/
136 THIS_MODULE, 0, &sc);
137 if (ret) {
138 CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n",
139 __func__, ret);
140 goto err_exit;
141 }
142
143 /* (3) Create a main component */
144 ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc);
145 if (ret) {
146 CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n",
147 __func__, ret);
148 goto err_exit_free;
149 }
150
151 /* (4) Set the driver ID and name strings */
152 snd_cx18_card_set_names(cxsc);
153
154 /* (5) Create other components: mixer, PCM, & proc files */
155 ret = snd_cx18_mixer_create(cxsc);
156 if (ret) {
157 CX18_ALSA_WARN("%s: snd_cx18_mixer_create() failed with err %d:"
158 "proceeding anyway\n", __func__, ret);
159 }
160
161 ret = snd_cx18_pcm_create(cxsc);
162 if (ret) {
163 CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n",
164 __func__, ret);
165 goto err_exit_free;
166 }
167 /* FIXME - proc files */
168
169 /* (7) Set the driver data and return 0 */
170 /* We do this out of normal order for PCI drivers to avoid races */
171 cx->alsa = cxsc;
172
173 /* (6) Register the card instance */
174 ret = snd_card_register(sc);
175 if (ret) {
176 cx->alsa = NULL;
177 CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n",
178 __func__, ret);
179 goto err_exit_free;
180 }
181
182 return 0;
183
184err_exit_free:
185 snd_card_free(sc);
186err_exit:
187 return ret;
188}
189
190static int __init cx18_alsa_init_callback(struct device *dev, void *data)
191{
192 struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
193 int *count = data;
194 struct cx18 *cx;
195 struct cx18_stream *s;
196
197 if (v4l2_dev == NULL) {
198 printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",
199 __func__);
200 return 0;
201 }
202
203 cx = to_cx18(v4l2_dev);
204 s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM];
205 if (s->video_dev == NULL) {
206 CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - "
207 "skipping\n", __func__);
208 return 0;
209 }
210
211 if (cx->alsa != NULL) {
212 CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n",
213 __func__);
214 return 0;
215 }
216
217 if (snd_cx18_init(v4l2_dev)) {
218 CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n",
219 __func__);
220 } else {
221 CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance "
222 "%d\n", __func__, *count);
223 (*count)++;
224 }
225 return 0;
226}
227
228static int __init cx18_alsa_init(void)
229{
230 struct device_driver *drv;
231 int count = 0;
232 int ret;
233
234 printk(KERN_INFO "cx18-alsa: module loading...\n");
235
236 drv = driver_find("cx18", &pci_bus_type);
237 ret = driver_for_each_device(drv, NULL, &count,
238 cx18_alsa_init_callback);
239 put_driver(drv);
240
241 if (count == 0) {
242 printk(KERN_ERR "cx18-alsa: no cx18 cards found with a PCM "
243 "capture stream allocated\n");
244 ret = -ENODEV;
245 } else {
246 printk(KERN_INFO "cx18-alsa: ALSA interface(s) created for %d "
247 "cx18 card(s)\n", count);
248 ret = 0;
249 }
250
251 printk(KERN_INFO "cx18-alsa: module load complete\n");
252 return ret;
253}
254
255static void snd_cx18_exit(struct snd_cx18_card *cxsc)
256{
257 struct cx18 *cx = to_cx18(cxsc->4l2_dev);
258
259 /* FIXME - pointer checks & shutdown cxsc */
260
261 snd_card_free(cxsc->sc);
262 cx->alsa = NULL;
263}
264
265static int cx18_alsa_exit_callback(struct device *dev, void *data)
266{
267 struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
268 struct snd_cx18_card *cxsc;
269 struct cx18 *cx;
270
271 if (v4l2_dev == NULL) {
272 printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n",
273 __func__);
274 return 0;
275 }
276
277 cxsc = to_snd_cx18_card(v4l2_dev);
278 if (cxsc == NULL) {
279 CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n",
280 __func__);
281 return 0;
282 }
283
284 snd_cx18_exit(cxsc);
285 return 0;
286}
287
288static void cx18_alsa_exit(void)
289{
290 struct device_driver *drv;
291 int ret;
292
293 printk(KERN_INFO "cx18-alsa: module unloading...\n");
294
295 drv = driver_find("cx18", &pci_bus_type);
296 ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback);
297 put_driver(drv);
298
299 printk(KERN_INFO "cx18-alsa: module unload complete\n");
300}
301
302module_init(cx18_alsa_init);
303module_exit(cx18_alsa_exit);