ca0106_mixer.c 14.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 *  Copyright (c) 2004 James Courtier-Dutton <James@superbug.demon.co.uk>
 *  Driver CA0106 chips. e.g. Sound Blaster Audigy LS and Live 24bit
4
 *  Version: 0.0.17
L
Linus Torvalds 已提交
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
 *
 *  FEATURES currently supported:
 *    See ca0106_main.c for features.
 * 
 *  Changelog:
 *    Support interrupts per period.
 *    Removed noise from Center/LFE channel when in Analog mode.
 *    Rename and remove mixer controls.
 *  0.0.6
 *    Use separate card based DMA buffer for periods table list.
 *  0.0.7
 *    Change remove and rename ctrls into lists.
 *  0.0.8
 *    Try to fix capture sources.
 *  0.0.9
 *    Fix AC3 output.
 *    Enable S32_LE format support.
 *  0.0.10
 *    Enable playback 48000 and 96000 rates. (Rates other that these do not work, even with "plug:front".)
 *  0.0.11
 *    Add Model name recognition.
 *  0.0.12
 *    Correct interrupt timing. interrupt at end of period, instead of in the middle of a playback period.
 *    Remove redundent "voice" handling.
 *  0.0.13
 *    Single trigger call for multi channels.
 *  0.0.14
 *    Set limits based on what the sound card hardware can do.
 *    playback periods_min=2, periods_max=8
 *    capture hw constraints require period_size = n * 64 bytes.
 *    playback hw constraints require period_size = n * 64 bytes.
 *  0.0.15
 *    Separated ca0106.c into separate functional .c files.
 *  0.0.16
 *    Modified Copyright message.
40 41
 *  0.0.17
 *    Implement Mic and Line in Capture.
L
Linus Torvalds 已提交
42 43 44 45 46 47 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
 *
 *  This code was initally based on code from ALSA's emu10k1x.c which is:
 *  Copyright (c) by Francisco Moraes <fmoraes@nc.rr.com>
 *
 *   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 <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/moduleparam.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/info.h>

#include "ca0106.h"

76 77
static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_info *uinfo)
L
Linus Torvalds 已提交
78 79 80 81 82 83 84 85
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
	uinfo->count = 1;
	uinfo->value.integer.min = 0;
	uinfo->value.integer.max = 1;
	return 0;
}

86 87
static int snd_ca0106_shared_spdif_get(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
88
{
89
	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
90 91 92 93 94

	ucontrol->value.enumerated.item[0] = emu->spdif_enable;
	return 0;
}

95 96
static int snd_ca0106_shared_spdif_put(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
97
{
98
	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
	unsigned int val;
	int change = 0;
	u32 mask;

	val = ucontrol->value.enumerated.item[0] ;
	change = (emu->spdif_enable != val);
	if (change) {
		emu->spdif_enable = val;
		if (val == 1) {
			/* Digital */
			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x0b000000);
			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) & ~0x1000);
			mask = inl(emu->port + GPIO) & ~0x101;
			outl(mask, emu->port + GPIO);

		} else {
			/* Analog */
			snd_ca0106_ptr_write(emu, SPDIF_SELECT1, 0, 0xf);
119
			snd_ca0106_ptr_write(emu, SPDIF_SELECT2, 0, 0x000f0000);
L
Linus Torvalds 已提交
120 121 122 123 124 125 126 127 128
			snd_ca0106_ptr_write(emu, CAPTURE_CONTROL, 0,
				snd_ca0106_ptr_read(emu, CAPTURE_CONTROL, 0) | 0x1000);
			mask = inl(emu->port + GPIO) | 0x101;
			outl(mask, emu->port + GPIO);
		}
	}
        return change;
}

129 130
static int snd_ca0106_capture_source_info(struct snd_kcontrol *kcontrol,
					  struct snd_ctl_elem_info *uinfo)
L
Linus Torvalds 已提交
131
{
T
Takashi Iwai 已提交
132 133 134
	static char *texts[6] = {
		"SPDIF out", "i2s mixer out", "SPDIF in", "i2s in", "AC97 in", "SRC out"
	};
L
Linus Torvalds 已提交
135 136 137 138 139 140 141 142 143 144

	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	uinfo->count = 1;
	uinfo->value.enumerated.items = 6;
	if (uinfo->value.enumerated.item > 5)
                uinfo->value.enumerated.item = 5;
	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
	return 0;
}

