diff options
Diffstat (limited to 'sound/drivers/dummy.c')
-rw-r--r-- | sound/drivers/dummy.c | 700 |
1 files changed, 537 insertions, 163 deletions
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c index 54239d2e0997..6ba066c41d2e 100644 --- a/sound/drivers/dummy.c +++ b/sound/drivers/dummy.c | |||
@@ -25,12 +25,15 @@ | |||
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/time.h> | 26 | #include <linux/time.h> |
27 | #include <linux/wait.h> | 27 | #include <linux/wait.h> |
28 | #include <linux/hrtimer.h> | ||
29 | #include <linux/math64.h> | ||
28 | #include <linux/moduleparam.h> | 30 | #include <linux/moduleparam.h> |
29 | #include <sound/core.h> | 31 | #include <sound/core.h> |
30 | #include <sound/control.h> | 32 | #include <sound/control.h> |
31 | #include <sound/tlv.h> | 33 | #include <sound/tlv.h> |
32 | #include <sound/pcm.h> | 34 | #include <sound/pcm.h> |
33 | #include <sound/rawmidi.h> | 35 | #include <sound/rawmidi.h> |
36 | #include <sound/info.h> | ||
34 | #include <sound/initval.h> | 37 | #include <sound/initval.h> |
35 | 38 | ||
36 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); | 39 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
@@ -39,7 +42,7 @@ MODULE_LICENSE("GPL"); | |||
39 | MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}"); | 42 | MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}"); |
40 | 43 | ||
41 | #define MAX_PCM_DEVICES 4 | 44 | #define MAX_PCM_DEVICES 4 |
42 | #define MAX_PCM_SUBSTREAMS 16 | 45 | #define MAX_PCM_SUBSTREAMS 128 |
43 | #define MAX_MIDI_DEVICES 2 | 46 | #define MAX_MIDI_DEVICES 2 |
44 | 47 | ||
45 | #if 0 /* emu10k1 emulation */ | 48 | #if 0 /* emu10k1 emulation */ |
@@ -148,6 +151,10 @@ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0}; | |||
148 | static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | 151 | static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; |
149 | static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; | 152 | static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; |
150 | //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; | 153 | //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; |
154 | #ifdef CONFIG_HIGH_RES_TIMERS | ||
155 | static int hrtimer = 1; | ||
156 | #endif | ||
157 | static int fake_buffer = 1; | ||
151 | 158 | ||
152 | module_param_array(index, int, NULL, 0444); | 159 | module_param_array(index, int, NULL, 0444); |
153 | MODULE_PARM_DESC(index, "Index value for dummy soundcard."); | 160 | MODULE_PARM_DESC(index, "Index value for dummy soundcard."); |
@@ -161,6 +168,12 @@ module_param_array(pcm_substreams, int, NULL, 0444); | |||
161 | MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); | 168 | MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); |
162 | //module_param_array(midi_devs, int, NULL, 0444); | 169 | //module_param_array(midi_devs, int, NULL, 0444); |
163 | //MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); | 170 | //MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); |
171 | module_param(fake_buffer, bool, 0444); | ||
172 | MODULE_PARM_DESC(fake_buffer, "Fake buffer allocations."); | ||
173 | #ifdef CONFIG_HIGH_RES_TIMERS | ||
174 | module_param(hrtimer, bool, 0644); | ||
175 | MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source."); | ||
176 | #endif | ||
164 | 177 | ||
165 | static struct platform_device *devices[SNDRV_CARDS]; | 178 | static struct platform_device *devices[SNDRV_CARDS]; |
166 | 179 | ||
@@ -171,137 +184,324 @@ static struct platform_device *devices[SNDRV_CARDS]; | |||
171 | #define MIXER_ADDR_CD 4 | 184 | #define MIXER_ADDR_CD 4 |
172 | #define MIXER_ADDR_LAST 4 | 185 | #define MIXER_ADDR_LAST 4 |
173 | 186 | ||
187 | struct dummy_timer_ops { | ||
188 | int (*create)(struct snd_pcm_substream *); | ||
189 | void (*free)(struct snd_pcm_substream *); | ||
190 | int (*prepare)(struct snd_pcm_substream *); | ||
191 | int (*start)(struct snd_pcm_substream *); | ||
192 | int (*stop)(struct snd_pcm_substream *); | ||
193 | snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *); | ||
194 | }; | ||
195 | |||
174 | struct snd_dummy { | 196 | struct snd_dummy { |
175 | struct snd_card *card; | 197 | struct snd_card *card; |
176 | struct snd_pcm *pcm; | 198 | struct snd_pcm *pcm; |
177 | spinlock_t mixer_lock; | 199 | spinlock_t mixer_lock; |
178 | int mixer_volume[MIXER_ADDR_LAST+1][2]; | 200 | int mixer_volume[MIXER_ADDR_LAST+1][2]; |
179 | int capture_source[MIXER_ADDR_LAST+1][2]; | 201 | int capture_source[MIXER_ADDR_LAST+1][2]; |
202 | const struct dummy_timer_ops *timer_ops; | ||
180 | }; | 203 | }; |
181 | 204 | ||
182 | struct snd_dummy_pcm { | 205 | /* |
183 | struct snd_dummy *dummy; | 206 | * system timer interface |
207 | */ | ||
208 | |||
209 | struct dummy_systimer_pcm { | ||
184 | spinlock_t lock; | 210 | spinlock_t lock; |
185 | struct timer_list timer; | 211 | struct timer_list timer; |
186 | unsigned int pcm_buffer_size; | 212 | unsigned long base_time; |
187 | unsigned int pcm_period_size; | 213 | unsigned int frac_pos; /* fractional sample position (based HZ) */ |
188 | unsigned int pcm_bps; /* bytes per second */ | 214 | unsigned int frac_period_rest; |
189 | unsigned int pcm_hz; /* HZ */ | 215 | unsigned int frac_buffer_size; /* buffer_size * HZ */ |
190 | unsigned int pcm_irq_pos; /* IRQ position */ | 216 | unsigned int frac_period_size; /* period_size * HZ */ |
191 | unsigned int pcm_buf_pos; /* position in buffer */ | 217 | unsigned int rate; |
218 | int elapsed; | ||
192 | struct snd_pcm_substream *substream; | 219 | struct snd_pcm_substream *substream; |
193 | }; | 220 | }; |
194 | 221 | ||
195 | 222 | static void dummy_systimer_rearm(struct dummy_systimer_pcm *dpcm) | |
196 | static inline void snd_card_dummy_pcm_timer_start(struct snd_dummy_pcm *dpcm) | ||
197 | { | 223 | { |
198 | dpcm->timer.expires = 1 + jiffies; | 224 | dpcm->timer.expires = jiffies + |
225 | (dpcm->frac_period_rest + dpcm->rate - 1) / dpcm->rate; | ||
199 | add_timer(&dpcm->timer); | 226 | add_timer(&dpcm->timer); |
200 | } | 227 | } |
201 | 228 | ||
202 | static inline void snd_card_dummy_pcm_timer_stop(struct snd_dummy_pcm *dpcm) | 229 | static void dummy_systimer_update(struct dummy_systimer_pcm *dpcm) |
203 | { | 230 | { |
204 | del_timer(&dpcm->timer); | 231 | unsigned long delta; |
232 | |||
233 | delta = jiffies - dpcm->base_time; | ||
234 | if (!delta) | ||
235 | return; | ||
236 | dpcm->base_time += delta; | ||
237 | delta *= dpcm->rate; | ||
238 | dpcm->frac_pos += delta; | ||
239 | while (dpcm->frac_pos >= dpcm->frac_buffer_size) | ||
240 | dpcm->frac_pos -= dpcm->frac_buffer_size; | ||
241 | while (dpcm->frac_period_rest <= delta) { | ||
242 | dpcm->elapsed++; | ||
243 | dpcm->frac_period_rest += dpcm->frac_period_size; | ||
244 | } | ||
245 | dpcm->frac_period_rest -= delta; | ||
205 | } | 246 | } |
206 | 247 | ||
207 | static int snd_card_dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | 248 | static int dummy_systimer_start(struct snd_pcm_substream *substream) |
208 | { | 249 | { |
209 | struct snd_pcm_runtime *runtime = substream->runtime; | 250 | struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; |
210 | struct snd_dummy_pcm *dpcm = runtime->private_data; | 251 | spin_lock(&dpcm->lock); |
211 | int err = 0; | 252 | dpcm->base_time = jiffies; |
253 | dummy_systimer_rearm(dpcm); | ||
254 | spin_unlock(&dpcm->lock); | ||
255 | return 0; | ||
256 | } | ||
212 | 257 | ||
258 | static int dummy_systimer_stop(struct snd_pcm_substream *substream) | ||
259 | { | ||
260 | struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; | ||
213 | spin_lock(&dpcm->lock); | 261 | spin_lock(&dpcm->lock); |
214 | switch (cmd) { | 262 | del_timer(&dpcm->timer); |
215 | case SNDRV_PCM_TRIGGER_START: | ||
216 | case SNDRV_PCM_TRIGGER_RESUME: | ||
217 | snd_card_dummy_pcm_timer_start(dpcm); | ||
218 | break; | ||
219 | case SNDRV_PCM_TRIGGER_STOP: | ||
220 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
221 | snd_card_dummy_pcm_timer_stop(dpcm); | ||
222 | break; | ||
223 | default: | ||
224 | err = -EINVAL; | ||
225 | break; | ||
226 | } | ||
227 | spin_unlock(&dpcm->lock); | 263 | spin_unlock(&dpcm->lock); |
228 | return 0; | 264 | return 0; |
229 | } | 265 | } |
230 | 266 | ||
231 | static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream) | 267 | static int dummy_systimer_prepare(struct snd_pcm_substream *substream) |
232 | { | 268 | { |
233 | struct snd_pcm_runtime *runtime = substream->runtime; | 269 | struct snd_pcm_runtime *runtime = substream->runtime; |
234 | struct snd_dummy_pcm *dpcm = runtime->private_data; | 270 | struct dummy_systimer_pcm *dpcm = runtime->private_data; |
235 | int bps; | ||
236 | |||
237 | bps = snd_pcm_format_width(runtime->format) * runtime->rate * | ||
238 | runtime->channels / 8; | ||
239 | |||
240 | if (bps <= 0) | ||
241 | return -EINVAL; | ||
242 | |||
243 | dpcm->pcm_bps = bps; | ||
244 | dpcm->pcm_hz = HZ; | ||
245 | dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream); | ||
246 | dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream); | ||
247 | dpcm->pcm_irq_pos = 0; | ||
248 | dpcm->pcm_buf_pos = 0; | ||
249 | 271 | ||
250 | snd_pcm_format_set_silence(runtime->format, runtime->dma_area, | 272 | dpcm->frac_pos = 0; |
251 | bytes_to_samples(runtime, runtime->dma_bytes)); | 273 | dpcm->rate = runtime->rate; |
274 | dpcm->frac_buffer_size = runtime->buffer_size * HZ; | ||
275 | dpcm->frac_period_size = runtime->period_size * HZ; | ||
276 | dpcm->frac_period_rest = dpcm->frac_period_size; | ||
277 | dpcm->elapsed = 0; | ||
252 | 278 | ||
253 | return 0; | 279 | return 0; |
254 | } | 280 | } |
255 | 281 | ||
256 | static void snd_card_dummy_pcm_timer_function(unsigned long data) | 282 | static void dummy_systimer_callback(unsigned long data) |
257 | { | 283 | { |
258 | struct snd_dummy_pcm *dpcm = (struct snd_dummy_pcm *)data; | 284 | struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data; |
259 | unsigned long flags; | 285 | unsigned long flags; |
286 | int elapsed = 0; | ||
260 | 287 | ||
261 | spin_lock_irqsave(&dpcm->lock, flags); | 288 | spin_lock_irqsave(&dpcm->lock, flags); |
262 | dpcm->timer.expires = 1 + jiffies; | 289 | dummy_systimer_update(dpcm); |
263 | add_timer(&dpcm->timer); | 290 | dummy_systimer_rearm(dpcm); |
264 | dpcm->pcm_irq_pos += dpcm->pcm_bps; | 291 | elapsed = dpcm->elapsed; |
265 | dpcm->pcm_buf_pos += dpcm->pcm_bps; | 292 | dpcm->elapsed = 0; |
266 | dpcm->pcm_buf_pos %= dpcm->pcm_buffer_size * dpcm->pcm_hz; | 293 | spin_unlock_irqrestore(&dpcm->lock, flags); |
267 | if (dpcm->pcm_irq_pos >= dpcm->pcm_period_size * dpcm->pcm_hz) { | 294 | if (elapsed) |
268 | dpcm->pcm_irq_pos %= dpcm->pcm_period_size * dpcm->pcm_hz; | 295 | snd_pcm_period_elapsed(dpcm->substream); |
269 | spin_unlock_irqrestore(&dpcm->lock, flags); | 296 | } |
297 | |||
298 | static snd_pcm_uframes_t | ||
299 | dummy_systimer_pointer(struct snd_pcm_substream *substream) | ||
300 | { | ||
301 | struct dummy_systimer_pcm *dpcm = substream->runtime->private_data; | ||
302 | snd_pcm_uframes_t pos; | ||
303 | |||
304 | spin_lock(&dpcm->lock); | ||
305 | dummy_systimer_update(dpcm); | ||
306 | pos = dpcm->frac_pos / HZ; | ||
307 | spin_unlock(&dpcm->lock); | ||
308 | return pos; | ||
309 | } | ||
310 | |||
311 | static int dummy_systimer_create(struct snd_pcm_substream *substream) | ||
312 | { | ||
313 | struct dummy_systimer_pcm *dpcm; | ||
314 | |||
315 | dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | ||
316 | if (!dpcm) | ||
317 | return -ENOMEM; | ||
318 | substream->runtime->private_data = dpcm; | ||
319 | init_timer(&dpcm->timer); | ||
320 | dpcm->timer.data = (unsigned long) dpcm; | ||
321 | dpcm->timer.function = dummy_systimer_callback; | ||
322 | spin_lock_init(&dpcm->lock); | ||
323 | dpcm->substream = substream; | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | static void dummy_systimer_free(struct snd_pcm_substream *substream) | ||
328 | { | ||
329 | kfree(substream->runtime->private_data); | ||
330 | } | ||
331 | |||
332 | static struct dummy_timer_ops dummy_systimer_ops = { | ||
333 | .create = dummy_systimer_create, | ||
334 | .free = dummy_systimer_free, | ||
335 | .prepare = dummy_systimer_prepare, | ||
336 | .start = dummy_systimer_start, | ||
337 | .stop = dummy_systimer_stop, | ||
338 | .pointer = dummy_systimer_pointer, | ||
339 | }; | ||
340 | |||
341 | #ifdef CONFIG_HIGH_RES_TIMERS | ||
342 | /* | ||
343 | * hrtimer interface | ||
344 | */ | ||
345 | |||
346 | struct dummy_hrtimer_pcm { | ||
347 | ktime_t base_time; | ||
348 | ktime_t period_time; | ||
349 | atomic_t running; | ||
350 | struct hrtimer timer; | ||
351 | struct tasklet_struct tasklet; | ||
352 | struct snd_pcm_substream *substream; | ||
353 | }; | ||
354 | |||
355 | static void dummy_hrtimer_pcm_elapsed(unsigned long priv) | ||
356 | { | ||
357 | struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv; | ||
358 | if (atomic_read(&dpcm->running)) | ||
270 | snd_pcm_period_elapsed(dpcm->substream); | 359 | snd_pcm_period_elapsed(dpcm->substream); |
271 | } else | ||
272 | spin_unlock_irqrestore(&dpcm->lock, flags); | ||
273 | } | 360 | } |
274 | 361 | ||
275 | static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *substream) | 362 | static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer) |
363 | { | ||
364 | struct dummy_hrtimer_pcm *dpcm; | ||
365 | |||
366 | dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer); | ||
367 | if (!atomic_read(&dpcm->running)) | ||
368 | return HRTIMER_NORESTART; | ||
369 | tasklet_schedule(&dpcm->tasklet); | ||
370 | hrtimer_forward_now(timer, dpcm->period_time); | ||
371 | return HRTIMER_RESTART; | ||
372 | } | ||
373 | |||
374 | static int dummy_hrtimer_start(struct snd_pcm_substream *substream) | ||
375 | { | ||
376 | struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; | ||
377 | |||
378 | dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer); | ||
379 | hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL); | ||
380 | atomic_set(&dpcm->running, 1); | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | static int dummy_hrtimer_stop(struct snd_pcm_substream *substream) | ||
385 | { | ||
386 | struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; | ||
387 | |||
388 | atomic_set(&dpcm->running, 0); | ||
389 | hrtimer_cancel(&dpcm->timer); | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm) | ||
394 | { | ||
395 | tasklet_kill(&dpcm->tasklet); | ||
396 | } | ||
397 | |||
398 | static snd_pcm_uframes_t | ||
399 | dummy_hrtimer_pointer(struct snd_pcm_substream *substream) | ||
276 | { | 400 | { |
277 | struct snd_pcm_runtime *runtime = substream->runtime; | 401 | struct snd_pcm_runtime *runtime = substream->runtime; |
278 | struct snd_dummy_pcm *dpcm = runtime->private_data; | 402 | struct dummy_hrtimer_pcm *dpcm = runtime->private_data; |
403 | u64 delta; | ||
404 | u32 pos; | ||
405 | |||
406 | delta = ktime_us_delta(hrtimer_cb_get_time(&dpcm->timer), | ||
407 | dpcm->base_time); | ||
408 | delta = div_u64(delta * runtime->rate + 999999, 1000000); | ||
409 | div_u64_rem(delta, runtime->buffer_size, &pos); | ||
410 | return pos; | ||
411 | } | ||
279 | 412 | ||
280 | return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz); | 413 | static int dummy_hrtimer_prepare(struct snd_pcm_substream *substream) |
414 | { | ||
415 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
416 | struct dummy_hrtimer_pcm *dpcm = runtime->private_data; | ||
417 | unsigned int period, rate; | ||
418 | long sec; | ||
419 | unsigned long nsecs; | ||
420 | |||
421 | dummy_hrtimer_sync(dpcm); | ||
422 | period = runtime->period_size; | ||
423 | rate = runtime->rate; | ||
424 | sec = period / rate; | ||
425 | period %= rate; | ||
426 | nsecs = div_u64((u64)period * 1000000000UL + rate - 1, rate); | ||
427 | dpcm->period_time = ktime_set(sec, nsecs); | ||
428 | |||
429 | return 0; | ||
281 | } | 430 | } |
282 | 431 | ||
283 | static struct snd_pcm_hardware snd_card_dummy_playback = | 432 | static int dummy_hrtimer_create(struct snd_pcm_substream *substream) |
284 | { | 433 | { |
285 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | 434 | struct dummy_hrtimer_pcm *dpcm; |
286 | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), | 435 | |
287 | .formats = USE_FORMATS, | 436 | dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); |
288 | .rates = USE_RATE, | 437 | if (!dpcm) |
289 | .rate_min = USE_RATE_MIN, | 438 | return -ENOMEM; |
290 | .rate_max = USE_RATE_MAX, | 439 | substream->runtime->private_data = dpcm; |
291 | .channels_min = USE_CHANNELS_MIN, | 440 | hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); |
292 | .channels_max = USE_CHANNELS_MAX, | 441 | dpcm->timer.function = dummy_hrtimer_callback; |
293 | .buffer_bytes_max = MAX_BUFFER_SIZE, | 442 | dpcm->substream = substream; |
294 | .period_bytes_min = 64, | 443 | atomic_set(&dpcm->running, 0); |
295 | .period_bytes_max = MAX_PERIOD_SIZE, | 444 | tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed, |
296 | .periods_min = USE_PERIODS_MIN, | 445 | (unsigned long)dpcm); |
297 | .periods_max = USE_PERIODS_MAX, | 446 | return 0; |
298 | .fifo_size = 0, | 447 | } |
448 | |||
449 | static void dummy_hrtimer_free(struct snd_pcm_substream *substream) | ||
450 | { | ||
451 | struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data; | ||
452 | dummy_hrtimer_sync(dpcm); | ||
453 | kfree(dpcm); | ||
454 | } | ||
455 | |||
456 | static struct dummy_timer_ops dummy_hrtimer_ops = { | ||
457 | .create = dummy_hrtimer_create, | ||
458 | .free = dummy_hrtimer_free, | ||
459 | .prepare = dummy_hrtimer_prepare, | ||
460 | .start = dummy_hrtimer_start, | ||
461 | .stop = dummy_hrtimer_stop, | ||
462 | .pointer = dummy_hrtimer_pointer, | ||
299 | }; | 463 | }; |
300 | 464 | ||
301 | static struct snd_pcm_hardware snd_card_dummy_capture = | 465 | #endif /* CONFIG_HIGH_RES_TIMERS */ |
466 | |||
467 | /* | ||
468 | * PCM interface | ||
469 | */ | ||
470 | |||
471 | static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
302 | { | 472 | { |
303 | .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | | 473 | struct snd_dummy *dummy = snd_pcm_substream_chip(substream); |
304 | SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), | 474 | |
475 | switch (cmd) { | ||
476 | case SNDRV_PCM_TRIGGER_START: | ||
477 | case SNDRV_PCM_TRIGGER_RESUME: | ||
478 | return dummy->timer_ops->start(substream); | ||
479 | case SNDRV_PCM_TRIGGER_STOP: | ||
480 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
481 | return dummy->timer_ops->stop(substream); | ||
482 | } | ||
483 | return -EINVAL; | ||
484 | } | ||
485 | |||
486 | static int dummy_pcm_prepare(struct snd_pcm_substream *substream) | ||
487 | { | ||
488 | struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||
489 | |||
490 | return dummy->timer_ops->prepare(substream); | ||
491 | } | ||
492 | |||
493 | static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream) | ||
494 | { | ||
495 | struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||
496 | |||
497 | return dummy->timer_ops->pointer(substream); | ||
498 | } | ||
499 | |||
500 | static struct snd_pcm_hardware dummy_pcm_hardware = { | ||
501 | .info = (SNDRV_PCM_INFO_MMAP | | ||
502 | SNDRV_PCM_INFO_INTERLEAVED | | ||
503 | SNDRV_PCM_INFO_RESUME | | ||
504 | SNDRV_PCM_INFO_MMAP_VALID), | ||
305 | .formats = USE_FORMATS, | 505 | .formats = USE_FORMATS, |
306 | .rates = USE_RATE, | 506 | .rates = USE_RATE, |
307 | .rate_min = USE_RATE_MIN, | 507 | .rate_min = USE_RATE_MIN, |
@@ -316,123 +516,152 @@ static struct snd_pcm_hardware snd_card_dummy_capture = | |||
316 | .fifo_size = 0, | 516 | .fifo_size = 0, |
317 | }; | 517 | }; |
318 | 518 | ||
319 | static void snd_card_dummy_runtime_free(struct snd_pcm_runtime *runtime) | 519 | static int dummy_pcm_hw_params(struct snd_pcm_substream *substream, |
320 | { | 520 | struct snd_pcm_hw_params *hw_params) |
321 | kfree(runtime->private_data); | ||
322 | } | ||
323 | |||
324 | static int snd_card_dummy_hw_params(struct snd_pcm_substream *substream, | ||
325 | struct snd_pcm_hw_params *hw_params) | ||
326 | { | 521 | { |
327 | return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); | 522 | if (fake_buffer) { |
523 | /* runtime->dma_bytes has to be set manually to allow mmap */ | ||
524 | substream->runtime->dma_bytes = params_buffer_bytes(hw_params); | ||
525 | return 0; | ||
526 | } | ||
527 | return snd_pcm_lib_malloc_pages(substream, | ||
528 | params_buffer_bytes(hw_params)); | ||
328 | } | 529 | } |
329 | 530 | ||
330 | static int snd_card_dummy_hw_free(struct snd_pcm_substream *substream) | 531 | static int dummy_pcm_hw_free(struct snd_pcm_substream *substream) |
331 | { | 532 | { |
533 | if (fake_buffer) | ||
534 | return 0; | ||
332 | return snd_pcm_lib_free_pages(substream); | 535 | return snd_pcm_lib_free_pages(substream); |
333 | } | 536 | } |
334 | 537 | ||
335 | static struct snd_dummy_pcm *new_pcm_stream(struct snd_pcm_substream *substream) | 538 | static int dummy_pcm_open(struct snd_pcm_substream *substream) |
336 | { | ||
337 | struct snd_dummy_pcm *dpcm; | ||
338 | |||
339 | dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL); | ||
340 | if (! dpcm) | ||
341 | return dpcm; | ||
342 | init_timer(&dpcm->timer); | ||
343 | dpcm->timer.data = (unsigned long) dpcm; | ||
344 | dpcm->timer.function = snd_card_dummy_pcm_timer_function; | ||
345 | spin_lock_init(&dpcm->lock); | ||
346 | dpcm->substream = substream; | ||
347 | return dpcm; | ||
348 | } | ||
349 | |||
350 | static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream) | ||
351 | { | 539 | { |
540 | struct snd_dummy *dummy = snd_pcm_substream_chip(substream); | ||
352 | struct snd_pcm_runtime *runtime = substream->runtime; | 541 | struct snd_pcm_runtime *runtime = substream->runtime; |
353 | struct snd_dummy_pcm *dpcm; | ||
354 | int err; | 542 | int err; |
355 | 543 | ||
356 | if ((dpcm = new_pcm_stream(substream)) == NULL) | 544 | dummy->timer_ops = &dummy_systimer_ops; |
357 | return -ENOMEM; | 545 | #ifdef CONFIG_HIGH_RES_TIMERS |
358 | runtime->private_data = dpcm; | 546 | if (hrtimer) |
359 | /* makes the infrastructure responsible for freeing dpcm */ | 547 | dummy->timer_ops = &dummy_hrtimer_ops; |
360 | runtime->private_free = snd_card_dummy_runtime_free; | 548 | #endif |
361 | runtime->hw = snd_card_dummy_playback; | 549 | |
550 | err = dummy->timer_ops->create(substream); | ||
551 | if (err < 0) | ||
552 | return err; | ||
553 | |||
554 | runtime->hw = dummy_pcm_hardware; | ||
362 | if (substream->pcm->device & 1) { | 555 | if (substream->pcm->device & 1) { |
363 | runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; | 556 | runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; |
364 | runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; | 557 | runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; |
365 | } | 558 | } |
366 | if (substream->pcm->device & 2) | 559 | if (substream->pcm->device & 2) |
367 | runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); | 560 | runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP | |
368 | err = add_playback_constraints(runtime); | 561 | SNDRV_PCM_INFO_MMAP_VALID); |
369 | if (err < 0) | 562 | |
563 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
564 | err = add_playback_constraints(substream->runtime); | ||
565 | else | ||
566 | err = add_capture_constraints(substream->runtime); | ||
567 | if (err < 0) { | ||
568 | dummy->timer_ops->free(substream); | ||
370 | return err; | 569 | return err; |
371 | 570 | } | |
372 | return 0; | 571 | return 0; |
373 | } | 572 | } |
374 | 573 | ||
375 | static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream) | 574 | static int dummy_pcm_close(struct snd_pcm_substream *substream) |
376 | { | 575 | { |
377 | struct snd_pcm_runtime *runtime = substream->runtime; | 576 | struct snd_dummy *dummy = snd_pcm_substream_chip(substream); |
378 | struct snd_dummy_pcm *dpcm; | 577 | dummy->timer_ops->free(substream); |
379 | int err; | 578 | return 0; |
579 | } | ||
380 | 580 | ||
381 | if ((dpcm = new_pcm_stream(substream)) == NULL) | 581 | /* |
382 | return -ENOMEM; | 582 | * dummy buffer handling |
383 | runtime->private_data = dpcm; | 583 | */ |
384 | /* makes the infrastructure responsible for freeing dpcm */ | 584 | |
385 | runtime->private_free = snd_card_dummy_runtime_free; | 585 | static void *dummy_page[2]; |
386 | runtime->hw = snd_card_dummy_capture; | 586 | |
387 | if (substream->pcm->device == 1) { | 587 | static void free_fake_buffer(void) |
388 | runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; | 588 | { |
389 | runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; | 589 | if (fake_buffer) { |
590 | int i; | ||
591 | for (i = 0; i < 2; i++) | ||
592 | if (dummy_page[i]) { | ||
593 | free_page((unsigned long)dummy_page[i]); | ||
594 | dummy_page[i] = NULL; | ||
595 | } | ||
390 | } | 596 | } |
391 | if (substream->pcm->device & 2) | 597 | } |
392 | runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); | ||
393 | err = add_capture_constraints(runtime); | ||
394 | if (err < 0) | ||
395 | return err; | ||
396 | 598 | ||
599 | static int alloc_fake_buffer(void) | ||
600 | { | ||
601 | int i; | ||
602 | |||
603 | if (!fake_buffer) | ||
604 | return 0; | ||
605 | for (i = 0; i < 2; i++) { | ||
606 | dummy_page[i] = (void *)get_zeroed_page(GFP_KERNEL); | ||
607 | if (!dummy_page[i]) { | ||
608 | free_fake_buffer(); | ||
609 | return -ENOMEM; | ||
610 | } | ||
611 | } | ||
397 | return 0; | 612 | return 0; |
398 | } | 613 | } |
399 | 614 | ||
400 | static int snd_card_dummy_playback_close(struct snd_pcm_substream *substream) | 615 | static int dummy_pcm_copy(struct snd_pcm_substream *substream, |
616 | int channel, snd_pcm_uframes_t pos, | ||
617 | void __user *dst, snd_pcm_uframes_t count) | ||
401 | { | 618 | { |
402 | return 0; | 619 | return 0; /* do nothing */ |
403 | } | 620 | } |
404 | 621 | ||
405 | static int snd_card_dummy_capture_close(struct snd_pcm_substream *substream) | 622 | static int dummy_pcm_silence(struct snd_pcm_substream *substream, |
623 | int channel, snd_pcm_uframes_t pos, | ||
624 | snd_pcm_uframes_t count) | ||
406 | { | 625 | { |
407 | return 0; | 626 | return 0; /* do nothing */ |
627 | } | ||
628 | |||
629 | static struct page *dummy_pcm_page(struct snd_pcm_substream *substream, | ||
630 | unsigned long offset) | ||
631 | { | ||
632 | return virt_to_page(dummy_page[substream->stream]); /* the same page */ | ||
408 | } | 633 | } |
409 | 634 | ||
410 | static struct snd_pcm_ops snd_card_dummy_playback_ops = { | 635 | static struct snd_pcm_ops dummy_pcm_ops = { |
411 | .open = snd_card_dummy_playback_open, | 636 | .open = dummy_pcm_open, |
412 | .close = snd_card_dummy_playback_close, | 637 | .close = dummy_pcm_close, |
413 | .ioctl = snd_pcm_lib_ioctl, | 638 | .ioctl = snd_pcm_lib_ioctl, |
414 | .hw_params = snd_card_dummy_hw_params, | 639 | .hw_params = dummy_pcm_hw_params, |
415 | .hw_free = snd_card_dummy_hw_free, | 640 | .hw_free = dummy_pcm_hw_free, |
416 | .prepare = snd_card_dummy_pcm_prepare, | 641 | .prepare = dummy_pcm_prepare, |
417 | .trigger = snd_card_dummy_pcm_trigger, | 642 | .trigger = dummy_pcm_trigger, |
418 | .pointer = snd_card_dummy_pcm_pointer, | 643 | .pointer = dummy_pcm_pointer, |
419 | }; | 644 | }; |
420 | 645 | ||
421 | static struct snd_pcm_ops snd_card_dummy_capture_ops = { | 646 | static struct snd_pcm_ops dummy_pcm_ops_no_buf = { |
422 | .open = snd_card_dummy_capture_open, | 647 | .open = dummy_pcm_open, |
423 | .close = snd_card_dummy_capture_close, | 648 | .close = dummy_pcm_close, |
424 | .ioctl = snd_pcm_lib_ioctl, | 649 | .ioctl = snd_pcm_lib_ioctl, |
425 | .hw_params = snd_card_dummy_hw_params, | 650 | .hw_params = dummy_pcm_hw_params, |
426 | .hw_free = snd_card_dummy_hw_free, | 651 | .hw_free = dummy_pcm_hw_free, |
427 | .prepare = snd_card_dummy_pcm_prepare, | 652 | .prepare = dummy_pcm_prepare, |
428 | .trigger = snd_card_dummy_pcm_trigger, | 653 | .trigger = dummy_pcm_trigger, |
429 | .pointer = snd_card_dummy_pcm_pointer, | 654 | .pointer = dummy_pcm_pointer, |
655 | .copy = dummy_pcm_copy, | ||
656 | .silence = dummy_pcm_silence, | ||
657 | .page = dummy_pcm_page, | ||
430 | }; | 658 | }; |
431 | 659 | ||
432 | static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, | 660 | static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, |
433 | int substreams) | 661 | int substreams) |
434 | { | 662 | { |
435 | struct snd_pcm *pcm; | 663 | struct snd_pcm *pcm; |
664 | struct snd_pcm_ops *ops; | ||
436 | int err; | 665 | int err; |
437 | 666 | ||
438 | err = snd_pcm_new(dummy->card, "Dummy PCM", device, | 667 | err = snd_pcm_new(dummy->card, "Dummy PCM", device, |
@@ -440,17 +669,28 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, | |||
440 | if (err < 0) | 669 | if (err < 0) |
441 | return err; | 670 | return err; |
442 | dummy->pcm = pcm; | 671 | dummy->pcm = pcm; |
443 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops); | 672 | if (fake_buffer) |
444 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops); | 673 | ops = &dummy_pcm_ops_no_buf; |
674 | else | ||
675 | ops = &dummy_pcm_ops; | ||
676 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ops); | ||
677 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, ops); | ||
445 | pcm->private_data = dummy; | 678 | pcm->private_data = dummy; |
446 | pcm->info_flags = 0; | 679 | pcm->info_flags = 0; |
447 | strcpy(pcm->name, "Dummy PCM"); | 680 | strcpy(pcm->name, "Dummy PCM"); |
448 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | 681 | if (!fake_buffer) { |
449 | snd_dma_continuous_data(GFP_KERNEL), | 682 | snd_pcm_lib_preallocate_pages_for_all(pcm, |
450 | 0, 64*1024); | 683 | SNDRV_DMA_TYPE_CONTINUOUS, |
684 | snd_dma_continuous_data(GFP_KERNEL), | ||
685 | 0, 64*1024); | ||
686 | } | ||
451 | return 0; | 687 | return 0; |
452 | } | 688 | } |
453 | 689 | ||
690 | /* | ||
691 | * mixer interface | ||
692 | */ | ||
693 | |||
454 | #define DUMMY_VOLUME(xname, xindex, addr) \ | 694 | #define DUMMY_VOLUME(xname, xindex, addr) \ |
455 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | 695 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ |
456 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ | 696 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ |
@@ -581,6 +821,131 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy) | |||
581 | return 0; | 821 | return 0; |
582 | } | 822 | } |
583 | 823 | ||
824 | #if defined(CONFIG_SND_DEBUG) && defined(CONFIG_PROC_FS) | ||
825 | /* | ||
826 | * proc interface | ||
827 | */ | ||
828 | static void print_formats(struct snd_info_buffer *buffer) | ||
829 | { | ||
830 | int i; | ||
831 | |||
832 | for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { | ||
833 | if (dummy_pcm_hardware.formats & (1ULL << i)) | ||
834 | snd_iprintf(buffer, " %s", snd_pcm_format_name(i)); | ||
835 | } | ||
836 | } | ||
837 | |||
838 | static void print_rates(struct snd_info_buffer *buffer) | ||
839 | { | ||
840 | static int rates[] = { | ||
841 | 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, | ||
842 | 64000, 88200, 96000, 176400, 192000, | ||
843 | }; | ||
844 | int i; | ||
845 | |||
846 | if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_CONTINUOUS) | ||
847 | snd_iprintf(buffer, " continuous"); | ||
848 | if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_KNOT) | ||
849 | snd_iprintf(buffer, " knot"); | ||
850 | for (i = 0; i < ARRAY_SIZE(rates); i++) | ||
851 | if (dummy_pcm_hardware.rates & (1 << i)) | ||
852 | snd_iprintf(buffer, " %d", rates[i]); | ||
853 | } | ||
854 | |||
855 | #define get_dummy_int_ptr(ofs) \ | ||
856 | (unsigned int *)((char *)&dummy_pcm_hardware + (ofs)) | ||
857 | #define get_dummy_ll_ptr(ofs) \ | ||
858 | (unsigned long long *)((char *)&dummy_pcm_hardware + (ofs)) | ||
859 | |||
860 | struct dummy_hw_field { | ||
861 | const char *name; | ||
862 | const char *format; | ||
863 | unsigned int offset; | ||
864 | unsigned int size; | ||
865 | }; | ||
866 | #define FIELD_ENTRY(item, fmt) { \ | ||
867 | .name = #item, \ | ||
868 | .format = fmt, \ | ||
869 | .offset = offsetof(struct snd_pcm_hardware, item), \ | ||
870 | .size = sizeof(dummy_pcm_hardware.item) } | ||
871 | |||
872 | static struct dummy_hw_field fields[] = { | ||
873 | FIELD_ENTRY(formats, "%#llx"), | ||
874 | FIELD_ENTRY(rates, "%#x"), | ||
875 | FIELD_ENTRY(rate_min, "%d"), | ||
876 | FIELD_ENTRY(rate_max, "%d"), | ||
877 | FIELD_ENTRY(channels_min, "%d"), | ||
878 | FIELD_ENTRY(channels_max, "%d"), | ||
879 | FIELD_ENTRY(buffer_bytes_max, "%ld"), | ||
880 | FIELD_ENTRY(period_bytes_min, "%ld"), | ||
881 | FIELD_ENTRY(period_bytes_max, "%ld"), | ||
882 | FIELD_ENTRY(periods_min, "%d"), | ||
883 | FIELD_ENTRY(periods_max, "%d"), | ||
884 | }; | ||
885 | |||
886 | static void dummy_proc_read(struct snd_info_entry *entry, | ||
887 | struct snd_info_buffer *buffer) | ||
888 | { | ||
889 | int i; | ||
890 | |||
891 | for (i = 0; i < ARRAY_SIZE(fields); i++) { | ||
892 | snd_iprintf(buffer, "%s ", fields[i].name); | ||
893 | if (fields[i].size == sizeof(int)) | ||
894 | snd_iprintf(buffer, fields[i].format, | ||
895 | *get_dummy_int_ptr(fields[i].offset)); | ||
896 | else | ||
897 | snd_iprintf(buffer, fields[i].format, | ||
898 | *get_dummy_ll_ptr(fields[i].offset)); | ||
899 | if (!strcmp(fields[i].name, "formats")) | ||
900 | print_formats(buffer); | ||
901 | else if (!strcmp(fields[i].name, "rates")) | ||
902 | print_rates(buffer); | ||
903 | snd_iprintf(buffer, "\n"); | ||
904 | } | ||
905 | } | ||
906 | |||
907 | static void dummy_proc_write(struct snd_info_entry *entry, | ||
908 | struct snd_info_buffer *buffer) | ||
909 | { | ||
910 | char line[64]; | ||
911 | |||
912 | while (!snd_info_get_line(buffer, line, sizeof(line))) { | ||
913 | char item[20]; | ||
914 | const char *ptr; | ||
915 | unsigned long long val; | ||
916 | int i; | ||
917 | |||
918 | ptr = snd_info_get_str(item, line, sizeof(item)); | ||
919 | for (i = 0; i < ARRAY_SIZE(fields); i++) { | ||
920 | if (!strcmp(item, fields[i].name)) | ||
921 | break; | ||
922 | } | ||
923 | if (i >= ARRAY_SIZE(fields)) | ||
924 | continue; | ||
925 | snd_info_get_str(item, ptr, sizeof(item)); | ||
926 | if (strict_strtoull(item, 0, &val)) | ||
927 | continue; | ||
928 | if (fields[i].size == sizeof(int)) | ||
929 | *get_dummy_int_ptr(fields[i].offset) = val; | ||
930 | else | ||
931 | *get_dummy_ll_ptr(fields[i].offset) = val; | ||
932 | } | ||
933 | } | ||
934 | |||
935 | static void __devinit dummy_proc_init(struct snd_dummy *chip) | ||
936 | { | ||
937 | struct snd_info_entry *entry; | ||
938 | |||
939 | if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) { | ||
940 | snd_info_set_text_ops(entry, chip, dummy_proc_read); | ||
941 | entry->c.text.write = dummy_proc_write; | ||
942 | entry->mode |= S_IWUSR; | ||
943 | } | ||
944 | } | ||
945 | #else | ||
946 | #define dummy_proc_init(x) | ||
947 | #endif /* CONFIG_SND_DEBUG && CONFIG_PROC_FS */ | ||
948 | |||
584 | static int __devinit snd_dummy_probe(struct platform_device *devptr) | 949 | static int __devinit snd_dummy_probe(struct platform_device *devptr) |
585 | { | 950 | { |
586 | struct snd_card *card; | 951 | struct snd_card *card; |
@@ -610,6 +975,8 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr) | |||
610 | strcpy(card->shortname, "Dummy"); | 975 | strcpy(card->shortname, "Dummy"); |
611 | sprintf(card->longname, "Dummy %i", dev + 1); | 976 | sprintf(card->longname, "Dummy %i", dev + 1); |
612 | 977 | ||
978 | dummy_proc_init(dummy); | ||
979 | |||
613 | snd_card_set_dev(card, &devptr->dev); | 980 | snd_card_set_dev(card, &devptr->dev); |
614 | 981 | ||
615 | err = snd_card_register(card); | 982 | err = snd_card_register(card); |
@@ -670,6 +1037,7 @@ static void snd_dummy_unregister_all(void) | |||
670 | for (i = 0; i < ARRAY_SIZE(devices); ++i) | 1037 | for (i = 0; i < ARRAY_SIZE(devices); ++i) |
671 | platform_device_unregister(devices[i]); | 1038 | platform_device_unregister(devices[i]); |
672 | platform_driver_unregister(&snd_dummy_driver); | 1039 | platform_driver_unregister(&snd_dummy_driver); |
1040 | free_fake_buffer(); | ||
673 | } | 1041 | } |
674 | 1042 | ||
675 | static int __init alsa_card_dummy_init(void) | 1043 | static int __init alsa_card_dummy_init(void) |
@@ -680,6 +1048,12 @@ static int __init alsa_card_dummy_init(void) | |||
680 | if (err < 0) | 1048 | if (err < 0) |
681 | return err; | 1049 | return err; |
682 | 1050 | ||
1051 | err = alloc_fake_buffer(); | ||
1052 | if (err < 0) { | ||
1053 | platform_driver_unregister(&snd_dummy_driver); | ||
1054 | return err; | ||
1055 | } | ||
1056 | |||
683 | cards = 0; | 1057 | cards = 0; |
684 | for (i = 0; i < SNDRV_CARDS; i++) { | 1058 | for (i = 0; i < SNDRV_CARDS; i++) { |
685 | struct platform_device *device; | 1059 | struct platform_device *device; |