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