dummy.c 20.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 *  Dummy soundcard
3
 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */

#include <linux/init.h>
22 23
#include <linux/err.h>
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
24 25 26 27 28 29 30
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/control.h>
31
#include <sound/tlv.h>
L
Linus Torvalds 已提交
32 33 34 35
#include <sound/pcm.h>
#include <sound/rawmidi.h>
#include <sound/initval.h>

36
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
L
Linus Torvalds 已提交
37 38 39 40 41 42 43 44 45 46
MODULE_DESCRIPTION("Dummy soundcard (/dev/null)");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");

#define MAX_PCM_DEVICES		4
#define MAX_PCM_SUBSTREAMS	16
#define MAX_MIDI_DEVICES	2

#if 0 /* emu10k1 emulation */
#define MAX_BUFFER_SIZE		(128 * 1024)
47
static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
L
Linus Torvalds 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
{
	int err;
	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
		return err;
	if ((err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX)) < 0)
		return err;
	return 0;
}
#define add_playback_constraints emu10k1_playback_constraints
#endif

#if 0 /* RME9652 emulation */
#define MAX_BUFFER_SIZE		(26 * 64 * 1024)
#define USE_FORMATS		SNDRV_PCM_FMTBIT_S32_LE
#define USE_CHANNELS_MIN	26
#define USE_CHANNELS_MAX	26
#define USE_PERIODS_MIN		2
#define USE_PERIODS_MAX		2
#endif

#if 0 /* ICE1712 emulation */
#define MAX_BUFFER_SIZE		(256 * 1024)
#define USE_FORMATS		SNDRV_PCM_FMTBIT_S32_LE
#define USE_CHANNELS_MIN	10
#define USE_CHANNELS_MAX	10
#define USE_PERIODS_MIN		1
#define USE_PERIODS_MAX		1024
#endif

#if 0 /* UDA1341 emulation */
#define MAX_BUFFER_SIZE		(16380)
#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
#define USE_CHANNELS_MIN	2
#define USE_CHANNELS_MAX	2
#define USE_PERIODS_MIN		2
#define USE_PERIODS_MAX		255
#endif

#if 0 /* simple AC97 bridge (intel8x0) with 48kHz AC97 only codec */
#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
#define USE_CHANNELS_MIN	2
#define USE_CHANNELS_MAX	2
#define USE_RATE		SNDRV_PCM_RATE_48000
#define USE_RATE_MIN		48000
#define USE_RATE_MAX		48000
#endif

95 96 97 98 99 100 101 102 103 104 105 106 107
#if 0 /* CA0106 */
#define USE_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
#define USE_CHANNELS_MIN	2
#define USE_CHANNELS_MAX	2
#define USE_RATE		(SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_192000) 
#define USE_RATE_MIN		48000 
#define USE_RATE_MAX		192000
#define MAX_BUFFER_SIZE		((65536-64)*8)
#define MAX_PERIOD_SIZE		(65536-64)
#define USE_PERIODS_MIN		2
#define USE_PERIODS_MAX		8
#endif

L
Linus Torvalds 已提交
108 109 110 111 112

/* defaults */
#ifndef MAX_BUFFER_SIZE
#define MAX_BUFFER_SIZE		(64*1024)
#endif
113 114 115
#ifndef MAX_PERIOD_SIZE
#define MAX_PERIOD_SIZE		MAX_BUFFER_SIZE
#endif
L
Linus Torvalds 已提交
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
#ifndef USE_FORMATS
#define USE_FORMATS 		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
#endif
#ifndef USE_RATE
#define USE_RATE		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000
#define USE_RATE_MIN		5500
#define USE_RATE_MAX		48000
#endif
#ifndef USE_CHANNELS_MIN
#define USE_CHANNELS_MIN 	1
#endif
#ifndef USE_CHANNELS_MAX
#define USE_CHANNELS_MAX 	2
#endif
#ifndef USE_PERIODS_MIN
#define USE_PERIODS_MIN 	1
#endif
#ifndef USE_PERIODS_MAX
#define USE_PERIODS_MAX 	1024
#endif
#ifndef add_playback_constraints
#define add_playback_constraints(x) 0
#endif
#ifndef add_capture_constraints
#define add_capture_constraints(x) 0
#endif