145 146
static int snd_ca0106_capture_source_get(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
147
{
148
	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
149 150 151 152 153

	ucontrol->value.enumerated.item[0] = emu->capture_source;
	return 0;
}

154 155
static int snd_ca0106_capture_source_put(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
156
{
157
	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
	unsigned int val;
	int change = 0;
	u32 mask;
	u32 source;

	val = ucontrol->value.enumerated.item[0] ;
	change = (emu->capture_source != val);
	if (change) {
		emu->capture_source = val;
		source = (val << 28) | (val << 24) | (val << 20) | (val << 16);
		mask = snd_ca0106_ptr_read(emu, CAPTURE_SOURCE, 0) & 0xffff;
		snd_ca0106_ptr_write(emu, CAPTURE_SOURCE, 0, source | mask);
	}
        return change;
}

174 175
static int snd_ca0106_capture_mic_line_in_info(struct snd_kcontrol *kcontrol,
					       struct snd_ctl_elem_info *uinfo)
176 177 178 179 180 181 182 183 184 185 186 187
{
	static char *texts[2] = { "Line in", "Mic in" };

	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
	uinfo->count = 1;
	uinfo->value.enumerated.items = 2;
	if (uinfo->value.enumerated.item > 1)
                uinfo->value.enumerated.item = 1;
	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
	return 0;
}

188 189
static int snd_ca0106_capture_mic_line_in_get(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
190
{
191
	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
192 193 194 195 196

	ucontrol->value.enumerated.item[0] = emu->capture_mic_line_in;
	return 0;
}

197 198
static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
					struct snd_ctl_elem_value *ucontrol)
199
{
200
	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
	unsigned int val;
	int change = 0;
	u32 tmp;

	val = ucontrol->value.enumerated.item[0] ;
	change = (emu->capture_mic_line_in != val);
	if (change) {
		emu->capture_mic_line_in = val;
		if (val) {
			snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */
			tmp = inl(emu->port+GPIO) & ~0x400;
			tmp = tmp | 0x400;
			outl(tmp, emu->port+GPIO);
			snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_MIC);
		} else {
			snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_PHONE); /* Mute input */
			tmp = inl(emu->port+GPIO) & ~0x400;
			outl(tmp, emu->port+GPIO);
			snd_ca0106_i2c_write(emu, ADC_MUX, ADC_MUX_LINEIN);
		}
	}
        return change;
}

225
static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in __devinitdata =
226 227 228 229 230 231 232 233
{
	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
	.name =		"Mic/Line in Capture",
	.info =		snd_ca0106_capture_mic_line_in_info,
	.get =		snd_ca0106_capture_mic_line_in_get,
	.put =		snd_ca0106_capture_mic_line_in_put
};

234 235
static int snd_ca0106_spdif_info(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_info *uinfo)
L
Linus Torvalds 已提交
236 237 238 239 240 241
{
	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
	uinfo->count = 1;
	return 0;
}

242 243
static int snd_ca0106_spdif_get(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
244
{
245
	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
246 247 248 249 250 251 252 253 254
	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);

	ucontrol->value.iec958.status[0] = (emu->spdif_bits[idx] >> 0) & 0xff;
	ucontrol->value.iec958.status[1] = (emu->spdif_bits[idx] >> 8) & 0xff;
	ucontrol->value.iec958.status[2] = (emu->spdif_bits[idx] >> 16) & 0xff;
	ucontrol->value.iec958.status[3] = (emu->spdif_bits[idx] >> 24) & 0xff;
        return 0;
}

