FILE
msnd_pinnacle_mixer.c
File preview with syntax highlighting.
linux-6.19.14/sound/isa/msnd/msnd_pinnacle_mixer.c
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 52static 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 64static 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 82static 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 116static 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 124static 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 134static 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 140 guard(spinlock_irqsave)(&msnd->mixer_lock); 141 ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100; 142 ucontrol->value.integer.value[0] /= 0xFFFF; 143 ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100; 144 ucontrol->value.integer.value[1] /= 0xFFFF; 145 return 0; 146} 147 148#define update_volm(a, b) \ 149 do { \ 150 writew((dev->left_levels[a] >> 1) * \ 151 readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \ 152 dev->SMA + SMA_##b##Left); \ 153 writew((dev->right_levels[a] >> 1) * \ 154 readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \ 155 dev->SMA + SMA_##b##Right); \ 156 } while (0); 157 158#define update_potm(d, s, ar) \ 159 do { \ 160 writeb((dev->left_levels[d] >> 8) * \ 161 readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \ 162 dev->SMA + SMA_##s##Left); \ 163 writeb((dev->right_levels[d] >> 8) * \ 164 readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \ 165 dev->SMA + SMA_##s##Right); \ 166 if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \ 167 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \ 168 } while (0); 169 170#define update_pot(d, s, ar) \ 171 do { \ 172 writeb(dev->left_levels[d] >> 8, \ 173 dev->SMA + SMA_##s##Left); \ 174 writeb(dev->right_levels[d] >> 8, \ 175 dev->SMA + SMA_##s##Right); \ 176 if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \ 177 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \ 178 } while (0); 179 180 181static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right) 182{ 183 int bLeft, bRight; 184 int wLeft, wRight; 185 int updatemaster = 0; 186 187 if (d >= LEVEL_ENTRIES) 188 return -EINVAL; 189 190 bLeft = left * 0xff / 100; 191 wLeft = left * 0xffff / 100; 192 193 bRight = right * 0xff / 100; 194 wRight = right * 0xffff / 100; 195 196 dev->left_levels[d] = wLeft; 197 dev->right_levels[d] = wRight; 198 199 switch (d) { 200 /* master volume unscaled controls */ 201 case MSND_MIXER_LINE: /* line pot control */ 202 /* scaled by IMIX in digital mix */ 203 writeb(bLeft, dev->SMA + SMA_bInPotPosLeft); 204 writeb(bRight, dev->SMA + SMA_bInPotPosRight); 205 if (snd_msnd_send_word(dev, 0, 0, HDEXAR_IN_SET_POTS) == 0) 206 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); 207 break; 208 case MSND_MIXER_MIC: /* mic pot control */ 209 if (dev->type == msndClassic) 210 return -EINVAL; 211 /* scaled by IMIX in digital mix */ 212 writeb(bLeft, dev->SMA + SMA_bMicPotPosLeft); 213 writeb(bRight, dev->SMA + SMA_bMicPotPosRight); 214 if (snd_msnd_send_word(dev, 0, 0, HDEXAR_MIC_SET_POTS) == 0) 215 snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); 216 break; 217 case MSND_MIXER_VOLUME: /* master volume */ 218 writew(wLeft, dev->SMA + SMA_wCurrMastVolLeft); 219 writew(wRight, dev->SMA + SMA_wCurrMastVolRight); 220 fallthrough; 221 case MSND_MIXER_AUX: /* aux pot control */ 222 /* scaled by master volume */ 223 224 /* digital controls */ 225 case MSND_MIXER_SYNTH: /* synth vol (dsp mix) */ 226 case MSND_MIXER_PCM: /* pcm vol (dsp mix) */ 227 case MSND_MIXER_IMIX: /* input monitor (dsp mix) */ 228 /* scaled by master volume */ 229 updatemaster = 1; 230 break; 231 232 default: 233 return -EINVAL; 234 } 235 236 if (updatemaster) { 237 /* update master volume scaled controls */ 238 update_volm(MSND_MIXER_PCM, wCurrPlayVol); 239 update_volm(MSND_MIXER_IMIX, wCurrInVol); 240 if (dev->type == msndPinnacle) 241 update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol); 242 update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); 243 } 244 245 return 0; 246} 247 248static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol, 249 struct snd_ctl_elem_value *ucontrol) 250{ 251 struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol); 252 int change, addr = kcontrol->private_value; 253 int left, right; 254 255 left = ucontrol->value.integer.value[0] % 101; 256 right = ucontrol->value.integer.value[1] % 101; 257 guard(spinlock_irqsave)(&msnd->mixer_lock); 258 change = msnd->left_levels[addr] != left 259 || msnd->right_levels[addr] != right; 260 snd_msndmix_set(msnd, addr, left, right); 261 return change; 262} 263 264 265#define DUMMY_VOLUME(xname, xindex, addr) \ 266{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 267 .info = snd_msndmix_volume_info, \ 268 .get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \ 269 .private_value = addr } 270 271 272static const struct snd_kcontrol_new snd_msnd_controls[] = { 273DUMMY_VOLUME("Master Volume", 0, MSND_MIXER_VOLUME), 274DUMMY_VOLUME("PCM Volume", 0, MSND_MIXER_PCM), 275DUMMY_VOLUME("Aux Volume", 0, MSND_MIXER_AUX), 276DUMMY_VOLUME("Line Volume", 0, MSND_MIXER_LINE), 277DUMMY_VOLUME("Mic Volume", 0, MSND_MIXER_MIC), 278DUMMY_VOLUME("Monitor", 0, MSND_MIXER_IMIX), 279{ 280 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 281 .name = "Capture Source", 282 .info = snd_msndmix_info_mux, 283 .get = snd_msndmix_get_mux, 284 .put = snd_msndmix_put_mux, 285} 286}; 287 288 289int snd_msndmix_new(struct snd_card *card) 290{ 291 struct snd_msnd *chip = card->private_data; 292 unsigned int idx; 293 int err; 294 295 if (snd_BUG_ON(!chip)) 296 return -EINVAL; 297 spin_lock_init(&chip->mixer_lock); 298 strscpy(card->mixername, "MSND Pinnacle Mixer"); 299 300 for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) { 301 err = snd_ctl_add(card, 302 snd_ctl_new1(snd_msnd_controls + idx, chip)); 303 if (err < 0) 304 return err; 305 } 306 307 return 0; 308} 309EXPORT_SYMBOL(snd_msndmix_new); 310 311void snd_msndmix_setup(struct snd_msnd *dev) 312{ 313 update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); 314 update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); 315 update_volm(MSND_MIXER_PCM, wCurrPlayVol); 316 update_volm(MSND_MIXER_IMIX, wCurrInVol); 317 if (dev->type == msndPinnacle) { 318 update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS); 319 update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol); 320 } 321} 322EXPORT_SYMBOL(snd_msndmix_setup); 323 324int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc) 325{ 326 dev->recsrc = -1; 327 return snd_msndmix_set_mux(dev, recsrc); 328} 329EXPORT_SYMBOL(snd_msndmix_force_recsrc);
~/
Read-only file preview.