Commit | Line | Data |
---|---|---|
269c11fb AW |
1 | /* |
2 | * ALSA interface to ivtv PCM capture streams | |
3 | * | |
4 | * Copyright (C) 2009,2012 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 for the cx18 driver | |
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 "ivtv-driver.h" | |
38 | #include "ivtv-version.h" | |
39 | #include "ivtv-alsa.h" | |
40 | #include "ivtv-alsa-mixer.h" | |
41 | #include "ivtv-alsa-pcm.h" | |
42 | ||
43 | int ivtv_alsa_debug; | |
27315059 | 44 | static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; |
269c11fb AW |
45 | |
46 | #define IVTV_DEBUG_ALSA_INFO(fmt, arg...) \ | |
47 | do { \ | |
48 | if (ivtv_alsa_debug & 2) \ | |
49 | pr_info("%s: " fmt, "ivtv-alsa", ## arg); \ | |
50 | } while (0) | |
51 | ||
52 | module_param_named(debug, ivtv_alsa_debug, int, 0644); | |
53 | MODULE_PARM_DESC(debug, | |
54 | "Debug level (bitmask). Default: 0\n" | |
55 | "\t\t\t 1/0x0001: warning\n" | |
56 | "\t\t\t 2/0x0002: info\n"); | |
57 | ||
27315059 NS |
58 | module_param_array(index, int, NULL, 0444); |
59 | MODULE_PARM_DESC(index, | |
60 | "Index value for IVTV ALSA capture interface(s).\n"); | |
61 | ||
269c11fb AW |
62 | MODULE_AUTHOR("Andy Walls"); |
63 | MODULE_DESCRIPTION("CX23415/CX23416 ALSA Interface"); | |
64 | MODULE_SUPPORTED_DEVICE("CX23415/CX23416 MPEG2 encoder"); | |
65 | MODULE_LICENSE("GPL"); | |
66 | ||
67 | MODULE_VERSION(IVTV_VERSION); | |
68 | ||
69 | static inline | |
70 | struct snd_ivtv_card *to_snd_ivtv_card(struct v4l2_device *v4l2_dev) | |
71 | { | |
72 | return to_ivtv(v4l2_dev)->alsa; | |
73 | } | |
74 | ||
75 | static inline | |
76 | struct snd_ivtv_card *p_to_snd_ivtv_card(struct v4l2_device **v4l2_dev) | |
77 | { | |
78 | return container_of(v4l2_dev, struct snd_ivtv_card, v4l2_dev); | |
79 | } | |
80 | ||
81 | static void snd_ivtv_card_free(struct snd_ivtv_card *itvsc) | |
82 | { | |
83 | if (itvsc == NULL) | |
84 | return; | |
85 | ||
86 | if (itvsc->v4l2_dev != NULL) | |
87 | to_ivtv(itvsc->v4l2_dev)->alsa = NULL; | |
88 | ||
89 | /* FIXME - take any other stopping actions needed */ | |
90 | ||
91 | kfree(itvsc); | |
92 | } | |
93 | ||
94 | static void snd_ivtv_card_private_free(struct snd_card *sc) | |
95 | { | |
96 | if (sc == NULL) | |
97 | return; | |
98 | snd_ivtv_card_free(sc->private_data); | |
99 | sc->private_data = NULL; | |
100 | sc->private_free = NULL; | |
101 | } | |
102 | ||
103 | static int snd_ivtv_card_create(struct v4l2_device *v4l2_dev, | |
104 | struct snd_card *sc, | |
105 | struct snd_ivtv_card **itvsc) | |
106 | { | |
107 | *itvsc = kzalloc(sizeof(struct snd_ivtv_card), GFP_KERNEL); | |
108 | if (*itvsc == NULL) | |
109 | return -ENOMEM; | |
110 | ||
111 | (*itvsc)->v4l2_dev = v4l2_dev; | |
112 | (*itvsc)->sc = sc; | |
113 | ||
114 | sc->private_data = *itvsc; | |
115 | sc->private_free = snd_ivtv_card_private_free; | |
116 | ||
117 | return 0; | |
118 | } | |
119 | ||
120 | static int snd_ivtv_card_set_names(struct snd_ivtv_card *itvsc) | |
121 | { | |
122 | struct ivtv *itv = to_ivtv(itvsc->v4l2_dev); | |
123 | struct snd_card *sc = itvsc->sc; | |
124 | ||
125 | /* sc->driver is used by alsa-lib's configurator: simple, unique */ | |
126 | strlcpy(sc->driver, "CX2341[56]", sizeof(sc->driver)); | |
127 | ||
128 | /* sc->shortname is a symlink in /proc/asound: IVTV-M -> cardN */ | |
129 | snprintf(sc->shortname, sizeof(sc->shortname), "IVTV-%d", | |
130 | itv->instance); | |
131 | ||
132 | /* sc->longname is read from /proc/asound/cards */ | |
133 | snprintf(sc->longname, sizeof(sc->longname), | |
134 | "CX2341[56] #%d %s TV/FM Radio/Line-In Capture", | |
135 | itv->instance, itv->card_name); | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
140 | static int snd_ivtv_init(struct v4l2_device *v4l2_dev) | |
141 | { | |
142 | struct ivtv *itv = to_ivtv(v4l2_dev); | |
143 | struct snd_card *sc = NULL; | |
144 | struct snd_ivtv_card *itvsc; | |
27315059 | 145 | int ret, idx; |
269c11fb AW |
146 | |
147 | /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ | |
148 | ||
149 | /* (1) Check and increment the device index */ | |
150 | /* This is a no-op for us. We'll use the itv->instance */ | |
151 | ||
152 | /* (2) Create a card instance */ | |
27315059 NS |
153 | /* use first available id if not specified otherwise*/ |
154 | idx = index[itv->instance] == -1 ? SNDRV_DEFAULT_IDX1 : index[itv->instance]; | |
e7356888 | 155 | ret = snd_card_new(&itv->pdev->dev, |
27315059 | 156 | idx, |
e7356888 TI |
157 | SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ |
158 | THIS_MODULE, 0, &sc); | |
269c11fb | 159 | if (ret) { |
e7356888 | 160 | IVTV_ALSA_ERR("%s: snd_card_new() failed with err %d\n", |
269c11fb AW |
161 | __func__, ret); |
162 | goto err_exit; | |
163 | } | |
164 | ||
165 | /* (3) Create a main component */ | |
166 | ret = snd_ivtv_card_create(v4l2_dev, sc, &itvsc); | |
167 | if (ret) { | |
168 | IVTV_ALSA_ERR("%s: snd_ivtv_card_create() failed with err %d\n", | |
169 | __func__, ret); | |
170 | goto err_exit_free; | |
171 | } | |
172 | ||
173 | /* (4) Set the driver ID and name strings */ | |
174 | snd_ivtv_card_set_names(itvsc); | |
175 | ||
176 | /* (5) Create other components: mixer, PCM, & proc files */ | |
177 | #if 0 | |
178 | ret = snd_ivtv_mixer_create(itvsc); | |
179 | if (ret) { | |
180 | IVTV_ALSA_WARN("%s: snd_ivtv_mixer_create() failed with err %d:" | |
181 | " proceeding anyway\n", __func__, ret); | |
182 | } | |
183 | #endif | |
184 | ||
185 | ret = snd_ivtv_pcm_create(itvsc); | |
186 | if (ret) { | |
187 | IVTV_ALSA_ERR("%s: snd_ivtv_pcm_create() failed with err %d\n", | |
188 | __func__, ret); | |
189 | goto err_exit_free; | |
190 | } | |
191 | /* FIXME - proc files */ | |
192 | ||
193 | /* (7) Set the driver data and return 0 */ | |
194 | /* We do this out of normal order for PCI drivers to avoid races */ | |
195 | itv->alsa = itvsc; | |
196 | ||
197 | /* (6) Register the card instance */ | |
198 | ret = snd_card_register(sc); | |
199 | if (ret) { | |
200 | itv->alsa = NULL; | |
201 | IVTV_ALSA_ERR("%s: snd_card_register() failed with err %d\n", | |
202 | __func__, ret); | |
203 | goto err_exit_free; | |
204 | } | |
205 | ||
27315059 NS |
206 | IVTV_ALSA_INFO("%s: Instance %d registered as ALSA card %d\n", |
207 | __func__, itv->instance, sc->number); | |
208 | ||
269c11fb AW |
209 | return 0; |
210 | ||
211 | err_exit_free: | |
212 | if (sc != NULL) | |
213 | snd_card_free(sc); | |
214 | kfree(itvsc); | |
215 | err_exit: | |
216 | return ret; | |
217 | } | |
218 | ||
cfb046cb | 219 | static int ivtv_alsa_load(struct ivtv *itv) |
269c11fb AW |
220 | { |
221 | struct v4l2_device *v4l2_dev = &itv->v4l2_dev; | |
222 | struct ivtv_stream *s; | |
223 | ||
224 | if (v4l2_dev == NULL) { | |
225 | pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n", | |
226 | __func__); | |
227 | return 0; | |
228 | } | |
229 | ||
230 | itv = to_ivtv(v4l2_dev); | |
231 | if (itv == NULL) { | |
232 | pr_err("ivtv-alsa itv is NULL\n"); | |
233 | return 0; | |
234 | } | |
235 | ||
236 | s = &itv->streams[IVTV_ENC_STREAM_TYPE_PCM]; | |
635d62f0 | 237 | if (s->vdev.v4l2_dev == NULL) { |
269c11fb AW |
238 | IVTV_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - " |
239 | "skipping\n", __func__); | |
240 | return 0; | |
241 | } | |
242 | ||
243 | if (itv->alsa != NULL) { | |
244 | IVTV_ALSA_ERR("%s: struct snd_ivtv_card * already exists\n", | |
245 | __func__); | |
246 | return 0; | |
247 | } | |
248 | ||
249 | if (snd_ivtv_init(v4l2_dev)) { | |
250 | IVTV_ALSA_ERR("%s: failed to create struct snd_ivtv_card\n", | |
251 | __func__); | |
252 | } else { | |
253 | IVTV_DEBUG_ALSA_INFO("%s: created ivtv ALSA interface instance " | |
254 | "\n", __func__); | |
255 | } | |
256 | return 0; | |
257 | } | |
258 | ||
259 | static int __init ivtv_alsa_init(void) | |
260 | { | |
261 | pr_info("ivtv-alsa: module loading...\n"); | |
262 | ivtv_ext_init = &ivtv_alsa_load; | |
263 | return 0; | |
264 | } | |
265 | ||
266 | static void __exit snd_ivtv_exit(struct snd_ivtv_card *itvsc) | |
267 | { | |
268 | struct ivtv *itv = to_ivtv(itvsc->v4l2_dev); | |
269 | ||
270 | /* FIXME - pointer checks & shutdown itvsc */ | |
271 | ||
272 | snd_card_free(itvsc->sc); | |
273 | itv->alsa = NULL; | |
274 | } | |
275 | ||
276 | static int __exit ivtv_alsa_exit_callback(struct device *dev, void *data) | |
277 | { | |
278 | struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); | |
279 | struct snd_ivtv_card *itvsc; | |
280 | ||
281 | if (v4l2_dev == NULL) { | |
282 | pr_err("ivtv-alsa: %s: struct v4l2_device * is NULL\n", | |
283 | __func__); | |
284 | return 0; | |
285 | } | |
286 | ||
287 | itvsc = to_snd_ivtv_card(v4l2_dev); | |
288 | if (itvsc == NULL) { | |
289 | IVTV_ALSA_WARN("%s: struct snd_ivtv_card * is NULL\n", | |
290 | __func__); | |
291 | return 0; | |
292 | } | |
293 | ||
294 | snd_ivtv_exit(itvsc); | |
295 | return 0; | |
296 | } | |
297 | ||
298 | static void __exit ivtv_alsa_exit(void) | |
299 | { | |
300 | struct device_driver *drv; | |
301 | int ret; | |
302 | ||
303 | pr_info("ivtv-alsa: module unloading...\n"); | |
304 | ||
305 | drv = driver_find("ivtv", &pci_bus_type); | |
306 | ret = driver_for_each_device(drv, NULL, NULL, ivtv_alsa_exit_callback); | |
307 | (void)ret; /* suppress compiler warning */ | |
308 | ||
309 | ivtv_ext_init = NULL; | |
310 | pr_info("ivtv-alsa: module unload complete\n"); | |
311 | } | |
312 | ||
313 | module_init(ivtv_alsa_init); | |
314 | module_exit(ivtv_alsa_exit); |