static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;	/* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;	/* ID for this card */
static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
//static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};

module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for dummy soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for dummy soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable this dummy soundcard.");
module_param_array(pcm_devs, int, NULL, 0444);
MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver.");
module_param_array(pcm_substreams, int, NULL, 0444);
MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-16) for dummy driver.");
//module_param_array(midi_devs, int, NULL, 0444);
//MODULE_PARM_DESC(midi_devs, "MIDI devices # (0-2) for dummy driver.");

163 164
static struct platform_device *devices[SNDRV_CARDS];

L
Linus Torvalds 已提交
165 166 167 168 169 170 171
#define MIXER_ADDR_MASTER	0
#define MIXER_ADDR_LINE		1
#define MIXER_ADDR_MIC		2
#define MIXER_ADDR_SYNTH	3
#define MIXER_ADDR_CD		4
#define MIXER_ADDR_LAST		4

172 173
struct snd_dummy {
	struct snd_card *card;
174
	struct snd_pcm *pcm;
L
Linus Torvalds 已提交
175 176 177
	spinlock_t mixer_lock;
	int mixer_volume[MIXER_ADDR_LAST+1][2];
	int capture_source[MIXER_ADDR_LAST+1][2];
178
};
L
Linus Torvalds 已提交
179

180 181
struct snd_dummy_pcm {
	struct snd_dummy *dummy;
L
Linus Torvalds 已提交
182 183
	spinlock_t lock;
	struct timer_list timer;
184 185
	unsigned int pcm_buffer_size;
	unsigned int pcm_period_size;
L
Linus Torvalds 已提交
186
	unsigned int pcm_bps;		/* bytes per second */
187
	unsigned int pcm_hz;		/* HZ */
L
Linus Torvalds 已提交
188 189
	unsigned int pcm_irq_pos;	/* IRQ position */
	unsigned int pcm_buf_pos;	/* position in buffer */
190 191
	struct snd_pcm_substream *substream;
};
L
Linus Torvalds 已提交
192 193


194
static inline void snd_card_dummy_pcm_timer_start(struct snd_dummy_pcm *dpcm)
L
Linus Torvalds 已提交
195 196 197 198 199
{
	dpcm->timer.expires = 1 + jiffies;
	add_timer(&dpcm->timer);
}

200
static inline void snd_card_dummy_pcm_timer_stop(struct snd_dummy_pcm *dpcm)
L
Linus Torvalds 已提交
201 202 203 204
{
	del_timer(&dpcm->timer);
}

205
static int snd_card_dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
L
Linus Torvalds 已提交
206
{
207 208
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_dummy_pcm *dpcm = runtime->private_data;
T
Takashi Iwai 已提交
209
	int err = 0;
L
Linus Torvalds 已提交
210

T
Takashi Iwai 已提交
211
	spin_lock(&dpcm->lock);
212 213 214
	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
T
Takashi Iwai 已提交
215
		snd_card_dummy_pcm_timer_start(dpcm);
216 217 218
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
T
Takashi Iwai 已提交
219
		snd_card_dummy_pcm_timer_stop(dpcm);
220 221
		break;
	default:
T
Takashi Iwai 已提交
222
		err = -EINVAL;
223
		break;
L
Linus Torvalds 已提交
224
	}
T
Takashi Iwai 已提交
225
	spin_unlock(&dpcm->lock);
226
	return 0;
L
Linus Torvalds 已提交
227 228
}

