162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 462306a36Sopenharmony_ci * Routines for control of ICS 2101 chip and "mixer" in GF1 chip 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/time.h> 862306a36Sopenharmony_ci#include <linux/wait.h> 962306a36Sopenharmony_ci#include <sound/core.h> 1062306a36Sopenharmony_ci#include <sound/control.h> 1162306a36Sopenharmony_ci#include <sound/gus.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define GF1_SINGLE(xname, xindex, shift, invert) \ 1862306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 1962306a36Sopenharmony_ci .info = snd_gf1_info_single, \ 2062306a36Sopenharmony_ci .get = snd_gf1_get_single, .put = snd_gf1_put_single, \ 2162306a36Sopenharmony_ci .private_value = shift | (invert << 8) } 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define snd_gf1_info_single snd_ctl_boolean_mono_info 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int snd_gf1_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); 2862306a36Sopenharmony_ci int shift = kcontrol->private_value & 0xff; 2962306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 8) & 1; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci ucontrol->value.integer.value[0] = (gus->mix_cntrl_reg >> shift) & 1; 3262306a36Sopenharmony_ci if (invert) 3362306a36Sopenharmony_ci ucontrol->value.integer.value[0] ^= 1; 3462306a36Sopenharmony_ci return 0; 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic int snd_gf1_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); 4062306a36Sopenharmony_ci unsigned long flags; 4162306a36Sopenharmony_ci int shift = kcontrol->private_value & 0xff; 4262306a36Sopenharmony_ci int invert = (kcontrol->private_value >> 8) & 1; 4362306a36Sopenharmony_ci int change; 4462306a36Sopenharmony_ci unsigned char oval, nval; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci nval = ucontrol->value.integer.value[0] & 1; 4762306a36Sopenharmony_ci if (invert) 4862306a36Sopenharmony_ci nval ^= 1; 4962306a36Sopenharmony_ci nval <<= shift; 5062306a36Sopenharmony_ci spin_lock_irqsave(&gus->reg_lock, flags); 5162306a36Sopenharmony_ci oval = gus->mix_cntrl_reg; 5262306a36Sopenharmony_ci nval = (oval & ~(1 << shift)) | nval; 5362306a36Sopenharmony_ci change = nval != oval; 5462306a36Sopenharmony_ci outb(gus->mix_cntrl_reg = nval, GUSP(gus, MIXCNTRLREG)); 5562306a36Sopenharmony_ci outb(gus->gf1.active_voice = 0, GUSP(gus, GF1PAGE)); 5662306a36Sopenharmony_ci spin_unlock_irqrestore(&gus->reg_lock, flags); 5762306a36Sopenharmony_ci return change; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci#define ICS_DOUBLE(xname, xindex, addr) \ 6162306a36Sopenharmony_ci{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ 6262306a36Sopenharmony_ci .info = snd_ics_info_double, \ 6362306a36Sopenharmony_ci .get = snd_ics_get_double, .put = snd_ics_put_double, \ 6462306a36Sopenharmony_ci .private_value = addr } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic int snd_ics_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 6962306a36Sopenharmony_ci uinfo->count = 2; 7062306a36Sopenharmony_ci uinfo->value.integer.min = 0; 7162306a36Sopenharmony_ci uinfo->value.integer.max = 127; 7262306a36Sopenharmony_ci return 0; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int snd_ics_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); 7862306a36Sopenharmony_ci unsigned long flags; 7962306a36Sopenharmony_ci int addr = kcontrol->private_value & 0xff; 8062306a36Sopenharmony_ci unsigned char left, right; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci spin_lock_irqsave(&gus->reg_lock, flags); 8362306a36Sopenharmony_ci left = gus->gf1.ics_regs[addr][0]; 8462306a36Sopenharmony_ci right = gus->gf1.ics_regs[addr][1]; 8562306a36Sopenharmony_ci spin_unlock_irqrestore(&gus->reg_lock, flags); 8662306a36Sopenharmony_ci ucontrol->value.integer.value[0] = left & 127; 8762306a36Sopenharmony_ci ucontrol->value.integer.value[1] = right & 127; 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int snd_ics_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct snd_gus_card *gus = snd_kcontrol_chip(kcontrol); 9462306a36Sopenharmony_ci unsigned long flags; 9562306a36Sopenharmony_ci int addr = kcontrol->private_value & 0xff; 9662306a36Sopenharmony_ci int change; 9762306a36Sopenharmony_ci unsigned char val1, val2, oval1, oval2; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci val1 = ucontrol->value.integer.value[0] & 127; 10062306a36Sopenharmony_ci val2 = ucontrol->value.integer.value[1] & 127; 10162306a36Sopenharmony_ci spin_lock_irqsave(&gus->reg_lock, flags); 10262306a36Sopenharmony_ci oval1 = gus->gf1.ics_regs[addr][0]; 10362306a36Sopenharmony_ci oval2 = gus->gf1.ics_regs[addr][1]; 10462306a36Sopenharmony_ci change = val1 != oval1 || val2 != oval2; 10562306a36Sopenharmony_ci gus->gf1.ics_regs[addr][0] = val1; 10662306a36Sopenharmony_ci gus->gf1.ics_regs[addr][1] = val2; 10762306a36Sopenharmony_ci if (gus->ics_flag && gus->ics_flipped && 10862306a36Sopenharmony_ci (addr == SNDRV_ICS_GF1_DEV || addr == SNDRV_ICS_MASTER_DEV)) 10962306a36Sopenharmony_ci swap(val1, val2); 11062306a36Sopenharmony_ci addr <<= 3; 11162306a36Sopenharmony_ci outb(addr | 0, GUSP(gus, MIXCNTRLPORT)); 11262306a36Sopenharmony_ci outb(1, GUSP(gus, MIXDATAPORT)); 11362306a36Sopenharmony_ci outb(addr | 2, GUSP(gus, MIXCNTRLPORT)); 11462306a36Sopenharmony_ci outb((unsigned char) val1, GUSP(gus, MIXDATAPORT)); 11562306a36Sopenharmony_ci outb(addr | 1, GUSP(gus, MIXCNTRLPORT)); 11662306a36Sopenharmony_ci outb(2, GUSP(gus, MIXDATAPORT)); 11762306a36Sopenharmony_ci outb(addr | 3, GUSP(gus, MIXCNTRLPORT)); 11862306a36Sopenharmony_ci outb((unsigned char) val2, GUSP(gus, MIXDATAPORT)); 11962306a36Sopenharmony_ci spin_unlock_irqrestore(&gus->reg_lock, flags); 12062306a36Sopenharmony_ci return change; 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_gf1_controls[] = { 12462306a36Sopenharmony_ciGF1_SINGLE("Master Playback Switch", 0, 1, 1), 12562306a36Sopenharmony_ciGF1_SINGLE("Line Switch", 0, 0, 1), 12662306a36Sopenharmony_ciGF1_SINGLE("Mic Switch", 0, 2, 0) 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic const struct snd_kcontrol_new snd_ics_controls[] = { 13062306a36Sopenharmony_ciGF1_SINGLE("Master Playback Switch", 0, 1, 1), 13162306a36Sopenharmony_ciICS_DOUBLE("Master Playback Volume", 0, SNDRV_ICS_MASTER_DEV), 13262306a36Sopenharmony_ciICS_DOUBLE("Synth Playback Volume", 0, SNDRV_ICS_GF1_DEV), 13362306a36Sopenharmony_ciGF1_SINGLE("Line Switch", 0, 0, 1), 13462306a36Sopenharmony_ciICS_DOUBLE("Line Playback Volume", 0, SNDRV_ICS_LINE_DEV), 13562306a36Sopenharmony_ciGF1_SINGLE("Mic Switch", 0, 2, 0), 13662306a36Sopenharmony_ciICS_DOUBLE("Mic Playback Volume", 0, SNDRV_ICS_MIC_DEV), 13762306a36Sopenharmony_ciICS_DOUBLE("CD Playback Volume", 0, SNDRV_ICS_CD_DEV) 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ciint snd_gf1_new_mixer(struct snd_gus_card * gus) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct snd_card *card; 14362306a36Sopenharmony_ci unsigned int idx, max; 14462306a36Sopenharmony_ci int err; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci if (snd_BUG_ON(!gus)) 14762306a36Sopenharmony_ci return -EINVAL; 14862306a36Sopenharmony_ci card = gus->card; 14962306a36Sopenharmony_ci if (snd_BUG_ON(!card)) 15062306a36Sopenharmony_ci return -EINVAL; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (gus->ics_flag) 15362306a36Sopenharmony_ci snd_component_add(card, "ICS2101"); 15462306a36Sopenharmony_ci if (card->mixername[0] == '\0') { 15562306a36Sopenharmony_ci strcpy(card->mixername, gus->ics_flag ? "GF1,ICS2101" : "GF1"); 15662306a36Sopenharmony_ci } else { 15762306a36Sopenharmony_ci if (gus->ics_flag) 15862306a36Sopenharmony_ci strcat(card->mixername, ",ICS2101"); 15962306a36Sopenharmony_ci strcat(card->mixername, ",GF1"); 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (!gus->ics_flag) { 16362306a36Sopenharmony_ci max = gus->ess_flag ? 1 : ARRAY_SIZE(snd_gf1_controls); 16462306a36Sopenharmony_ci for (idx = 0; idx < max; idx++) { 16562306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_gf1_controls[idx], gus)); 16662306a36Sopenharmony_ci if (err < 0) 16762306a36Sopenharmony_ci return err; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci } else { 17062306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(snd_ics_controls); idx++) { 17162306a36Sopenharmony_ci err = snd_ctl_add(card, snd_ctl_new1(&snd_ics_controls[idx], gus)); 17262306a36Sopenharmony_ci if (err < 0) 17362306a36Sopenharmony_ci return err; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 178