255 256
static int snd_ca0106_spdif_get_mask(struct snd_kcontrol *kcontrol,
				      struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
257 258 259 260 261 262 263 264
{
	ucontrol->value.iec958.status[0] = 0xff;
	ucontrol->value.iec958.status[1] = 0xff;
	ucontrol->value.iec958.status[2] = 0xff;
	ucontrol->value.iec958.status[3] = 0xff;
        return 0;
}

265 266
static int snd_ca0106_spdif_put(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
267
{
268
	struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
	int change;
	unsigned int val;

	val = (ucontrol->value.iec958.status[0] << 0) |
	      (ucontrol->value.iec958.status[1] << 8) |
	      (ucontrol->value.iec958.status[2] << 16) |
	      (ucontrol->value.iec958.status[3] << 24);
	change = val != emu->spdif_bits[idx];
	if (change) {
		snd_ca0106_ptr_write(emu, SPCS0 + idx, 0, val);
		emu->spdif_bits[idx] = val;
	}
        return change;
}

285 286
static int snd_ca0106_volume_info(struct snd_kcontrol *kcontrol,
				  struct snd_ctl_elem_info *uinfo)
L
Linus Torvalds 已提交
287 288 289 290 291 292 293 294
{
        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
        uinfo->count = 2;
        uinfo->value.integer.min = 0;
        uinfo->value.integer.max = 255;
        return 0;
}

295 296
static int snd_ca0106_volume_get(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
297
{
298
        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
L
Linus Torvalds 已提交
299
        unsigned int value;
T
Takashi Iwai 已提交
300 301 302 303
	int channel_id, reg;

	channel_id = (kcontrol->private_value >> 8) & 0xff;
	reg = kcontrol->private_value & 0xff;
L
Linus Torvalds 已提交
304 305 306 307 308 309 310

        value = snd_ca0106_ptr_read(emu, reg, channel_id);
        ucontrol->value.integer.value[0] = 0xff - ((value >> 24) & 0xff); /* Left */
        ucontrol->value.integer.value[1] = 0xff - ((value >> 16) & 0xff); /* Right */
        return 0;
}

311 312
static int snd_ca0106_volume_put(struct snd_kcontrol *kcontrol,
				 struct snd_ctl_elem_value *ucontrol)
L
Linus Torvalds 已提交
313
{
314
        struct snd_ca0106 *emu = snd_kcontrol_chip(kcontrol);
T
Takashi Iwai 已提交
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341
        unsigned int oval, nval;
	int channel_id, reg;

	channel_id = (kcontrol->private_value >> 8) & 0xff;
	reg = kcontrol->private_value & 0xff;

	oval = snd_ca0106_ptr_read(emu, reg, channel_id);
	nval = ((0xff - ucontrol->value.integer.value[0]) << 24) |
		((0xff - ucontrol->value.integer.value[1]) << 16);
        nval |= ((0xff - ucontrol->value.integer.value[0]) << 8) |
		((0xff - ucontrol->value.integer.value[1]) );
	if (oval == nval)
		return 0;
	snd_ca0106_ptr_write(emu, reg, channel_id, nval);
	return 1;
}

#define CA_VOLUME(xname,chid,reg) \
{								\
	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,	\
	.info = snd_ca0106_volume_info,				\
	.get =          snd_ca0106_volume_get,			\
	.put =          snd_ca0106_volume_put,			\
	.private_value = ((chid) << 8) | (reg)			\
}


342
static struct snd_kcontrol_new snd_ca0106_volume_ctls[] __devinitdata = {
T
Takashi Iwai 已提交
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
	CA_VOLUME("Analog Front Playback Volume",
		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME2),
        CA_VOLUME("Analog Rear Playback Volume",
		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME2),
	CA_VOLUME("Analog Center/LFE Playback Volume",
		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME2),
        CA_VOLUME("Analog Side Playback Volume",
		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME2),

        CA_VOLUME("SPDIF Front Playback Volume",
		  CONTROL_FRONT_CHANNEL, PLAYBACK_VOLUME1),
	CA_VOLUME("SPDIF Rear Playback Volume",
		  CONTROL_REAR_CHANNEL, PLAYBACK_VOLUME1),
	CA_VOLUME("SPDIF Center/LFE Playback Volume",
		  CONTROL_CENTER_LFE_CHANNEL, PLAYBACK_VOLUME1),
	CA_VOLUME("SPDIF Unknown Playback Volume",
		  CONTROL_UNKNOWN_CHANNEL, PLAYBACK_VOLUME1),

        CA_VOLUME("CAPTURE feedback Playback Volume",
		  1, CAPTURE_CONTROL),

	{
		.access =	SNDRV_CTL_ELEM_ACCESS_READ,
		.iface =        SNDRV_CTL_ELEM_IFACE_PCM,
		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
		.count =	4,
		.info =         snd_ca0106_spdif_info,
		.get =          snd_ca0106_spdif_get_mask
	},
	{
		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
		.name =		"SPDIF Out",
		.info =		snd_ca0106_shared_spdif_info,
		.get =		snd_ca0106_shared_spdif_get,
		.put =		snd_ca0106_shared_spdif_put
	},
	{
		.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
		.name =		"Capture Source",
		.info =		snd_ca0106_capture_source_info,
		.get =		snd_ca0106_capture_source_get,
		.put =		snd_ca0106_capture_source_put
	},
	{
		.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
		.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
		.count =	4,
		.info =         snd_ca0106_spdif_info,
		.get =          snd_ca0106_spdif_get,
		.put =          snd_ca0106_spdif_put
	},
L
Linus Torvalds 已提交
394 395
};

396
static int __devinit remove_ctl(struct snd_card *card, const char *name)
L
Linus Torvalds 已提交
397
{
398
	struct snd_ctl_elem_id id;
L
Linus Torvalds 已提交
399 400 401 402 403 404
	memset(&id, 0, sizeof(id));
	strcpy(id.name, name);
	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
	return snd_ctl_remove_id(card, &id);
}

405
static struct snd_kcontrol __devinit *ctl_find(struct snd_card *card, const char *name)
L
Linus Torvalds 已提交
406
{
407
	struct snd_ctl_elem_id sid;
L
Linus Torvalds 已提交
408 409 410 411 412 413 414
	memset(&sid, 0, sizeof(sid));
	/* FIXME: strcpy is bad. */
	strcpy(sid.name, name);
	sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
	return snd_ctl_find_id(card, &sid);
}

415
static int __devinit rename_ctl(struct snd_card *card, const char *src, const char *dst)
L
Linus Torvalds 已提交
416
{
417
	struct snd_kcontrol *kctl = ctl_find(card, src);
L
Linus Torvalds 已提交
418 419 420 421 422 423 424
	if (kctl) {
		strcpy(kctl->id.name, dst);
		return 0;
	}
	return -ENOENT;
}

425
int __devinit snd_ca0106_mixer(struct snd_ca0106 *emu)
L
Linus Torvalds 已提交
426
{
T
Takashi Iwai 已提交
427
	int i, err;
428
        struct snd_card *card = emu->card;
L
Linus Torvalds 已提交
429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
	char **c;
	static char *ca0106_remove_ctls[] = {
		"Master Mono Playback Switch",
		"Master Mono Playback Volume",
		"3D Control - Switch",
		"3D Control Sigmatel - Depth",
		"PCM Playback Switch",
		"PCM Playback Volume",
		"CD Playback Switch",
		"CD Playback Volume",
		"Phone Playback Switch",
		"Phone Playback Volume",
		"Video Playback Switch",
		"Video Playback Volume",
		"PC Speaker Playback Switch",
		"PC Speaker Playback Volume",
		"Mono Output Select",
		"Capture Source",
		"Capture Switch",
		"Capture Volume",
		"External Amplifier",
		"Sigmatel 4-Speaker Stereo Playback Switch",
		"Sigmatel Surround Phase Inversion Playback ",
		NULL
	};
	static char *ca0106_rename_ctls[] = {
		"Master Playback Switch", "Capture Switch",
		"Master Playback Volume", "Capture Volume",
		"Line Playback Switch", "AC97 Line Capture Switch",
		"Line Playback Volume", "AC97 Line Capture Volume",
		"Aux Playback Switch", "AC97 Aux Capture Switch",
		"Aux Playback Volume", "AC97 Aux Capture Volume",
		"Mic Playback Switch", "AC97 Mic Capture Switch",
		"Mic Playback Volume", "AC97 Mic Capture Volume",
		"Mic Select", "AC97 Mic Select",
		"Mic Boost (+20dB)", "AC97 Mic Boost (+20dB)",
		NULL
	};
#if 1
T
Takashi Iwai 已提交
468
	for (c = ca0106_remove_ctls; *c; c++)
L
Linus Torvalds 已提交
469
		remove_ctl(card, *c);
T
Takashi Iwai 已提交
470
	for (c = ca0106_rename_ctls; *c; c += 2)
L
Linus Torvalds 已提交
471 472 473
		rename_ctl(card, c[0], c[1]);
#endif

T
Takashi Iwai 已提交
474 475 476 477 478
	for (i = 0; i < ARRAY_SIZE(snd_ca0106_volume_ctls); i++) {
		err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_volume_ctls[i], emu));
		if (err < 0)
			return err;
	}
479
	if (emu->details->i2c_adc == 1) {
T
Takashi Iwai 已提交
480 481
		err = snd_ctl_add(card, snd_ctl_new1(&snd_ca0106_capture_mic_line_in, emu));
		if (err < 0)
482 483
			return err;
	}
L
Linus Torvalds 已提交
484 485 486
        return 0;
}