229
static int snd_card_dummy_pcm_prepare(struct snd_pcm_substream *substream)
L
Linus Torvalds 已提交
230
{
231 232
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_dummy_pcm *dpcm = runtime->private_data;
233 234 235 236
	int bps;

	bps = snd_pcm_format_width(runtime->format) * runtime->rate *
		runtime->channels / 8;
L
Linus Torvalds 已提交
237 238 239

	if (bps <= 0)
		return -EINVAL;
240

L
Linus Torvalds 已提交
241
	dpcm->pcm_bps = bps;
242 243 244
	dpcm->pcm_hz = HZ;
	dpcm->pcm_buffer_size = snd_pcm_lib_buffer_bytes(substream);
	dpcm->pcm_period_size = snd_pcm_lib_period_bytes(substream);
L
Linus Torvalds 已提交
245 246
	dpcm->pcm_irq_pos = 0;
	dpcm->pcm_buf_pos = 0;
247 248 249 250

	snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
			bytes_to_samples(runtime, runtime->dma_bytes));

L
Linus Torvalds 已提交
251 252 253 254 255
	return 0;
}

static void snd_card_dummy_pcm_timer_function(unsigned long data)
{
256
	struct snd_dummy_pcm *dpcm = (struct snd_dummy_pcm *)data;
257
	unsigned long flags;
L
Linus Torvalds 已提交
258
	
259
	spin_lock_irqsave(&dpcm->lock, flags);
L
Linus Torvalds 已提交
260 261
	dpcm->timer.expires = 1 + jiffies;
	add_timer(&dpcm->timer);
262
	dpcm->pcm_irq_pos += dpcm->pcm_bps;
263 264
	dpcm->pcm_buf_pos += dpcm->pcm_bps;
	dpcm->pcm_buf_pos %= dpcm->pcm_buffer_size * dpcm->pcm_hz;
265 266
	if (dpcm->pcm_irq_pos >= dpcm->pcm_period_size * dpcm->pcm_hz) {
		dpcm->pcm_irq_pos %= dpcm->pcm_period_size * dpcm->pcm_hz;
267
		spin_unlock_irqrestore(&dpcm->lock, flags);
L
Linus Torvalds 已提交
268
		snd_pcm_period_elapsed(dpcm->substream);
269 270
	} else
		spin_unlock_irqrestore(&dpcm->lock, flags);
L
Linus Torvalds 已提交
271 272
}

273
static snd_pcm_uframes_t snd_card_dummy_pcm_pointer(struct snd_pcm_substream *substream)
L
Linus Torvalds 已提交
274
{
275 276
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_dummy_pcm *dpcm = runtime->private_data;
L
Linus Torvalds 已提交
277

278
	return bytes_to_frames(runtime, dpcm->pcm_buf_pos / dpcm->pcm_hz);
L
Linus Torvalds 已提交
279 280
}

281
static struct snd_pcm_hardware snd_card_dummy_playback =
L
Linus Torvalds 已提交
282 283
{
	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
284
				 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
L
Linus Torvalds 已提交
285 286 287 288 289 290 291 292
	.formats =		USE_FORMATS,
	.rates =		USE_RATE,
	.rate_min =		USE_RATE_MIN,
	.rate_max =		USE_RATE_MAX,
	.channels_min =		USE_CHANNELS_MIN,
	.channels_max =		USE_CHANNELS_MAX,
	.buffer_bytes_max =	MAX_BUFFER_SIZE,
	.period_bytes_min =	64,
293
	.period_bytes_max =	MAX_PERIOD_SIZE,
L
Linus Torvalds 已提交
294 295 296 297 298
	.periods_min =		USE_PERIODS_MIN,
	.periods_max =		USE_PERIODS_MAX,
	.fifo_size =		0,
};

299
static struct snd_pcm_hardware snd_card_dummy_capture =
L
Linus Torvalds 已提交
300 301
{
	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
302
				 SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID),
L
Linus Torvalds 已提交
303 304 305 306 307 308 309 310
	.formats =		USE_FORMATS,
	.rates =		USE_RATE,
	.rate_min =		USE_RATE_MIN,
	.rate_max =		USE_RATE_MAX,
	.channels_min =		USE_CHANNELS_MIN,
	.channels_max =		USE_CHANNELS_MAX,
	.buffer_bytes_max =	MAX_BUFFER_SIZE,
	.period_bytes_min =	64,
311
	.period_bytes_max =	MAX_PERIOD_SIZE,
L
Linus Torvalds 已提交
312 313 314 315 316
	.periods_min =		USE_PERIODS_MIN,
	.periods_max =		USE_PERIODS_MAX,
	.fifo_size =		0,
};

