diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-09-03 09:59:26 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-09-03 09:59:26 -0400 |
commit | c631d03c6873b9e17906556e84fcafc42f26a7c2 (patch) | |
tree | 618cfbbc0fa79fb8fe8b6a436a11bf435266deef /sound/drivers/dummy.c | |
parent | 326ba5010a5429a5a528b268b36a5900d4ab0eba (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>
Diffstat (limited to 'sound/drivers/dummy.c')
-rw-r--r-- | sound/drivers/dummy.c | 413 |
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}; | |||
148 | static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; | 150 | static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1}; |
149 | static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8}; | 151 | static 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 | ||
154 | static int hrtimer = 1; | ||
155 | #endif | ||
151 | 156 | ||
152 | module_param_array(index, int, NULL, 0444); | 157 | module_param_array(index, int, NULL, 0444); |
153 | MODULE_PARM_DESC(index, "Index value for dummy soundcard."); | 158 | MODULE_PARM_DESC(index, "Index value for dummy soundcard."); |
@@ -161,6 +166,10 @@ module_param_array(pcm_substreams, int, NULL, 0444); | |||
161 | MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver."); | 166 | MODULE_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 | ||
170 | module_param(hrtimer, bool, 0644); | ||
171 | MODULE_PARM_DESC(hrtimer, "Use hrtimer as the timer source."); | ||
172 | #endif | ||
164 | 173 | ||
165 | static struct platform_device *devices[SNDRV_CARDS]; | 174 | static 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 | ||
183 | struct 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 | |||
174 | struct snd_dummy { | 192 | struct 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 | ||
182 | struct snd_dummy_pcm { | 201 | /* |
183 | struct snd_dummy *dummy; | 202 | * system timer interface |
203 | */ | ||
204 | |||
205 | struct 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 | 217 | static int dummy_systimer_start(struct snd_pcm_substream *substream) | |
196 | static 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 | ||
202 | static inline void snd_card_dummy_pcm_timer_stop(struct snd_dummy_pcm *dpcm) | 227 | static int dummy_systimer_stop(struct snd_pcm_substream *substream) |
203 | { | ||
204 | del_timer(&dpcm->timer); | ||
205 | } | ||
206 | |||
207 | static 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 | ||
231 | static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream) | 236 | static 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 | ||
256 | static void snd_card_dummy_pcm_timer_function(unsigned long data) | 258 | static 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 | ||
275 | static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *substream) | 277 | static snd_pcm_uframes_t |
278 | dummy_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 | ||
283 | static struct snd_pcm_hardware snd_card_dummy_playback = | 286 | static 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 | |
302 | static void dummy_systimer_free(struct snd_pcm_substream *substream) | ||
303 | { | ||
304 | kfree(substream->runtime->private_data); | ||
305 | } | ||
306 | |||
307 | static 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 | |||
321 | struct 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 | |||
330 | static 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 | |||
337 | static 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 | |||
349 | static 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 | |||
359 | static 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 | |||
368 | static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm) | ||
369 | { | ||
370 | tasklet_kill(&dpcm->tasklet); | ||
371 | } | ||
372 | |||
373 | static snd_pcm_uframes_t | ||
374 | dummy_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 | |||
388 | static 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 | |||
407 | static 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 | |||
424 | static 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 | |||
431 | static 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 | ||
301 | static struct snd_pcm_hardware snd_card_dummy_capture = | 440 | #endif /* CONFIG_HIGH_RES_TIMERS */ |
441 | |||
442 | /* | ||
443 | * PCM interface | ||
444 | */ | ||
445 | |||
446 | static 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 | |||
461 | static 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 | |||
471 | static 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 | |||
478 | static 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 | ||
319 | static void snd_card_dummy_runtime_free(struct snd_pcm_runtime *runtime) | 497 | static 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 | |||
324 | static 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 | ||
330 | static int snd_card_dummy_hw_free(struct snd_pcm_substream *substream) | 504 | static 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 | ||
335 | static struct snd_dummy_pcm *new_pcm_stream(struct snd_pcm_substream *substream) | 509 | 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 | { | 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 | |||
375 | static 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 | |||
400 | static int snd_card_dummy_playback_close(struct snd_pcm_substream *substream) | ||
401 | { | ||
402 | return 0; | 542 | return 0; |
403 | } | 543 | } |
404 | 544 | ||
405 | static int snd_card_dummy_capture_close(struct snd_pcm_substream *substream) | 545 | static 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 | ||
410 | static struct snd_pcm_ops snd_card_dummy_playback_ops = { | 552 | static 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 | |||
421 | static 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 | ||
432 | static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device, | 563 | static 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"); |