aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2009-09-03 09:59:26 -0400
committerTakashi Iwai <tiwai@suse.de>2009-09-03 09:59:26 -0400
commitc631d03c6873b9e17906556e84fcafc42f26a7c2 (patch)
tree618cfbbc0fa79fb8fe8b6a436a11bf435266deef
parent326ba5010a5429a5a528b268b36a5900d4ab0eba (diff)
ALSA: dummy - Support high-res timer mode
Allow snd-dummy driver to use high-res timer as its timing source instead of the system timer. The new module option "hrtimer" is added to turn on/off the high-res timer support. It can be switched even dynamically via sysfs. Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/drivers/dummy.c413
1 files changed, 272 insertions, 141 deletions
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index 54239d2e0997..f387d53e5039 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -25,6 +25,8 @@
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>
@@ -148,6 +150,9 @@ static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
148static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; 150static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
149static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; 151static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
150//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2}; 152//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
153#ifdef CONFIG_HIGH_RES_TIMERS
154static int hrtimer = 1;
155#endif
151 156
152module_param_array(index, int, NULL, 0444); 157module_param_array(index, int, NULL, 0444);
153MODULE_PARM_DESC(index, "Index value for dummy soundcard."); 158MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
@@ -161,6 +166,10 @@ module_param_array(pcm_substreams, int, NULL, 0444);
161MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); 166MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver.");
162//module_param_array(midi_devs, int, NULL, 0444); 167//module_param_array(midi_devs, int, NULL, 0444);
163//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver."); 168//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");
169#ifdef CONFIG_HIGH_RES_TIMERS
170module_param(hrtimer, bool, 0644);
171MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source.");
172#endif
164 173
165static struct platform_device *devices[SNDRV_CARDS]; 174static struct platform_device *devices[SNDRV_CARDS];
166 175
@@ -171,16 +180,29 @@ static struct platform_device *devices[SNDRV_CARDS];
171#define MIXER_ADDR_CD 4 180#define MIXER_ADDR_CD 4
172#define MIXER_ADDR_LAST 4 181#define MIXER_ADDR_LAST 4
173 182
183struct dummy_timer_ops {
184 int (*create)(struct snd_pcm_substream *);
185 void (*free)(struct snd_pcm_substream *);
186 int (*prepare)(struct snd_pcm_substream *);
187 int (*start)(struct snd_pcm_substream *);
188 int (*stop)(struct snd_pcm_substream *);
189 snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
190};
191
174struct snd_dummy { 192struct snd_dummy {
175 struct snd_card *card; 193 struct snd_card *card;
176 struct snd_pcm *pcm; 194 struct snd_pcm *pcm;
177 spinlock_t mixer_lock; 195 spinlock_t mixer_lock;
178 int mixer_volume[MIXER_ADDR_LAST+1][2]; 196 int mixer_volume[MIXER_ADDR_LAST+1][2];
179 int capture_source[MIXER_ADDR_LAST+1][2]; 197 int capture_source[MIXER_ADDR_LAST+1][2];
198 const struct dummy_timer_ops *timer_ops;
180}; 199};
181 200
182struct snd_dummy_pcm { 201/*
183 struct snd_dummy *dummy; 202 * system timer interface
203 */
204
205struct dummy_systimer_pcm {
184 spinlock_t lock; 206 spinlock_t lock;
185 struct timer_list timer; 207 struct timer_list timer;
186 unsigned int pcm_buffer_size; 208 unsigned int pcm_buffer_size;
@@ -192,46 +214,29 @@ struct snd_dummy_pcm {
192 struct snd_pcm_substream *substream; 214 struct snd_pcm_substream *substream;
193}; 215};
194 216
195 217static int dummy_systimer_start(struct snd_pcm_substream *substream)
196static inline void snd_card_dummy_pcm_timer_start(struct snd_dummy_pcm *dpcm)
197{ 218{
219 struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
220 spin_lock(&dpcm->lock);
198 dpcm->timer.expires = 1 + jiffies; 221 dpcm->timer.expires = 1 + jiffies;
199 add_timer(&dpcm->timer); 222 add_timer(&dpcm->timer);
223 spin_unlock(&dpcm->lock);
224 return 0;
200} 225}
201 226
202static inline void snd_card_dummy_pcm_timer_stop(struct snd_dummy_pcm *dpcm) 227static int dummy_systimer_stop(struct snd_pcm_substream *substream)
203{
204 del_timer(&dpcm->timer);
205}
206
207static int snd_card_dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
208{ 228{
209 struct snd_pcm_runtime *runtime = substream->runtime; 229 struct dummy_systimer_pcm *dpcm = substream->runtime->private_data;
210 struct snd_dummy_pcm *dpcm = runtime->private_data;
211 int err = 0;
212
213 spin_lock(&dpcm->lock); 230 spin_lock(&dpcm->lock);
214 switch (cmd) { 231 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); 232 spin_unlock(&dpcm->lock);
228 return 0; 233 return 0;
229} 234}
230 235
231static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream) 236static int dummy_systimer_prepare(struct snd_pcm_substream *substream)
232{ 237{
233 struct snd_pcm_runtime *runtime = substream->runtime; 238 struct snd_pcm_runtime *runtime = substream->runtime;
234 struct snd_dummy_pcm *dpcm = runtime->private_data; 239 struct dummy_systimer_pcm *dpcm = runtime->private_data;
235 int bps; 240 int bps;
236 241
237 bps = snd_pcm_format_width(runtime->format) * runtime->rate * 242 bps = snd_pcm_format_width(runtime->format) * runtime->rate *
@@ -247,15 +252,12 @@ static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream)
247 dpcm->pcm_irq_pos = 0; 252 dpcm->pcm_irq_pos = 0;
248 dpcm->pcm_buf_pos = 0; 253 dpcm->pcm_buf_pos = 0;
249 254
250 snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
251 bytes_to_samples(runtime, runtime->dma_bytes));
252
253 return 0; 255 return 0;
254} 256}
255 257
256static void snd_card_dummy_pcm_timer_function(unsigned long data) 258static void dummy_systimer_callback(unsigned long data)
257{ 259{
258 struct snd_dummy_pcm *dpcm = (struct snd_dummy_pcm *)data; 260 struct dummy_systimer_pcm *dpcm = (struct dummy_systimer_pcm *)data;
259 unsigned long flags; 261 unsigned long flags;
260 262
261 spin_lock_irqsave(&dpcm->lock, flags); 263 spin_lock_irqsave(&dpcm->lock, flags);
@@ -272,36 +274,212 @@ static void snd_card_dummy_pcm_timer_function(unsigned long data)
272 spin_unlock_irqrestore(&dpcm->lock, flags); 274 spin_unlock_irqrestore(&dpcm->lock, flags);
273} 275}
274 276
275static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *substream) 277static snd_pcm_uframes_t
278dummy_systimer_pointer(struct snd_pcm_substream *substream)
276{ 279{
277 struct snd_pcm_runtime *runtime = substream->runtime; 280 struct snd_pcm_runtime *runtime = substream->runtime;
278 struct snd_dummy_pcm *dpcm = runtime->private_data; 281 struct dummy_systimer_pcm *dpcm = runtime->private_data;
279 282
280 return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz); 283 return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz);
281} 284}
282 285
283static struct snd_pcm_hardware snd_card_dummy_playback = 286static int dummy_systimer_create(struct snd_pcm_substream *substream)
284{ 287{
285 .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 288 struct dummy_systimer_pcm *dpcm;
286 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID), 289
287 .formats = USE_FORMATS, 290 dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
288 .rates = USE_RATE, 291 if (!dpcm)
289 .rate_min = USE_RATE_MIN, 292 return -ENOMEM;
290 .rate_max = USE_RATE_MAX, 293 substream->runtime->private_data = dpcm;
291 .channels_min = USE_CHANNELS_MIN, 294 init_timer(&dpcm->timer);
292 .channels_max = USE_CHANNELS_MAX, 295 dpcm->timer.data = (unsigned long) dpcm;
293 .buffer_bytes_max = MAX_BUFFER_SIZE, 296 dpcm->timer.function = dummy_systimer_callback;
294 .period_bytes_min = 64, 297 spin_lock_init(&dpcm->lock);
295 .period_bytes_max = MAX_PERIOD_SIZE, 298 dpcm->substream = substream;
296 .periods_min = USE_PERIODS_MIN, 299 return 0;
297 .periods_max = USE_PERIODS_MAX, 300}
298 .fifo_size = 0, 301
302static void dummy_systimer_free(struct snd_pcm_substream *substream)
303{
304 kfree(substream->runtime->private_data);
305}
306
307static struct dummy_timer_ops dummy_systimer_ops = {
308 .create = dummy_systimer_create,
309 .free = dummy_systimer_free,
310 .prepare = dummy_systimer_prepare,
311 .start = dummy_systimer_start,
312 .stop = dummy_systimer_stop,
313 .pointer = dummy_systimer_pointer,
314};
315
316#ifdef CONFIG_HIGH_RES_TIMERS
317/*
318 * hrtimer interface
319 */
320
321struct dummy_hrtimer_pcm {
322 ktime_t base_time;
323 ktime_t period_time;
324 atomic_t running;
325 struct hrtimer timer;
326 struct tasklet_struct tasklet;
327 struct snd_pcm_substream *substream;
328};
329
330static void dummy_hrtimer_pcm_elapsed(unsigned long priv)
331{
332 struct dummy_hrtimer_pcm *dpcm = (struct dummy_hrtimer_pcm *)priv;
333 if (atomic_read(&dpcm->running))
334 snd_pcm_period_elapsed(dpcm->substream);
335}
336
337static enum hrtimer_restart dummy_hrtimer_callback(struct hrtimer *timer)
338{
339 struct dummy_hrtimer_pcm *dpcm;
340
341 dpcm = container_of(timer, struct dummy_hrtimer_pcm, timer);
342 if (!atomic_read(&dpcm->running))
343 return HRTIMER_NORESTART;
344 tasklet_schedule(&dpcm->tasklet);
345 hrtimer_forward_now(timer, dpcm->period_time);
346 return HRTIMER_RESTART;
347}
348
349static int dummy_hrtimer_start(struct snd_pcm_substream *substream)
350{
351 struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
352
353 dpcm->base_time = hrtimer_cb_get_time(&dpcm->timer);
354 hrtimer_start(&dpcm->timer, dpcm->period_time, HRTIMER_MODE_REL);
355 atomic_set(&dpcm->running, 1);
356 return 0;
357}
358
359static int dummy_hrtimer_stop(struct snd_pcm_substream *substream)
360{
361 struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
362
363 atomic_set(&dpcm->running, 0);
364 hrtimer_cancel(&dpcm->timer);
365 return 0;
366}
367
368static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
369{
370 tasklet_kill(&dpcm->tasklet);
371}
372
373static snd_pcm_uframes_t
374dummy_hrtimer_pointer(struct snd_pcm_substream *substream)
375{
376 struct snd_pcm_runtime *runtime = substream->runtime;
377 struct dummy_hrtimer_pcm *dpcm = runtime->private_data;
378 u64 delta;
379 u32 pos;
380
381 delta = ktime_us_delta(hrtimer_cb_get_time(&dpcm->timer),
382 dpcm->base_time);
383 delta = div_u64(delta * runtime->rate + 999999, 1000000);
384 div_u64_rem(delta, runtime->buffer_size, &pos);
385 return pos;
386}
387
388static int dummy_hrtimer_prepare(struct snd_pcm_substream *substream)
389{
390 struct snd_pcm_runtime *runtime = substream->runtime;
391 struct dummy_hrtimer_pcm *dpcm = runtime->private_data;
392 unsigned int period, rate;
393 long sec;
394 unsigned long nsecs;
395
396 dummy_hrtimer_sync(dpcm);
397 period = runtime->period_size;
398 rate = runtime->rate;
399 sec = period / rate;
400 period %= rate;
401 nsecs = div_u64((u64)period * 1000000000UL + rate - 1, rate);
402 dpcm->period_time = ktime_set(sec, nsecs);
403
404 return 0;
405}
406
407static int dummy_hrtimer_create(struct snd_pcm_substream *substream)
408{
409 struct dummy_hrtimer_pcm *dpcm;
410
411 dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
412 if (!dpcm)
413 return -ENOMEM;
414 substream->runtime->private_data = dpcm;
415 hrtimer_init(&dpcm->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
416 dpcm->timer.function = dummy_hrtimer_callback;
417 dpcm->substream = substream;
418 atomic_set(&dpcm->running, 0);
419 tasklet_init(&dpcm->tasklet, dummy_hrtimer_pcm_elapsed,
420 (unsigned long)dpcm);
421 return 0;
422}
423
424static void dummy_hrtimer_free(struct snd_pcm_substream *substream)
425{
426 struct dummy_hrtimer_pcm *dpcm = substream->runtime->private_data;
427 dummy_hrtimer_sync(dpcm);
428 kfree(dpcm);
429}
430
431static struct dummy_timer_ops dummy_hrtimer_ops = {
432 .create = dummy_hrtimer_create,
433 .free = dummy_hrtimer_free,
434 .prepare = dummy_hrtimer_prepare,
435 .start = dummy_hrtimer_start,
436 .stop = dummy_hrtimer_stop,
437 .pointer = dummy_hrtimer_pointer,
299}; 438};
300 439
301static struct snd_pcm_hardware snd_card_dummy_capture = 440#endif /* CONFIG_HIGH_RES_TIMERS */
441
442/*
443 * PCM interface
444 */
445
446static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
447{
448 struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
449
450 switch (cmd) {
451 case SNDRV_PCM_TRIGGER_START:
452 case SNDRV_PCM_TRIGGER_RESUME:
453 return dummy->timer_ops->start(substream);
454 case SNDRV_PCM_TRIGGER_STOP:
455 case SNDRV_PCM_TRIGGER_SUSPEND:
456 return dummy->timer_ops->stop(substream);
457 }
458 return -EINVAL;
459}
460
461static int dummy_pcm_prepare(struct snd_pcm_substream *substream)
462{
463 struct snd_pcm_runtime *runtime = substream->runtime;
464 struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
465
466 snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
467 bytes_to_samples(runtime, runtime->dma_bytes));
468 return dummy->timer_ops->prepare(substream);
469}
470
471static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream)
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 return dummy->timer_ops->pointer(substream);
476}
477
478static struct snd_pcm_hardware dummy_pcm_hardware = {
479 .info = (SNDRV_PCM_INFO_MMAP |
480 SNDRV_PCM_INFO_INTERLEAVED |
481 SNDRV_PCM_INFO_RESUME |
482 SNDRV_PCM_INFO_MMAP_VALID),
305 .formats = USE_FORMATS, 483 .formats = USE_FORMATS,
306 .rates = USE_RATE, 484 .rates = USE_RATE,
307 .rate_min = USE_RATE_MIN, 485 .rate_min = USE_RATE_MIN,
@@ -316,117 +494,70 @@ static struct snd_pcm_hardware snd_card_dummy_capture =
316 .fifo_size = 0, 494 .fifo_size = 0,
317}; 495};
318 496
319static void snd_card_dummy_runtime_free(struct snd_pcm_runtime *runtime) 497static int dummy_pcm_hw_params(struct snd_pcm_substream *substream,
320{ 498 struct snd_pcm_hw_params *hw_params)
321 kfree(runtime->private_data);
322}
323
324static int snd_card_dummy_hw_params(struct snd_pcm_substream *substream,
325 struct snd_pcm_hw_params *hw_params)
326{ 499{
327 return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); 500 return snd_pcm_lib_malloc_pages(substream,
501 params_buffer_bytes(hw_params));
328} 502}
329 503
330static int snd_card_dummy_hw_free(struct snd_pcm_substream *substream) 504static int dummy_pcm_hw_free(struct snd_pcm_substream *substream)
331{ 505{
332 return snd_pcm_lib_free_pages(substream); 506 return snd_pcm_lib_free_pages(substream);
333} 507}
334 508
335static struct snd_dummy_pcm *new_pcm_stream(struct snd_pcm_substream *substream) 509static 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
350static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream)
351{ 510{
511 struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
352 struct snd_pcm_runtime *runtime = substream->runtime; 512 struct snd_pcm_runtime *runtime = substream->runtime;
353 struct snd_dummy_pcm *dpcm;
354 int err; 513 int err;
355 514
356 if ((dpcm = new_pcm_stream(substream)) == NULL) 515 dummy->timer_ops = &dummy_systimer_ops;
357 return -ENOMEM; 516#ifdef CONFIG_HIGH_RES_TIMERS
358 runtime->private_data = dpcm; 517 if (hrtimer)
359 /* makes the infrastructure responsible for freeing dpcm */ 518 dummy->timer_ops = &dummy_hrtimer_ops;
360 runtime->private_free = snd_card_dummy_runtime_free; 519#endif
361 runtime->hw = snd_card_dummy_playback; 520
362 if (substream->pcm->device & 1) { 521 err = dummy->timer_ops->create(substream);
363 runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
364 runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
365 }
366 if (substream->pcm->device & 2)
367 runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
368 err = add_playback_constraints(runtime);
369 if (err < 0) 522 if (err < 0)
370 return err; 523 return err;
371 524
372 return 0; 525 runtime->hw = dummy_pcm_hardware;
373} 526 if (substream->pcm->device & 1) {
374
375static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream)
376{
377 struct snd_pcm_runtime *runtime = substream->runtime;
378 struct snd_dummy_pcm *dpcm;
379 int err;
380
381 if ((dpcm = new_pcm_stream(substream)) == NULL)
382 return -ENOMEM;
383 runtime->private_data = dpcm;
384 /* makes the infrastructure responsible for freeing dpcm */
385 runtime->private_free = snd_card_dummy_runtime_free;
386 runtime->hw = snd_card_dummy_capture;
387 if (substream->pcm->device == 1) {
388 runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED; 527 runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
389 runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED; 528 runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
390 } 529 }
391 if (substream->pcm->device & 2) 530 if (substream->pcm->device & 2)
392 runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID); 531 runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
393 err = add_capture_constraints(runtime); 532 SNDRV_PCM_INFO_MMAP_VALID);
394 if (err < 0) 533
534 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
535 err = add_playback_constraints(substream->runtime);
536 else
537 err = add_capture_constraints(substream->runtime);
538 if (err < 0) {
539 dummy->timer_ops->free(substream);
395 return err; 540 return err;
396 541 }
397 return 0;
398}
399
400static int snd_card_dummy_playback_close(struct snd_pcm_substream *substream)
401{
402 return 0; 542 return 0;
403} 543}
404 544
405static int snd_card_dummy_capture_close(struct snd_pcm_substream *substream) 545static int dummy_pcm_close(struct snd_pcm_substream *substream)
406{ 546{
547 struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
548 dummy->timer_ops->free(substream);
407 return 0; 549 return 0;
408} 550}
409 551
410static struct snd_pcm_ops snd_card_dummy_playback_ops = { 552static struct snd_pcm_ops dummy_pcm_ops = {
411 .open = snd_card_dummy_playback_open, 553 .open = dummy_pcm_open,
412 .close = snd_card_dummy_playback_close, 554 .close = dummy_pcm_close,
413 .ioctl = snd_pcm_lib_ioctl, 555 .ioctl = snd_pcm_lib_ioctl,
414 .hw_params = snd_card_dummy_hw_params, 556 .hw_params = dummy_pcm_hw_params,
415 .hw_free = snd_card_dummy_hw_free, 557 .hw_free = dummy_pcm_hw_free,
416 .prepare = snd_card_dummy_pcm_prepare, 558 .prepare = dummy_pcm_prepare,
417 .trigger = snd_card_dummy_pcm_trigger, 559 .trigger = dummy_pcm_trigger,
418 .pointer = snd_card_dummy_pcm_pointer, 560 .pointer = dummy_pcm_pointer,
419};
420
421static struct snd_pcm_ops snd_card_dummy_capture_ops = {
422 .open = snd_card_dummy_capture_open,
423 .close = snd_card_dummy_capture_close,
424 .ioctl = snd_pcm_lib_ioctl,
425 .hw_params = snd_card_dummy_hw_params,
426 .hw_free = snd_card_dummy_hw_free,
427 .prepare = snd_card_dummy_pcm_prepare,
428 .trigger = snd_card_dummy_pcm_trigger,
429 .pointer = snd_card_dummy_pcm_pointer,
430}; 561};
431 562
432static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, 563static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
@@ -440,8 +571,8 @@ static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
440 if (err < 0) 571 if (err < 0)
441 return err; 572 return err;
442 dummy->pcm = pcm; 573 dummy->pcm = pcm;
443 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops); 574 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &dummy_pcm_ops);
444 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops); 575 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &dummy_pcm_ops);
445 pcm->private_data = dummy; 576 pcm->private_data = dummy;
446 pcm->info_flags = 0; 577 pcm->info_flags = 0;
447 strcpy(pcm->name, "Dummy PCM"); 578 strcpy(pcm->name, "Dummy PCM");