317
static void snd_card_dummy_runtime_free(struct snd_pcm_runtime *runtime)
L
Linus Torvalds 已提交
318
{
T
Takashi Iwai 已提交
319
	kfree(runtime->private_data);
L
Linus Torvalds 已提交
320 321
}

322 323
static int snd_card_dummy_hw_params(struct snd_pcm_substream *substream,
				    struct snd_pcm_hw_params *hw_params)
L
Linus Torvalds 已提交
324 325 326 327
{
	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
}

328
static int snd_card_dummy_hw_free(struct snd_pcm_substream *substream)
L
Linus Torvalds 已提交
329 330 331 332
{
	return snd_pcm_lib_free_pages(substream);
}

333
static struct snd_dummy_pcm *new_pcm_stream(struct snd_pcm_substream *substream)
L
Linus Torvalds 已提交
334
{
335
	struct snd_dummy_pcm *dpcm;
L
Linus Torvalds 已提交
336

337
	dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
T
Takashi Iwai 已提交
338 339
	if (! dpcm)
		return dpcm;
L
Linus Torvalds 已提交
340 341 342 343 344
	init_timer(&dpcm->timer);
	dpcm->timer.data = (unsigned long) dpcm;
	dpcm->timer.function = snd_card_dummy_pcm_timer_function;
	spin_lock_init(&dpcm->lock);
	dpcm->substream = substream;
T
Takashi Iwai 已提交
345 346 347
	return dpcm;
}

348
static int snd_card_dummy_playback_open(struct snd_pcm_substream *substream)
T
Takashi Iwai 已提交
349
{
350 351
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_dummy_pcm *dpcm;
T
Takashi Iwai 已提交
352 353 354 355
	int err;

	if ((dpcm = new_pcm_stream(substream)) == NULL)
		return -ENOMEM;
L
Linus Torvalds 已提交
356
	runtime->private_data = dpcm;
357
	/* makes the infrastructure responsible for freeing dpcm */
L
Linus Torvalds 已提交
358 359 360 361 362 363 364 365
	runtime->private_free = snd_card_dummy_runtime_free;
	runtime->hw = snd_card_dummy_playback;
	if (substream->pcm->device & 1) {
		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
	}
	if (substream->pcm->device & 2)
		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
366
	if ((err = add_playback_constraints(runtime)) < 0)
L
Linus Torvalds 已提交
367 368 369 370 371
		return err;

	return 0;
}

372
static int snd_card_dummy_capture_open(struct snd_pcm_substream *substream)
L
Linus Torvalds 已提交
373
{
374 375
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct snd_dummy_pcm *dpcm;
L
Linus Torvalds 已提交
376 377
	int err;

T
Takashi Iwai 已提交
378
	if ((dpcm = new_pcm_stream(substream)) == NULL)
L
Linus Torvalds 已提交
379 380
		return -ENOMEM;
	runtime->private_data = dpcm;
381
	/* makes the infrastructure responsible for freeing dpcm */
L
Linus Torvalds 已提交
382 383 384 385 386 387 388 389
	runtime->private_free = snd_card_dummy_runtime_free;
	runtime->hw = snd_card_dummy_capture;
	if (substream->pcm->device == 1) {
		runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
		runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
	}
	if (substream->pcm->device & 2)
		runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID);
390
	if ((err = add_capture_constraints(runtime)) < 0)
L
Linus Torvalds 已提交
391 392 393 394 395
		return err;

	return 0;
}

396
static int snd_card_dummy_playback_close(struct snd_pcm_substream *substream)
L
Linus Torvalds 已提交
397 398 399 400
{
	return 0;
}

401
static int snd_card_dummy_capture_close(struct snd_pcm_substream *substream)
L
Linus Torvalds 已提交
402 403 404 405
{
	return 0;
}

