952 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>4 * Routines for Sound Blaster mixer control5 */6 7#include <linux/io.h>8#include <linux/delay.h>9#include <linux/time.h>10#include <sound/core.h>11#include <sound/sb.h>12#include <sound/control.h>13 14#undef IO_DEBUG15 16void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char data)17{18 outb(reg, SBP(chip, MIXER_ADDR));19 udelay(10);20 outb(data, SBP(chip, MIXER_DATA));21 udelay(10);22#ifdef IO_DEBUG23 dev_dbg(chip->card->dev, "mixer_write 0x%x 0x%x\n", reg, data);24#endif25}26 27unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg)28{29 unsigned char result;30 31 outb(reg, SBP(chip, MIXER_ADDR));32 udelay(10);33 result = inb(SBP(chip, MIXER_DATA));34 udelay(10);35#ifdef IO_DEBUG36 dev_dbg(chip->card->dev, "mixer_read 0x%x 0x%x\n", reg, result);37#endif38 return result;39}40 41/*42 * Single channel mixer element43 */44 45static int snd_sbmixer_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)46{47 int mask = (kcontrol->private_value >> 24) & 0xff;48 49 uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;50 uinfo->count = 1;51 uinfo->value.integer.min = 0;52 uinfo->value.integer.max = mask;53 return 0;54}55 56static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)57{58 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);59 unsigned long flags;60 int reg = kcontrol->private_value & 0xff;61 int shift = (kcontrol->private_value >> 16) & 0xff;62 int mask = (kcontrol->private_value >> 24) & 0xff;63 unsigned char val;64 65 spin_lock_irqsave(&sb->mixer_lock, flags);66 val = (snd_sbmixer_read(sb, reg) >> shift) & mask;67 spin_unlock_irqrestore(&sb->mixer_lock, flags);68 ucontrol->value.integer.value[0] = val;69 return 0;70}71 72static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)73{74 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);75 unsigned long flags;76 int reg = kcontrol->private_value & 0xff;77 int shift = (kcontrol->private_value >> 16) & 0x07;78 int mask = (kcontrol->private_value >> 24) & 0xff;79 int change;80 unsigned char val, oval;81 82 val = (ucontrol->value.integer.value[0] & mask) << shift;83 spin_lock_irqsave(&sb->mixer_lock, flags);84 oval = snd_sbmixer_read(sb, reg);85 val = (oval & ~(mask << shift)) | val;86 change = val != oval;87 if (change)88 snd_sbmixer_write(sb, reg, val);89 spin_unlock_irqrestore(&sb->mixer_lock, flags);90 return change;91}92 93/*94 * Double channel mixer element95 */96 97static int snd_sbmixer_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)98{99 int mask = (kcontrol->private_value >> 24) & 0xff;100 101 uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;102 uinfo->count = 2;103 uinfo->value.integer.min = 0;104 uinfo->value.integer.max = mask;105 return 0;106}107 108static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)109{110 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);111 unsigned long flags;112 int left_reg = kcontrol->private_value & 0xff;113 int right_reg = (kcontrol->private_value >> 8) & 0xff;114 int left_shift = (kcontrol->private_value >> 16) & 0x07;115 int right_shift = (kcontrol->private_value >> 19) & 0x07;116 int mask = (kcontrol->private_value >> 24) & 0xff;117 unsigned char left, right;118 119 spin_lock_irqsave(&sb->mixer_lock, flags);120 left = (snd_sbmixer_read(sb, left_reg) >> left_shift) & mask;121 right = (snd_sbmixer_read(sb, right_reg) >> right_shift) & mask;122 spin_unlock_irqrestore(&sb->mixer_lock, flags);123 ucontrol->value.integer.value[0] = left;124 ucontrol->value.integer.value[1] = right;125 return 0;126}127 128static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)129{130 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);131 unsigned long flags;132 int left_reg = kcontrol->private_value & 0xff;133 int right_reg = (kcontrol->private_value >> 8) & 0xff;134 int left_shift = (kcontrol->private_value >> 16) & 0x07;135 int right_shift = (kcontrol->private_value >> 19) & 0x07;136 int mask = (kcontrol->private_value >> 24) & 0xff;137 int change;138 unsigned char left, right, oleft, oright;139 140 left = (ucontrol->value.integer.value[0] & mask) << left_shift;141 right = (ucontrol->value.integer.value[1] & mask) << right_shift;142 spin_lock_irqsave(&sb->mixer_lock, flags);143 if (left_reg == right_reg) {144 oleft = snd_sbmixer_read(sb, left_reg);145 left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right;146 change = left != oleft;147 if (change)148 snd_sbmixer_write(sb, left_reg, left);149 } else {150 oleft = snd_sbmixer_read(sb, left_reg);151 oright = snd_sbmixer_read(sb, right_reg);152 left = (oleft & ~(mask << left_shift)) | left;153 right = (oright & ~(mask << right_shift)) | right;154 change = left != oleft || right != oright;155 if (change) {156 snd_sbmixer_write(sb, left_reg, left);157 snd_sbmixer_write(sb, right_reg, right);158 }159 }160 spin_unlock_irqrestore(&sb->mixer_lock, flags);161 return change;162}163 164/*165 * DT-019x / ALS-007 capture/input switch166 */167 168static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)169{170 static const char * const texts[5] = {171 "CD", "Mic", "Line", "Synth", "Master"172 };173 174 return snd_ctl_enum_info(uinfo, 1, 5, texts);175}176 177static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)178{179 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);180 unsigned long flags;181 unsigned char oval;182 183 spin_lock_irqsave(&sb->mixer_lock, flags);184 oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);185 spin_unlock_irqrestore(&sb->mixer_lock, flags);186 switch (oval & 0x07) {187 case SB_DT019X_CAP_CD:188 ucontrol->value.enumerated.item[0] = 0;189 break;190 case SB_DT019X_CAP_MIC:191 ucontrol->value.enumerated.item[0] = 1;192 break;193 case SB_DT019X_CAP_LINE:194 ucontrol->value.enumerated.item[0] = 2;195 break;196 case SB_DT019X_CAP_MAIN:197 ucontrol->value.enumerated.item[0] = 4;198 break;199 /* To record the synth on these cards you must record the main. */200 /* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */201 /* duplicate case labels if left uncommented. */202 /* case SB_DT019X_CAP_SYNTH:203 * ucontrol->value.enumerated.item[0] = 3;204 * break;205 */206 default:207 ucontrol->value.enumerated.item[0] = 4;208 break;209 }210 return 0;211}212 213static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)214{215 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);216 unsigned long flags;217 int change;218 unsigned char nval, oval;219 220 if (ucontrol->value.enumerated.item[0] > 4)221 return -EINVAL;222 switch (ucontrol->value.enumerated.item[0]) {223 case 0:224 nval = SB_DT019X_CAP_CD;225 break;226 case 1:227 nval = SB_DT019X_CAP_MIC;228 break;229 case 2:230 nval = SB_DT019X_CAP_LINE;231 break;232 case 3:233 nval = SB_DT019X_CAP_SYNTH;234 break;235 case 4:236 nval = SB_DT019X_CAP_MAIN;237 break;238 default:239 nval = SB_DT019X_CAP_MAIN;240 }241 spin_lock_irqsave(&sb->mixer_lock, flags);242 oval = snd_sbmixer_read(sb, SB_DT019X_CAPTURE_SW);243 change = nval != oval;244 if (change)245 snd_sbmixer_write(sb, SB_DT019X_CAPTURE_SW, nval);246 spin_unlock_irqrestore(&sb->mixer_lock, flags);247 return change;248}249 250/*251 * ALS4000 mono recording control switch252 */253 254static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol,255 struct snd_ctl_elem_info *uinfo)256{257 static const char * const texts[3] = {258 "L chan only", "R chan only", "L ch/2 + R ch/2"259 };260 261 return snd_ctl_enum_info(uinfo, 1, 3, texts);262}263 264static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol,265 struct snd_ctl_elem_value *ucontrol)266{267 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);268 unsigned long flags;269 unsigned char oval;270 271 spin_lock_irqsave(&sb->mixer_lock, flags);272 oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);273 spin_unlock_irqrestore(&sb->mixer_lock, flags);274 oval >>= 6;275 if (oval > 2)276 oval = 2;277 278 ucontrol->value.enumerated.item[0] = oval;279 return 0;280}281 282static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol,283 struct snd_ctl_elem_value *ucontrol)284{285 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);286 unsigned long flags;287 int change;288 unsigned char nval, oval;289 290 if (ucontrol->value.enumerated.item[0] > 2)291 return -EINVAL;292 spin_lock_irqsave(&sb->mixer_lock, flags);293 oval = snd_sbmixer_read(sb, SB_ALS4000_MONO_IO_CTRL);294 295 nval = (oval & ~(3 << 6))296 | (ucontrol->value.enumerated.item[0] << 6);297 change = nval != oval;298 if (change)299 snd_sbmixer_write(sb, SB_ALS4000_MONO_IO_CTRL, nval);300 spin_unlock_irqrestore(&sb->mixer_lock, flags);301 return change;302}303 304/*305 * SBPRO input multiplexer306 */307 308static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)309{310 static const char * const texts[3] = {311 "Mic", "CD", "Line"312 };313 314 return snd_ctl_enum_info(uinfo, 1, 3, texts);315}316 317 318static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)319{320 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);321 unsigned long flags;322 unsigned char oval;323 324 spin_lock_irqsave(&sb->mixer_lock, flags);325 oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);326 spin_unlock_irqrestore(&sb->mixer_lock, flags);327 switch ((oval >> 0x01) & 0x03) {328 case SB_DSP_MIXS_CD:329 ucontrol->value.enumerated.item[0] = 1;330 break;331 case SB_DSP_MIXS_LINE:332 ucontrol->value.enumerated.item[0] = 2;333 break;334 default:335 ucontrol->value.enumerated.item[0] = 0;336 break;337 }338 return 0;339}340 341static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)342{343 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);344 unsigned long flags;345 int change;346 unsigned char nval, oval;347 348 if (ucontrol->value.enumerated.item[0] > 2)349 return -EINVAL;350 switch (ucontrol->value.enumerated.item[0]) {351 case 1:352 nval = SB_DSP_MIXS_CD;353 break;354 case 2:355 nval = SB_DSP_MIXS_LINE;356 break;357 default:358 nval = SB_DSP_MIXS_MIC;359 }360 nval <<= 1;361 spin_lock_irqsave(&sb->mixer_lock, flags);362 oval = snd_sbmixer_read(sb, SB_DSP_CAPTURE_SOURCE);363 nval |= oval & ~0x06;364 change = nval != oval;365 if (change)366 snd_sbmixer_write(sb, SB_DSP_CAPTURE_SOURCE, nval);367 spin_unlock_irqrestore(&sb->mixer_lock, flags);368 return change;369}370 371/*372 * SB16 input switch373 */374 375static int snd_sb16mixer_info_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)376{377 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;378 uinfo->count = 4;379 uinfo->value.integer.min = 0;380 uinfo->value.integer.max = 1;381 return 0;382}383 384static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)385{386 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);387 unsigned long flags;388 int reg1 = kcontrol->private_value & 0xff;389 int reg2 = (kcontrol->private_value >> 8) & 0xff;390 int left_shift = (kcontrol->private_value >> 16) & 0x0f;391 int right_shift = (kcontrol->private_value >> 24) & 0x0f;392 unsigned char val1, val2;393 394 spin_lock_irqsave(&sb->mixer_lock, flags);395 val1 = snd_sbmixer_read(sb, reg1);396 val2 = snd_sbmixer_read(sb, reg2);397 spin_unlock_irqrestore(&sb->mixer_lock, flags);398 ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01;399 ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01;400 ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01;401 ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01;402 return 0;403} 404 405static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)406{407 struct snd_sb *sb = snd_kcontrol_chip(kcontrol);408 unsigned long flags;409 int reg1 = kcontrol->private_value & 0xff;410 int reg2 = (kcontrol->private_value >> 8) & 0xff;411 int left_shift = (kcontrol->private_value >> 16) & 0x0f;412 int right_shift = (kcontrol->private_value >> 24) & 0x0f;413 int change;414 unsigned char val1, val2, oval1, oval2;415 416 spin_lock_irqsave(&sb->mixer_lock, flags);417 oval1 = snd_sbmixer_read(sb, reg1);418 oval2 = snd_sbmixer_read(sb, reg2);419 val1 = oval1 & ~((1 << left_shift) | (1 << right_shift));420 val2 = oval2 & ~((1 << left_shift) | (1 << right_shift));421 val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift;422 val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift;423 val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift;424 val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift;425 change = val1 != oval1 || val2 != oval2;426 if (change) {427 snd_sbmixer_write(sb, reg1, val1);428 snd_sbmixer_write(sb, reg2, val2);429 }430 spin_unlock_irqrestore(&sb->mixer_lock, flags);431 return change;432}433 434 435/*436 */437/*438 */439int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value)440{441 static const struct snd_kcontrol_new newctls[] = {442 [SB_MIX_SINGLE] = {443 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,444 .info = snd_sbmixer_info_single,445 .get = snd_sbmixer_get_single,446 .put = snd_sbmixer_put_single,447 },448 [SB_MIX_DOUBLE] = {449 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,450 .info = snd_sbmixer_info_double,451 .get = snd_sbmixer_get_double,452 .put = snd_sbmixer_put_double,453 },454 [SB_MIX_INPUT_SW] = {455 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,456 .info = snd_sb16mixer_info_input_sw,457 .get = snd_sb16mixer_get_input_sw,458 .put = snd_sb16mixer_put_input_sw,459 },460 [SB_MIX_CAPTURE_PRO] = {461 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,462 .info = snd_sb8mixer_info_mux,463 .get = snd_sb8mixer_get_mux,464 .put = snd_sb8mixer_put_mux,465 },466 [SB_MIX_CAPTURE_DT019X] = {467 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,468 .info = snd_dt019x_input_sw_info,469 .get = snd_dt019x_input_sw_get,470 .put = snd_dt019x_input_sw_put,471 },472 [SB_MIX_MONO_CAPTURE_ALS4K] = {473 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,474 .info = snd_als4k_mono_capture_route_info,475 .get = snd_als4k_mono_capture_route_get,476 .put = snd_als4k_mono_capture_route_put,477 },478 };479 struct snd_kcontrol *ctl;480 int err;481 482 ctl = snd_ctl_new1(&newctls[type], chip);483 if (! ctl)484 return -ENOMEM;485 strscpy(ctl->id.name, name, sizeof(ctl->id.name));486 ctl->id.index = index;487 ctl->private_value = value;488 err = snd_ctl_add(chip->card, ctl);489 if (err < 0)490 return err;491 return 0;492}493 494/*495 * SB 2.0 specific mixer elements496 */497 498static const struct sbmix_elem snd_sb20_controls[] = {499 SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7),500 SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3),501 SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7),502 SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7)503};504 505static const unsigned char snd_sb20_init_values[][2] = {506 { SB_DSP20_MASTER_DEV, 0 },507 { SB_DSP20_FM_DEV, 0 },508};509 510/*511 * SB Pro specific mixer elements512 */513static const struct sbmix_elem snd_sbpro_controls[] = {514 SB_DOUBLE("Master Playback Volume",515 SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7),516 SB_DOUBLE("PCM Playback Volume",517 SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7),518 SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1),519 SB_DOUBLE("Synth Playback Volume",520 SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7),521 SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7),522 SB_DOUBLE("Line Playback Volume",523 SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7),524 SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3),525 {526 .name = "Capture Source",527 .type = SB_MIX_CAPTURE_PRO528 },529 SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1),530 SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)531};532 533static const unsigned char snd_sbpro_init_values[][2] = {534 { SB_DSP_MASTER_DEV, 0 },535 { SB_DSP_PCM_DEV, 0 },536 { SB_DSP_FM_DEV, 0 },537};538 539/*540 * SB16 specific mixer elements541 */542static const struct sbmix_elem snd_sb16_controls[] = {543 SB_DOUBLE("Master Playback Volume",544 SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),545 SB_DOUBLE("PCM Playback Volume",546 SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31),547 SB16_INPUT_SW("Synth Capture Route",548 SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5),549 SB_DOUBLE("Synth Playback Volume",550 SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31),551 SB16_INPUT_SW("CD Capture Route",552 SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1),553 SB_DOUBLE("CD Playback Switch",554 SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),555 SB_DOUBLE("CD Playback Volume",556 SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31),557 SB16_INPUT_SW("Mic Capture Route",558 SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0),559 SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),560 SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),561 SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3),562 SB_DOUBLE("Capture Volume",563 SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),564 SB_DOUBLE("Playback Volume",565 SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),566 SB16_INPUT_SW("Line Capture Route",567 SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3),568 SB_DOUBLE("Line Playback Switch",569 SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),570 SB_DOUBLE("Line Playback Volume",571 SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),572 SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),573 SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1),574 SB_DOUBLE("Tone Control - Bass",575 SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15),576 SB_DOUBLE("Tone Control - Treble",577 SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15)578};579 580static const unsigned char snd_sb16_init_values[][2] = {581 { SB_DSP4_MASTER_DEV + 0, 0 },582 { SB_DSP4_MASTER_DEV + 1, 0 },583 { SB_DSP4_PCM_DEV + 0, 0 },584 { SB_DSP4_PCM_DEV + 1, 0 },585 { SB_DSP4_SYNTH_DEV + 0, 0 },586 { SB_DSP4_SYNTH_DEV + 1, 0 },587 { SB_DSP4_INPUT_LEFT, 0 },588 { SB_DSP4_INPUT_RIGHT, 0 },589 { SB_DSP4_OUTPUT_SW, 0 },590 { SB_DSP4_SPEAKER_DEV, 0 },591};592 593/*594 * DT019x specific mixer elements595 */596static const struct sbmix_elem snd_dt019x_controls[] = {597 /* ALS4000 below has some parts which we might be lacking,598 * e.g. snd_als4000_ctl_mono_playback_switch - check it! */599 SB_DOUBLE("Master Playback Volume",600 SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4, 0, 15),601 SB_DOUBLE("PCM Playback Switch",602 SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),603 SB_DOUBLE("PCM Playback Volume",604 SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4, 0, 15),605 SB_DOUBLE("Synth Playback Switch",606 SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),607 SB_DOUBLE("Synth Playback Volume",608 SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4, 0, 15),609 SB_DOUBLE("CD Playback Switch",610 SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),611 SB_DOUBLE("CD Playback Volume",612 SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4, 0, 15),613 SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),614 SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7),615 SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0, 7),616 SB_DOUBLE("Line Playback Switch",617 SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),618 SB_DOUBLE("Line Playback Volume",619 SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4, 0, 15),620 {621 .name = "Capture Source",622 .type = SB_MIX_CAPTURE_DT019X623 }624};625 626static const unsigned char snd_dt019x_init_values[][2] = {627 { SB_DT019X_MASTER_DEV, 0 },628 { SB_DT019X_PCM_DEV, 0 },629 { SB_DT019X_SYNTH_DEV, 0 },630 { SB_DT019X_CD_DEV, 0 },631 { SB_DT019X_MIC_DEV, 0 }, /* Includes PC-speaker in high nibble */632 { SB_DT019X_LINE_DEV, 0 },633 { SB_DSP4_OUTPUT_SW, 0 },634 { SB_DT019X_OUTPUT_SW2, 0 },635 { SB_DT019X_CAPTURE_SW, 0x06 },636};637 638/*639 * ALS4000 specific mixer elements640 */641static const struct sbmix_elem snd_als4000_controls[] = {642 SB_DOUBLE("PCM Playback Switch",643 SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),644 SB_DOUBLE("Synth Playback Switch",645 SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),646 SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03),647 SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1),648 {649 .name = "Master Mono Capture Route",650 .type = SB_MIX_MONO_CAPTURE_ALS4K651 },652 SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1),653 SB_SINGLE("Analog Loopback Switch", SB_ALS4000_MIC_IN_GAIN, 7, 0x01),654 SB_SINGLE("3D Control - Switch", SB_ALS4000_3D_SND_FX, 6, 0x01),655 SB_SINGLE("Digital Loopback Switch",656 SB_ALS4000_CR3_CONFIGURATION, 7, 0x01),657 /* FIXME: functionality of 3D controls might be swapped, I didn't find658 * a description of how to identify what is supposed to be what */659 SB_SINGLE("3D Control - Level", SB_ALS4000_3D_SND_FX, 0, 0x07),660 /* FIXME: maybe there's actually some standard 3D ctrl name for it?? */661 SB_SINGLE("3D Control - Freq", SB_ALS4000_3D_SND_FX, 4, 0x03),662 /* FIXME: ALS4000a.pdf mentions BBD (Bucket Brigade Device) time delay,663 * but what ALSA 3D attribute is that actually? "Center", "Depth",664 * "Wide" or "Space" or even "Level"? Assuming "Wide" for now... */665 SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f),666 SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01),667 SB_SINGLE("Master Playback 8kHz / 20kHz LPF Switch",668 SB_ALS4000_FMDAC, 5, 0x01),669#ifdef NOT_AVAILABLE670 SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01),671 SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f),672#endif673};674 675static const unsigned char snd_als4000_init_values[][2] = {676 { SB_DSP4_MASTER_DEV + 0, 0 },677 { SB_DSP4_MASTER_DEV + 1, 0 },678 { SB_DSP4_PCM_DEV + 0, 0 },679 { SB_DSP4_PCM_DEV + 1, 0 },680 { SB_DSP4_SYNTH_DEV + 0, 0 },681 { SB_DSP4_SYNTH_DEV + 1, 0 },682 { SB_DSP4_SPEAKER_DEV, 0 },683 { SB_DSP4_OUTPUT_SW, 0 },684 { SB_DSP4_INPUT_LEFT, 0 },685 { SB_DSP4_INPUT_RIGHT, 0 },686 { SB_DT019X_OUTPUT_SW2, 0 },687 { SB_ALS4000_MIC_IN_GAIN, 0 },688};689 690/*691 */692static int snd_sbmixer_init(struct snd_sb *chip,693 const struct sbmix_elem *controls,694 int controls_count,695 const unsigned char map[][2],696 int map_count,697 char *name)698{699 unsigned long flags;700 struct snd_card *card = chip->card;701 int idx, err;702 703 /* mixer reset */704 spin_lock_irqsave(&chip->mixer_lock, flags);705 snd_sbmixer_write(chip, 0x00, 0x00);706 spin_unlock_irqrestore(&chip->mixer_lock, flags);707 708 /* mute and zero volume channels */709 for (idx = 0; idx < map_count; idx++) {710 spin_lock_irqsave(&chip->mixer_lock, flags);711 snd_sbmixer_write(chip, map[idx][0], map[idx][1]);712 spin_unlock_irqrestore(&chip->mixer_lock, flags);713 }714 715 for (idx = 0; idx < controls_count; idx++) {716 err = snd_sbmixer_add_ctl_elem(chip, &controls[idx]);717 if (err < 0)718 return err;719 }720 snd_component_add(card, name);721 strcpy(card->mixername, name);722 return 0;723}724 725int snd_sbmixer_new(struct snd_sb *chip)726{727 struct snd_card *card;728 int err;729 730 if (snd_BUG_ON(!chip || !chip->card))731 return -EINVAL;732 733 card = chip->card;734 735 switch (chip->hardware) {736 case SB_HW_10:737 return 0; /* no mixer chip on SB1.x */738 case SB_HW_20:739 case SB_HW_201:740 err = snd_sbmixer_init(chip,741 snd_sb20_controls,742 ARRAY_SIZE(snd_sb20_controls),743 snd_sb20_init_values,744 ARRAY_SIZE(snd_sb20_init_values),745 "CTL1335");746 if (err < 0)747 return err;748 break;749 case SB_HW_PRO:750 case SB_HW_JAZZ16:751 err = snd_sbmixer_init(chip,752 snd_sbpro_controls,753 ARRAY_SIZE(snd_sbpro_controls),754 snd_sbpro_init_values,755 ARRAY_SIZE(snd_sbpro_init_values),756 "CTL1345");757 if (err < 0)758 return err;759 break;760 case SB_HW_16:761 case SB_HW_ALS100:762 case SB_HW_CS5530:763 err = snd_sbmixer_init(chip,764 snd_sb16_controls,765 ARRAY_SIZE(snd_sb16_controls),766 snd_sb16_init_values,767 ARRAY_SIZE(snd_sb16_init_values),768 "CTL1745");769 if (err < 0)770 return err;771 break;772 case SB_HW_ALS4000:773 /* use only the first 16 controls from SB16 */774 err = snd_sbmixer_init(chip,775 snd_sb16_controls,776 16,777 snd_sb16_init_values,778 ARRAY_SIZE(snd_sb16_init_values),779 "ALS4000");780 if (err < 0)781 return err;782 err = snd_sbmixer_init(chip,783 snd_als4000_controls,784 ARRAY_SIZE(snd_als4000_controls),785 snd_als4000_init_values,786 ARRAY_SIZE(snd_als4000_init_values),787 "ALS4000");788 if (err < 0)789 return err;790 break;791 case SB_HW_DT019X:792 err = snd_sbmixer_init(chip,793 snd_dt019x_controls,794 ARRAY_SIZE(snd_dt019x_controls),795 snd_dt019x_init_values,796 ARRAY_SIZE(snd_dt019x_init_values),797 "DT019X");798 if (err < 0)799 return err;800 break;801 default:802 strcpy(card->mixername, "???");803 }804 return 0;805}806 807#ifdef CONFIG_PM808static const unsigned char sb20_saved_regs[] = {809 SB_DSP20_MASTER_DEV,810 SB_DSP20_PCM_DEV,811 SB_DSP20_FM_DEV,812 SB_DSP20_CD_DEV,813};814 815static const unsigned char sbpro_saved_regs[] = {816 SB_DSP_MASTER_DEV,817 SB_DSP_PCM_DEV,818 SB_DSP_PLAYBACK_FILT,819 SB_DSP_FM_DEV,820 SB_DSP_CD_DEV,821 SB_DSP_LINE_DEV,822 SB_DSP_MIC_DEV,823 SB_DSP_CAPTURE_SOURCE,824 SB_DSP_CAPTURE_FILT,825};826 827static const unsigned char sb16_saved_regs[] = {828 SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,829 SB_DSP4_3DSE,830 SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1,831 SB_DSP4_TREBLE_DEV, SB_DSP4_TREBLE_DEV + 1,832 SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,833 SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,834 SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,835 SB_DSP4_OUTPUT_SW,836 SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,837 SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1,838 SB_DSP4_MIC_DEV,839 SB_DSP4_SPEAKER_DEV,840 SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,841 SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,842 SB_DSP4_MIC_AGC843};844 845static const unsigned char dt019x_saved_regs[] = {846 SB_DT019X_MASTER_DEV,847 SB_DT019X_PCM_DEV,848 SB_DT019X_SYNTH_DEV,849 SB_DT019X_CD_DEV,850 SB_DT019X_MIC_DEV,851 SB_DT019X_SPKR_DEV,852 SB_DT019X_LINE_DEV,853 SB_DSP4_OUTPUT_SW,854 SB_DT019X_OUTPUT_SW2,855 SB_DT019X_CAPTURE_SW,856};857 858static const unsigned char als4000_saved_regs[] = {859 /* please verify in dsheet whether regs to be added860 are actually real H/W or just dummy */861 SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1,862 SB_DSP4_OUTPUT_SW,863 SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1,864 SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT,865 SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1,866 SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1,867 SB_DSP4_MIC_DEV,868 SB_DSP4_SPEAKER_DEV,869 SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1,870 SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1,871 SB_DT019X_OUTPUT_SW2,872 SB_ALS4000_MONO_IO_CTRL,873 SB_ALS4000_MIC_IN_GAIN,874 SB_ALS4000_FMDAC,875 SB_ALS4000_3D_SND_FX,876 SB_ALS4000_3D_TIME_DELAY,877 SB_ALS4000_CR3_CONFIGURATION,878};879 880static void save_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs)881{882 unsigned char *val = chip->saved_regs;883 if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))884 return;885 for (; num_regs; num_regs--)886 *val++ = snd_sbmixer_read(chip, *regs++);887}888 889static void restore_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs)890{891 unsigned char *val = chip->saved_regs;892 if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs)))893 return;894 for (; num_regs; num_regs--)895 snd_sbmixer_write(chip, *regs++, *val++);896}897 898void snd_sbmixer_suspend(struct snd_sb *chip)899{900 switch (chip->hardware) {901 case SB_HW_20:902 case SB_HW_201:903 save_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));904 break;905 case SB_HW_PRO:906 case SB_HW_JAZZ16:907 save_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));908 break;909 case SB_HW_16:910 case SB_HW_ALS100:911 case SB_HW_CS5530:912 save_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));913 break;914 case SB_HW_ALS4000:915 save_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));916 break;917 case SB_HW_DT019X:918 save_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));919 break;920 default:921 break;922 }923}924 925void snd_sbmixer_resume(struct snd_sb *chip)926{927 switch (chip->hardware) {928 case SB_HW_20:929 case SB_HW_201:930 restore_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));931 break;932 case SB_HW_PRO:933 case SB_HW_JAZZ16:934 restore_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));935 break;936 case SB_HW_16:937 case SB_HW_ALS100:938 case SB_HW_CS5530:939 restore_mixer(chip, sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs));940 break;941 case SB_HW_ALS4000:942 restore_mixer(chip, als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs));943 break;944 case SB_HW_DT019X:945 restore_mixer(chip, dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs));946 break;947 default:948 break;949 }950}951#endif952