1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 4 * Routines for control of ICS 2101 chip and "mixer" in GF1 chip 5 */ 6 7 #include <linux/time.h> 8 #include <linux/wait.h> 9 #include <sound/core.h> 10 #include <sound/control.h> 11 #include <sound/gus.h> 12 13 /* 14 * 15 */ 16 17 #define GF1_SINGLE(xname, xindex, shift, invert) \ 18 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 19 .info = snd_gf1_info_single, \ 20 .get = snd_gf1_get_single, .put = snd_gf1_put_single, \ 21 .private_value = shift | (invert << 8) } 22 23 #define snd_gf1_info_single snd_ctl_boolean_mono_info 24 25 static int snd_gf1_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 26 { 27 struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); 28 int shift = kcontrol->private_value & 0xff; 29 int invert = (kcontrol->private_value >> 8) & 1; 30 31 ucontrol->value.integer.value[0] = (gus->mix_cntrl_reg >> shift) & 1; 32 if (invert) 33 ucontrol->value.integer.value[0] ^= 1; 34 return 0; 35 } 36 37 static int snd_gf1_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 38 { 39 struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); 40 unsigned long flags; 41 int shift = kcontrol->private_value & 0xff; 42 int invert = (kcontrol->private_value >> 8) & 1; 43 int change; 44 unsigned char oval, nval; 45 46 nval = ucontrol->value.integer.value[0] & 1; 47 if (invert) 48 nval ^= 1; 49 nval <<= shift; 50 spin_lock_irqsave(&gus->reg_lock, flags); 51 oval = gus->mix_cntrl_reg; 52 nval = (oval & ~(1 << shift)) | nval; 53 change = nval != oval; 54 outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG)); 55 outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); 56 spin_unlock_irqrestore(&gus->reg_lock, flags); 57 return change; 58 } 59 60 #define ICS_DOUBLE(xname, xindex, addr) \ 61 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 62 .info = snd_ics_info_double, \ 63 .get = snd_ics_get_double, .put = snd_ics_put_double, \ 64 .private_value = addr } 65 66 static int snd_ics_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 67 { 68 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 69 uinfo->count = 2; 70 uinfo->value.integer.min = 0; 71 uinfo->value.integer.max = 127; 72 return 0; 73 } 74 75 static int snd_ics_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 76 { 77 struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); 78 unsigned long flags; 79 int addr = kcontrol->private_value & 0xff; 80 unsigned char left, right; 81 82 spin_lock_irqsave(&gus->reg_lock, flags); 83 left = gus->gf1.ics_regs[addr][0]; 84 right = gus->gf1.ics_regs[addr][1]; 85 spin_unlock_irqrestore(&gus->reg_lock, flags); 86 ucontrol->value.integer.value[0] = left & 127; 87 ucontrol->value.integer.value[1] = right & 127; 88 return 0; 89 } 90 91 static int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 92 { 93 struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); 94 unsigned long flags; 95 int addr = kcontrol->private_value & 0xff; 96 int change; 97 unsigned char val1, val2, oval1, oval2; 98 99 val1 = ucontrol->value.integer.value[0] & 127; 100 val2 = ucontrol->value.integer.value[1] & 127; 101 spin_lock_irqsave(&gus->reg_lock, flags); 102 oval1 = gus->gf1.ics_regs[addr][0]; 103 oval2 = gus->gf1.ics_regs[addr][1]; 104 change = val1 != oval1 || val2 != oval2; 105 gus->gf1.ics_regs[addr][0] = val1; 106 gus->gf1.ics_regs[addr][1] = val2; 107 if (gus->ics_flag && gus->ics_flipped && 108 (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) 109 swap(val1, val2); 110 addr <<= 3; 111 outb(addr | 0, GUSP(gus, MIXCNTRLPORT)); 112 outb(1, GUSP(gus, MIXDATAPORT)); 113 outb(addr | 2, GUSP(gus, MIXCNTRLPORT)); 114 outb((unsigned char) val1, GUSP(gus, MIXDATAPORT)); 115 outb(addr | 1, GUSP(gus, MIXCNTRLPORT)); 116 outb(2, GUSP(gus, MIXDATAPORT)); 117 outb(addr | 3, GUSP(gus, MIXCNTRLPORT)); 118 outb((unsigned char) val2, GUSP(gus, MIXDATAPORT)); 119 spin_unlock_irqrestore(&gus->reg_lock, flags); 120 return change; 121 } 122 123 static const struct snd_kcontrol_new snd_gf1_controls[] = { 124 GF1_SINGLE("Master Playback Switch", 0, 1, 1), 125 GF1_SINGLE("Line Switch", 0, 0, 1), 126 GF1_SINGLE("Mic Switch", 0, 2, 0) 127 }; 128 129 static const struct snd_kcontrol_new snd_ics_controls[] = { 130 GF1_SINGLE("Master Playback Switch", 0, 1, 1), 131 ICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV), 132 ICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV), 133 GF1_SINGLE("Line Switch", 0, 0, 1), 134 ICS_DOUBLE("Line Playback Volume", 0, SNDRV_ICS_LINE_DEV), 135 GF1_SINGLE("Mic Switch", 0, 2, 0), 136 ICS_DOUBLE("Mic Playback Volume", 0, SNDRV_ICS_MIC_DEV), 137 ICS_DOUBLE("CD Playback Volume", 0, SNDRV_ICS_CD_DEV) 138 }; 139 140 int snd_gf1_new_mixer(struct snd_gus_card * gus) 141 { 142 struct snd_card *card; 143 unsigned int idx, max; 144 int err; 145 146 if (snd_BUG_ON(!gus)) 147 return -EINVAL; 148 card = gus->card; 149 if (snd_BUG_ON(!card)) 150 return -EINVAL; 151 152 if (gus->ics_flag) 153 snd_component_add(card, "ICS2101"); 154 if (card->mixername[0] == '\0') { 155 strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); 156 } else { 157 if (gus->ics_flag) 158 strcat(card->mixername, ",ICS2101"); 159 strcat(card->mixername, ",GF1"); 160 } 161 162 if (!gus->ics_flag) { 163 max = gus->ess_flag ? 1 : ARRAY_SIZE(snd_gf1_controls); 164 for (idx = 0; idx < max; idx++) { 165 err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus)); 166 if (err < 0) 167 return err; 168 } 169 } else { 170 for (idx = 0; idx < ARRAY_SIZE(snd_ics_controls); idx++) { 171 err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus)); 172 if (err < 0) 173 return err; 174 } 175 } 176 return 0; 177 } 178
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.