406
static struct snd_pcm_ops snd_card_dummy_playback_ops = {
L
Linus Torvalds 已提交
407 408 409 410 411
	.open =			snd_card_dummy_playback_open,
	.close =		snd_card_dummy_playback_close,
	.ioctl =		snd_pcm_lib_ioctl,
	.hw_params =		snd_card_dummy_hw_params,
	.hw_free =		snd_card_dummy_hw_free,
T
Takashi Iwai 已提交
412 413 414
	.prepare =		snd_card_dummy_pcm_prepare,
	.trigger =		snd_card_dummy_pcm_trigger,
	.pointer =		snd_card_dummy_pcm_pointer,
L
Linus Torvalds 已提交
415 416
};

417
static struct snd_pcm_ops snd_card_dummy_capture_ops = {
L
Linus Torvalds 已提交
418 419 420 421 422
	.open =			snd_card_dummy_capture_open,
	.close =		snd_card_dummy_capture_close,
	.ioctl =		snd_pcm_lib_ioctl,
	.hw_params =		snd_card_dummy_hw_params,
	.hw_free =		snd_card_dummy_hw_free,
T
Takashi Iwai 已提交
423 424 425
	.prepare =		snd_card_dummy_pcm_prepare,
	.trigger =		snd_card_dummy_pcm_trigger,
	.pointer =		snd_card_dummy_pcm_pointer,
L
Linus Torvalds 已提交
426 427
};

428 429
static int __devinit snd_card_dummy_pcm(struct snd_dummy *dummy, int device,
					int substreams)
L
Linus Torvalds 已提交
430
{
431
	struct snd_pcm *pcm;
L
Linus Torvalds 已提交
432 433
	int err;

434 435
	if ((err = snd_pcm_new(dummy->card, "Dummy PCM", device,
			       substreams, substreams, &pcm)) < 0)
L
Linus Torvalds 已提交
436
		return err;
437
	dummy->pcm = pcm;
L
Linus Torvalds 已提交
438 439 440 441 442 443 444 445 446 447 448 449
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_dummy_playback_ops);
	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_dummy_capture_ops);
	pcm->private_data = dummy;
	pcm->info_flags = 0;
	strcpy(pcm->name, "Dummy PCM");
	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
					      snd_dma_continuous_data(GFP_KERNEL),
					      0, 64*1024);
	return 0;
}

#define DUMMY_VOLUME(xname, xindex, addr) \
450 451 452
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
  .name = xname, .index = xindex, \
L
Linus Torvalds 已提交
453 454
  .info = snd_dummy_volume_info, \
  .get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \
455 456
  .private_value = addr, \
  .tlv = { .p = db_scale_dummy } }
L
Linus Torvalds 已提交
457

458 459
static int snd_dummy_volume_info(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_info *uinfo)
L
Linus Torvalds 已提交
460 461 462 463 464 465 466 467
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
	uinfo->count = 2;
	uinfo->value.integer.min = -50;
	uinfo->value.integer.max = 100;
	return 0;
}
 
468 469
static int snd_dummy_volume_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
470
{
471
	struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
472 473
	int addr = kcontrol->private_value;

T
Takashi Iwai 已提交
474
	spin_lock_irq(&dummy->mixer_lock);
L
Linus Torvalds 已提交
475 476
	ucontrol->value.integer.value[0] = dummy->mixer_volume[addr][0];
	ucontrol->value.integer.value[1] = dummy->mixer_volume[addr][1];
T
Takashi Iwai 已提交
477
	spin_unlock_irq(&dummy->mixer_lock);
L
Linus Torvalds 已提交
478 479 480
	return 0;
}

481 482
static int snd_dummy_volume_put(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
483
{
484
	struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
485 486 487 488 489 490 491 492 493 494 495 496 497
	int change, addr = kcontrol->private_value;
	int left, right;

	left = ucontrol->value.integer.value[0];
	if (left < -50)
		left = -50;
	if (left > 100)
		left = 100;
	right = ucontrol->value.integer.value[1];
	if (right < -50)
		right = -50;
	if (right > 100)
		right = 100;
T
Takashi Iwai 已提交
498
	spin_lock_irq(&dummy->mixer_lock);
L
Linus Torvalds 已提交
499 500 501 502
	change = dummy->mixer_volume[addr][0] != left ||
	         dummy->mixer_volume[addr][1] != right;
	dummy->mixer_volume[addr][0] = left;
	dummy->mixer_volume[addr][1] = right;
T
Takashi Iwai 已提交
503
	spin_unlock_irq(&dummy->mixer_lock);
L
Linus Torvalds 已提交
504 505 506
	return change;
}

507
static const DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0);
508

