1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /*************************************************************************** 3 msnd_pinnacle_mixer.c - description 4 ------------------- 5 begin : Fre Jun 7 2002 6 copyright : (C) 2002 by karsten wiese 7 email : annabellesgarden@yahoo.de 8 ***************************************************************************/ 9 10 /*************************************************************************** 11 * * 12 * * 13 ***************************************************************************/ 14 15 #include <linux/io.h> 16 #include <linux/export.h> 17 18 #include <sound/core.h> 19 #include <sound/control.h> 20 #include "msnd.h" 21 #include "msnd_pinnacle.h" 22 23 24 #define MSND_MIXER_VOLUME 0 25 #define MSND_MIXER_PCM 1 26 #define MSND_MIXER_AUX 2 /* Input source 1 (aux1) */ 27 #define MSND_MIXER_IMIX 3 /* Recording monitor */ 28 #define MSND_MIXER_SYNTH 4 29 #define MSND_MIXER_SPEAKER 5 30 #define MSND_MIXER_LINE 6 31 #define MSND_MIXER_MIC 7 32 #define MSND_MIXER_RECLEV 11 /* Recording level */ 33 #define MSND_MIXER_IGAIN 12 /* Input gain */ 34 #define MSND_MIXER_OGAIN 13 /* Output gain */ 35 #define MSND_MIXER_DIGITAL 17 /* Digital (input) 1 */ 36 37 /* Device mask bits */ 38 39 #define MSND_MASK_VOLUME (1 << MSND_MIXER_VOLUME) 40 #define MSND_MASK_SYNTH (1 << MSND_MIXER_SYNTH) 41 #define MSND_MASK_PCM (1 << MSND_MIXER_PCM) 42 #define MSND_MASK_SPEAKER (1 << MSND_MIXER_SPEAKER) 43 #define MSND_MASK_LINE (1 << MSND_MIXER_LINE) 44 #define MSND_MASK_MIC (1 << MSND_MIXER_MIC) 45 #define MSND_MASK_IMIX (1 << MSND_MIXER_IMIX) 46 #define MSND_MASK_RECLEV (1 << MSND_MIXER_RECLEV) 47 #define MSND_MASK_IGAIN (1 << MSND_MIXER_IGAIN) 48 #define MSND_MASK_OGAIN (1 << MSND_MIXER_OGAIN) 49 #define MSND_MASK_AUX (1 << MSND_MIXER_AUX) 50 #define MSND_MASK_DIGITAL (1 << MSND_MIXER_DIGITAL) 51 52 static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol, 53 struct snd_ctl_elem_info *uinfo) 54 { 55 static const char * const texts[3] = { 56 "Analog", "MASS", "SPDIF", 57 }; 58 struct snd_msnd *chip = snd_kcontrol_chip(kcontrol); 59 unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2; 60 61 return snd_ctl_enum_info(uinfo, 1, items, texts); 62 } 63 64 static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol, 65 struct snd_ctl_elem_value *ucontrol) 66 { 67 struct snd_msnd *chip = snd_kcontrol_chip(kcontrol); 68 /* MSND_MASK_IMIX is the default */ 69 ucontrol->value.enumerated.item[0] = 0; 70 71 if (chip->recsrc & MSND_MASK_SYNTH) { 72 ucontrol->value.enumerated.item[0] = 1; 73 } else if ((chip->recsrc & MSND_MASK_DIGITAL) && 74 test_bit(F_HAVEDIGITAL, &chip->flags)) { 75 ucontrol->value.enumerated.item[0] = 2; 76 } 77 78 79 return 0; 80 } 81 82 static int snd_msndmix_set_mux(struct snd_msnd *chip, int val) 83 { 84 unsigned newrecsrc; 85 int change; 86 unsigned char msndbyte; 87 88 switch (val) { 89 case 0: 90 newrecsrc = MSND_MASK_IMIX; 91 msndbyte = HDEXAR_SET_ANA_IN; 92 break; 93 case 1: 94 newrecsrc = MSND_MASK_SYNTH; 95 msndbyte = HDEXAR_SET_SYNTH_IN; 96 break; 97 case 2: 98 newrecsrc = MSND_MASK_DIGITAL; 99 msndbyte = HDEXAR_SET_DAT_IN; 100 break; 101 default: 102 return -EINVAL; 103 } 104 change = newrecsrc != chip->recsrc; 105 if (change) { 106 change = 0; 107 if (!snd_msnd_send_word(chip, 0, 0, msndbyte)) 108 if (!snd_msnd_send_dsp_cmd(chip, HDEX_AUX_REQ)) { 109 chip->recsrc = newrecsrc; 110 change = 1; 111 } 112 } 113 return change; 114 } 115 116 static int snd_msndmix_put_mux(struct snd_kcontrol *kcontrol, 117 struct snd_ctl_elem_value *ucontrol) 118 { 119 struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol); 120 return snd_msndmix_set_mux(msnd, ucontrol->value.enumerated.item[0]); 121 } 122 123 124 static int snd_msndmix_volume_info(struct snd_kcontrol *kcontrol, 125 struct snd_ctl_elem_info *uinfo) 126 { 127 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 128 uinfo->count = 2; 129 uinfo->value.integer.min = 0; 130 uinfo->value.integer.max = 100; 131 return 0; 132 } 133 134 static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol, 135 struct snd_ctl_elem_value *ucontrol) 136 { 137 struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol); 138 int addr = kcontrol->private_value; 139 unsigned long flags; 140 141 spin_lock_irqsave(&msnd->mixer_lock, flags); 142 ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100; 143 ucontrol->value.integer.value[0] /= 0xFFFF; 144 ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100; 145 ucontrol->value.integer.value[1] /= 0xFFFF; 146 spin_unlock_irqrestore(&msnd->mixer_lock, flags); 147 return 0; 148 } 149 150 #define update_volm(a, b) \ 151 do { \ 152 writew((dev->left_levels[a] >> 1) * \ 153 readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \ 154 dev->SMA + SMA_##b##Left); \ 155 writew((dev->right_levels[a] >> 1) * \ 156 readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \ 157 dev->SMA + SMA_##b##Right); \ 158 } while (0); 159 160 #define update_potm(d, s, ar) \ 161 do { \ 162 writeb((dev->left_levels[d] >> 8) * \ 163 readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \ 164 dev->SMA + SMA_##s##Left); \ 165 writeb((dev->right_levels[d] >> 8) * \ 166 readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \ 167 dev->SMA + SMA_##s##Right); \ 168 if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \ 169 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \ 170 } while (0); 171 172 #define update_pot(d, s, ar) \ 173 do { \ 174 writeb(dev->left_levels[d] >> 8, \ 175 dev->SMA + SMA_##s##Left); \ 176 writeb(dev->right_levels[d] >> 8, \ 177 dev->SMA + SMA_##s##Right); \ 178 if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \ 179 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \ 180 } while (0); 181 182 183 static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right) 184 { 185 int bLeft, bRight; 186 int wLeft, wRight; 187 int updatemaster = 0; 188 189 if (d >= LEVEL_ENTRIES) 190 return -EINVAL; 191 192 bLeft = left * 0xff / 100; 193 wLeft = left * 0xffff / 100; 194 195 bRight = right * 0xff / 100; 196 wRight = right * 0xffff / 100; 197 198 dev->left_levels[d] = wLeft; 199 dev->right_levels[d] = wRight; 200 201 switch (d) { 202 /* master volume unscaled controls */ 203 case MSND_MIXER_LINE: /* line pot control */ 204 /* scaled by IMIX in digital mix */ 205 writeb(bLeft, dev->SMA + SMA_bInPotPosLeft); 206 writeb(bRight, dev->SMA + SMA_bInPotPosRight); 207 if (snd_msnd_send_word(dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) 208 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); 209 break; 210 case MSND_MIXER_MIC: /* mic pot control */ 211 if (dev->type == msndClassic) 212 return -EINVAL; 213 /* scaled by IMIX in digital mix */ 214 writeb(bLeft, dev->SMA + SMA_bMicPotPosLeft); 215 writeb(bRight, dev->SMA + SMA_bMicPotPosRight); 216 if (snd_msnd_send_word(dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0) 217 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); 218 break; 219 case MSND_MIXER_VOLUME: /* master volume */ 220 writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft); 221 writew(wRight, dev->SMA + SMA_wCurrMastVolRight); 222 fallthrough; 223 case MSND_MIXER_AUX: /* aux pot control */ 224 /* scaled by master volume */ 225 226 /* digital controls */ 227 case MSND_MIXER_SYNTH: /* synth vol (dsp mix) */ 228 case MSND_MIXER_PCM: /* pcm vol (dsp mix) */ 229 case MSND_MIXER_IMIX: /* input monitor (dsp mix) */ 230 /* scaled by master volume */ 231 updatemaster = 1; 232 break; 233 234 default: 235 return -EINVAL; 236 } 237 238 if (updatemaster) { 239 /* update master volume scaled controls */ 240 update_volm(MSND_MIXER_PCM, wCurrPlayVol); 241 update_volm(MSND_MIXER_IMIX, wCurrInVol); 242 if (dev->type == msndPinnacle) 243 update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol); 244 update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); 245 } 246 247 return 0; 248 } 249 250 static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol, 251 struct snd_ctl_elem_value *ucontrol) 252 { 253 struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol); 254 int change, addr = kcontrol->private_value; 255 int left, right; 256 unsigned long flags; 257 258 left = ucontrol->value.integer.value[0] % 101; 259 right = ucontrol->value.integer.value[1] % 101; 260 spin_lock_irqsave(&msnd->mixer_lock, flags); 261 change = msnd->left_levels[addr] != left 262 || msnd->right_levels[addr] != right; 263 snd_msndmix_set(msnd, addr, left, right); 264 spin_unlock_irqrestore(&msnd->mixer_lock, flags); 265 return change; 266 } 267 268 269 #define DUMMY_VOLUME(xname, xindex, addr) \ 270 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 271 .info = snd_msndmix_volume_info, \ 272 .get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \ 273 .private_value = addr } 274 275 276 static const struct snd_kcontrol_new snd_msnd_controls[] = { 277 DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME), 278 DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM), 279 DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX), 280 DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE), 281 DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC), 282 DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX), 283 { 284 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 285 .name = "Capture Source", 286 .info = snd_msndmix_info_mux, 287 .get = snd_msndmix_get_mux, 288 .put = snd_msndmix_put_mux, 289 } 290 }; 291 292 293 int snd_msndmix_new(struct snd_card *card) 294 { 295 struct snd_msnd *chip = card->private_data; 296 unsigned int idx; 297 int err; 298 299 if (snd_BUG_ON(!chip)) 300 return -EINVAL; 301 spin_lock_init(&chip->mixer_lock); 302 strcpy(card->mixername, "MSND Pinnacle Mixer"); 303 304 for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) { 305 err = snd_ctl_add(card, 306 snd_ctl_new1(snd_msnd_controls + idx, chip)); 307 if (err < 0) 308 return err; 309 } 310 311 return 0; 312 } 313 EXPORT_SYMBOL(snd_msndmix_new); 314 315 void snd_msndmix_setup(struct snd_msnd *dev) 316 { 317 update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); 318 update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); 319 update_volm(MSND_MIXER_PCM, wCurrPlayVol); 320 update_volm(MSND_MIXER_IMIX, wCurrInVol); 321 if (dev->type == msndPinnacle) { 322 update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS); 323 update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol); 324 } 325 } 326 EXPORT_SYMBOL(snd_msndmix_setup); 327 328 int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc) 329 { 330 dev->recsrc = -1; 331 return snd_msndmix_set_mux(dev, recsrc); 332 } 333 EXPORT_SYMBOL(snd_msndmix_force_recsrc); 334
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.