L
Linus Torvalds 已提交
509 510 511 512 513 514
#define DUMMY_CAPSRC(xname, xindex, addr) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
  .info = snd_dummy_capsrc_info, \
  .get = snd_dummy_capsrc_get, .put = snd_dummy_capsrc_put, \
  .private_value = addr }

515
#define snd_dummy_capsrc_info	snd_ctl_boolean_stereo_info
L
Linus Torvalds 已提交
516
 
517 518
static int snd_dummy_capsrc_get(struct snd_kcontrol *kcontrol,
				struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
519
{
520
	struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
521 522
	int addr = kcontrol->private_value;

T
Takashi Iwai 已提交
523
	spin_lock_irq(&dummy->mixer_lock);
L
Linus Torvalds 已提交
524 525
	ucontrol->value.integer.value[0] = dummy->capture_source[addr][0];
	ucontrol->value.integer.value[1] = dummy->capture_source[addr][1];
T
Takashi Iwai 已提交
526
	spin_unlock_irq(&dummy->mixer_lock);
L
Linus Torvalds 已提交
527 528 529
	return 0;
}

530
static int snd_dummy_capsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
531
{
532
	struct snd_dummy *dummy = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
533 534 535 536 537
	int change, addr = kcontrol->private_value;
	int left, right;

	left = ucontrol->value.integer.value[0] & 1;
	right = ucontrol->value.integer.value[1] & 1;
T
Takashi Iwai 已提交
538
	spin_lock_irq(&dummy->mixer_lock);
L
Linus Torvalds 已提交
539 540 541 542
	change = dummy->capture_source[addr][0] != left &&
	         dummy->capture_source[addr][1] != right;
	dummy->capture_source[addr][0] = left;
	dummy->capture_source[addr][1] = right;
T
Takashi Iwai 已提交
543
	spin_unlock_irq(&dummy->mixer_lock);
L
Linus Torvalds 已提交
544 545 546
	return change;
}

547
static struct snd_kcontrol_new snd_dummy_controls[] = {
L
Linus Torvalds 已提交
548 549 550
DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),
DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER),
DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH),
551
DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_SYNTH),
L
Linus Torvalds 已提交
552
DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE),
553
DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_LINE),
L
Linus Torvalds 已提交
554
DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),
555
DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MIC),
L
Linus Torvalds 已提交
556
DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD),
557
DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD)
L
Linus Torvalds 已提交
558 559
};

560
static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)
L
Linus Torvalds 已提交
561
{
562
	struct snd_card *card = dummy->card;
L
Linus Torvalds 已提交
563 564 565
	unsigned int idx;
	int err;

566 567
	if (snd_BUG_ON(!dummy))
		return -EINVAL;
L
Linus Torvalds 已提交
568 569 570 571 572 573 574 575 576 577
	spin_lock_init(&dummy->mixer_lock);
	strcpy(card->mixername, "Dummy Mixer");

	for (idx = 0; idx < ARRAY_SIZE(snd_dummy_controls); idx++) {
		if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_dummy_controls[idx], dummy))) < 0)
			return err;
	}
	return 0;
}

578
static int __devinit snd_dummy_probe(struct platform_device *devptr)
L
Linus Torvalds 已提交
579
{
580 581
	struct snd_card *card;
	struct snd_dummy *dummy;
L
Linus Torvalds 已提交
582
	int idx, err;
583
	int dev = devptr->id;
L
Linus Torvalds 已提交
584 585

	card = snd_card_new(index[dev], id[dev], THIS_MODULE,
586
			    sizeof(struct snd_dummy));
L
Linus Torvalds 已提交
587 588
	if (card == NULL)
		return -ENOMEM;
589
	dummy = card->private_data;
L
Linus Torvalds 已提交
590 591 592 593 594 595 596 597 598 599 600 601 602 603
	dummy->card = card;
	for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) {
		if (pcm_substreams[dev] < 1)
			pcm_substreams[dev] = 1;
		if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
			pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
		if ((err = snd_card_dummy_pcm(dummy, idx, pcm_substreams[dev])) < 0)
			goto __nodev;
	}
	if ((err = snd_card_dummy_new_mixer(dummy)) < 0)
		goto __nodev;
	strcpy(card->driver, "Dummy");
	strcpy(card->shortname, "Dummy");
	sprintf(card->longname, "Dummy %i", dev + 1);
604

605
	snd_card_set_dev(card, &devptr->dev);
606

L
Linus Torvalds 已提交
607
	if ((err = snd_card_register(card)) == 0) {
608
		platform_set_drvdata(devptr, card);
L
Linus Torvalds 已提交
609 610 611 612 613 614 615
		return 0;
	}
      __nodev:
	snd_card_free(card);
	return err;
}

616
static int __devexit snd_dummy_remove(struct platform_device *devptr)
617 618 619 620 621 622 623 624
{
	snd_card_free(platform_get_drvdata(devptr));
	platform_set_drvdata(devptr, NULL);
	return 0;
}

#ifdef CONFIG_PM
static int snd_dummy_suspend(struct platform_device *pdev, pm_message_t state)
L
Linus Torvalds 已提交
625
{
626 627
	struct snd_card *card = platform_get_drvdata(pdev);
	struct snd_dummy *dummy = card->private_data;
L
Linus Torvalds 已提交
628

629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646
	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
	snd_pcm_suspend_all(dummy->pcm);
	return 0;
}
	
static int snd_dummy_resume(struct platform_device *pdev)
{
	struct snd_card *card = platform_get_drvdata(pdev);

	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
	return 0;
}
#endif

#define SND_DUMMY_DRIVER	"snd_dummy"

static struct platform_driver snd_dummy_driver = {
	.probe		= snd_dummy_probe,
647
	.remove		= __devexit_p(snd_dummy_remove),
648 649 650
#ifdef CONFIG_PM
	.suspend	= snd_dummy_suspend,
	.resume		= snd_dummy_resume,
L
Linus Torvalds 已提交
651
#endif
652 653 654 655 656
	.driver		= {
		.name	= SND_DUMMY_DRIVER
	},
};

R
Randy Dunlap 已提交
657
static void snd_dummy_unregister_all(void)
658 659 660 661 662 663 664 665
{
	int i;

	for (i = 0; i < ARRAY_SIZE(devices); ++i)
		platform_device_unregister(devices[i]);
	platform_driver_unregister(&snd_dummy_driver);
}

666 667 668 669 670 671 672 673
static int __init alsa_card_dummy_init(void)
{
	int i, cards, err;

	if ((err = platform_driver_register(&snd_dummy_driver)) < 0)
		return err;

	cards = 0;
674
	for (i = 0; i < SNDRV_CARDS; i++) {
675
		struct platform_device *device;
676 677
		if (! enable[i])
			continue;
678 679
		device = platform_device_register_simple(SND_DUMMY_DRIVER,
							 i, NULL, 0);
680 681
		if (IS_ERR(device))
			continue;
682 683 684 685
		if (!platform_get_drvdata(device)) {
			platform_device_unregister(device);
			continue;
		}
686
		devices[i] = device;
L
Linus Torvalds 已提交
687 688 689 690 691 692
		cards++;
	}
	if (!cards) {
#ifdef MODULE
		printk(KERN_ERR "Dummy soundcard not found or device busy\n");
#endif
693 694
		snd_dummy_unregister_all();
		return -ENODEV;
L
Linus Torvalds 已提交
695 696 697 698 699 700
	}
	return 0;
}

static void __exit alsa_card_dummy_exit(void)
{
701
	snd_dummy_unregister_all();
L
Linus Torvalds 已提交
702 703 704 705
}

module_init(alsa_card_dummy_init)
module_exit(alsa_card_dummy_exit)