1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * PMac Burgundy lowlevel functions
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) by Takashi Iwai <tiwai@suse.de>
6*4882a593Smuzhiyun * code based on dmasound.c.
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/io.h>
10*4882a593Smuzhiyun #include <linux/init.h>
11*4882a593Smuzhiyun #include <linux/delay.h>
12*4882a593Smuzhiyun #include <sound/core.h>
13*4882a593Smuzhiyun #include "pmac.h"
14*4882a593Smuzhiyun #include "burgundy.h"
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun /* Waits for busy flag to clear */
18*4882a593Smuzhiyun static inline void
snd_pmac_burgundy_busy_wait(struct snd_pmac * chip)19*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(struct snd_pmac *chip)
20*4882a593Smuzhiyun {
21*4882a593Smuzhiyun int timeout = 50;
22*4882a593Smuzhiyun while ((in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) && timeout--)
23*4882a593Smuzhiyun udelay(1);
24*4882a593Smuzhiyun if (timeout < 0)
25*4882a593Smuzhiyun printk(KERN_DEBUG "burgundy_busy_wait: timeout\n");
26*4882a593Smuzhiyun }
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun static inline void
snd_pmac_burgundy_extend_wait(struct snd_pmac * chip)29*4882a593Smuzhiyun snd_pmac_burgundy_extend_wait(struct snd_pmac *chip)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun int timeout;
32*4882a593Smuzhiyun timeout = 50;
33*4882a593Smuzhiyun while (!(in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--)
34*4882a593Smuzhiyun udelay(1);
35*4882a593Smuzhiyun if (timeout < 0)
36*4882a593Smuzhiyun printk(KERN_DEBUG "burgundy_extend_wait: timeout #1\n");
37*4882a593Smuzhiyun timeout = 50;
38*4882a593Smuzhiyun while ((in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--)
39*4882a593Smuzhiyun udelay(1);
40*4882a593Smuzhiyun if (timeout < 0)
41*4882a593Smuzhiyun printk(KERN_DEBUG "burgundy_extend_wait: timeout #2\n");
42*4882a593Smuzhiyun }
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun static void
snd_pmac_burgundy_wcw(struct snd_pmac * chip,unsigned addr,unsigned val)45*4882a593Smuzhiyun snd_pmac_burgundy_wcw(struct snd_pmac *chip, unsigned addr, unsigned val)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun out_le32(&chip->awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff));
48*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(chip);
49*4882a593Smuzhiyun out_le32(&chip->awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff));
50*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(chip);
51*4882a593Smuzhiyun out_le32(&chip->awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff));
52*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(chip);
53*4882a593Smuzhiyun out_le32(&chip->awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff));
54*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(chip);
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun static unsigned
snd_pmac_burgundy_rcw(struct snd_pmac * chip,unsigned addr)58*4882a593Smuzhiyun snd_pmac_burgundy_rcw(struct snd_pmac *chip, unsigned addr)
59*4882a593Smuzhiyun {
60*4882a593Smuzhiyun unsigned val = 0;
61*4882a593Smuzhiyun unsigned long flags;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun spin_lock_irqsave(&chip->reg_lock, flags);
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun out_le32(&chip->awacs->codec_ctrl, addr + 0x100000);
66*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(chip);
67*4882a593Smuzhiyun snd_pmac_burgundy_extend_wait(chip);
68*4882a593Smuzhiyun val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun out_le32(&chip->awacs->codec_ctrl, addr + 0x100100);
71*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(chip);
72*4882a593Smuzhiyun snd_pmac_burgundy_extend_wait(chip);
73*4882a593Smuzhiyun val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<8;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun out_le32(&chip->awacs->codec_ctrl, addr + 0x100200);
76*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(chip);
77*4882a593Smuzhiyun snd_pmac_burgundy_extend_wait(chip);
78*4882a593Smuzhiyun val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<16;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun out_le32(&chip->awacs->codec_ctrl, addr + 0x100300);
81*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(chip);
82*4882a593Smuzhiyun snd_pmac_burgundy_extend_wait(chip);
83*4882a593Smuzhiyun val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<24;
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->reg_lock, flags);
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun return val;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun static void
snd_pmac_burgundy_wcb(struct snd_pmac * chip,unsigned int addr,unsigned int val)91*4882a593Smuzhiyun snd_pmac_burgundy_wcb(struct snd_pmac *chip, unsigned int addr,
92*4882a593Smuzhiyun unsigned int val)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun out_le32(&chip->awacs->codec_ctrl, addr + 0x300000 + (val & 0xff));
95*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(chip);
96*4882a593Smuzhiyun }
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun static unsigned
snd_pmac_burgundy_rcb(struct snd_pmac * chip,unsigned int addr)99*4882a593Smuzhiyun snd_pmac_burgundy_rcb(struct snd_pmac *chip, unsigned int addr)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun unsigned val = 0;
102*4882a593Smuzhiyun unsigned long flags;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun spin_lock_irqsave(&chip->reg_lock, flags);
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun out_le32(&chip->awacs->codec_ctrl, addr + 0x100000);
107*4882a593Smuzhiyun snd_pmac_burgundy_busy_wait(chip);
108*4882a593Smuzhiyun snd_pmac_burgundy_extend_wait(chip);
109*4882a593Smuzhiyun val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun spin_unlock_irqrestore(&chip->reg_lock, flags);
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun return val;
114*4882a593Smuzhiyun }
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun #define BASE2ADDR(base) ((base) << 12)
117*4882a593Smuzhiyun #define ADDR2BASE(addr) ((addr) >> 12)
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun /*
120*4882a593Smuzhiyun * Burgundy volume: 0 - 100, stereo, word reg
121*4882a593Smuzhiyun */
122*4882a593Smuzhiyun static void
snd_pmac_burgundy_write_volume(struct snd_pmac * chip,unsigned int address,long * volume,int shift)123*4882a593Smuzhiyun snd_pmac_burgundy_write_volume(struct snd_pmac *chip, unsigned int address,
124*4882a593Smuzhiyun long *volume, int shift)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun int hardvolume, lvolume, rvolume;
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun if (volume[0] < 0 || volume[0] > 100 ||
129*4882a593Smuzhiyun volume[1] < 0 || volume[1] > 100)
130*4882a593Smuzhiyun return; /* -EINVAL */
131*4882a593Smuzhiyun lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0;
132*4882a593Smuzhiyun rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0;
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun hardvolume = lvolume + (rvolume << shift);
135*4882a593Smuzhiyun if (shift == 8)
136*4882a593Smuzhiyun hardvolume |= hardvolume << 16;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun snd_pmac_burgundy_wcw(chip, address, hardvolume);
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun static void
snd_pmac_burgundy_read_volume(struct snd_pmac * chip,unsigned int address,long * volume,int shift)142*4882a593Smuzhiyun snd_pmac_burgundy_read_volume(struct snd_pmac *chip, unsigned int address,
143*4882a593Smuzhiyun long *volume, int shift)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun int wvolume;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun wvolume = snd_pmac_burgundy_rcw(chip, address);
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun volume[0] = wvolume & 0xff;
150*4882a593Smuzhiyun if (volume[0] >= BURGUNDY_VOLUME_OFFSET)
151*4882a593Smuzhiyun volume[0] -= BURGUNDY_VOLUME_OFFSET;
152*4882a593Smuzhiyun else
153*4882a593Smuzhiyun volume[0] = 0;
154*4882a593Smuzhiyun volume[1] = (wvolume >> shift) & 0xff;
155*4882a593Smuzhiyun if (volume[1] >= BURGUNDY_VOLUME_OFFSET)
156*4882a593Smuzhiyun volume[1] -= BURGUNDY_VOLUME_OFFSET;
157*4882a593Smuzhiyun else
158*4882a593Smuzhiyun volume[1] = 0;
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
snd_pmac_burgundy_info_volume(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)161*4882a593Smuzhiyun static int snd_pmac_burgundy_info_volume(struct snd_kcontrol *kcontrol,
162*4882a593Smuzhiyun struct snd_ctl_elem_info *uinfo)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
165*4882a593Smuzhiyun uinfo->count = 2;
166*4882a593Smuzhiyun uinfo->value.integer.min = 0;
167*4882a593Smuzhiyun uinfo->value.integer.max = 100;
168*4882a593Smuzhiyun return 0;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
snd_pmac_burgundy_get_volume(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)171*4882a593Smuzhiyun static int snd_pmac_burgundy_get_volume(struct snd_kcontrol *kcontrol,
172*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
175*4882a593Smuzhiyun unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
176*4882a593Smuzhiyun int shift = (kcontrol->private_value >> 8) & 0xff;
177*4882a593Smuzhiyun snd_pmac_burgundy_read_volume(chip, addr,
178*4882a593Smuzhiyun ucontrol->value.integer.value, shift);
179*4882a593Smuzhiyun return 0;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun
snd_pmac_burgundy_put_volume(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)182*4882a593Smuzhiyun static int snd_pmac_burgundy_put_volume(struct snd_kcontrol *kcontrol,
183*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
186*4882a593Smuzhiyun unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
187*4882a593Smuzhiyun int shift = (kcontrol->private_value >> 8) & 0xff;
188*4882a593Smuzhiyun long nvoices[2];
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun snd_pmac_burgundy_write_volume(chip, addr,
191*4882a593Smuzhiyun ucontrol->value.integer.value, shift);
192*4882a593Smuzhiyun snd_pmac_burgundy_read_volume(chip, addr, nvoices, shift);
193*4882a593Smuzhiyun return (nvoices[0] != ucontrol->value.integer.value[0] ||
194*4882a593Smuzhiyun nvoices[1] != ucontrol->value.integer.value[1]);
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun #define BURGUNDY_VOLUME_W(xname, xindex, addr, shift) \
198*4882a593Smuzhiyun { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
199*4882a593Smuzhiyun .info = snd_pmac_burgundy_info_volume,\
200*4882a593Smuzhiyun .get = snd_pmac_burgundy_get_volume,\
201*4882a593Smuzhiyun .put = snd_pmac_burgundy_put_volume,\
202*4882a593Smuzhiyun .private_value = ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) }
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun /*
205*4882a593Smuzhiyun * Burgundy volume: 0 - 100, stereo, 2-byte reg
206*4882a593Smuzhiyun */
207*4882a593Smuzhiyun static void
snd_pmac_burgundy_write_volume_2b(struct snd_pmac * chip,unsigned int address,long * volume,int off)208*4882a593Smuzhiyun snd_pmac_burgundy_write_volume_2b(struct snd_pmac *chip, unsigned int address,
209*4882a593Smuzhiyun long *volume, int off)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun int lvolume, rvolume;
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun off |= off << 2;
214*4882a593Smuzhiyun lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0;
215*4882a593Smuzhiyun rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0;
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, address + off, lvolume);
218*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, address + off + 0x500, rvolume);
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun static void
snd_pmac_burgundy_read_volume_2b(struct snd_pmac * chip,unsigned int address,long * volume,int off)222*4882a593Smuzhiyun snd_pmac_burgundy_read_volume_2b(struct snd_pmac *chip, unsigned int address,
223*4882a593Smuzhiyun long *volume, int off)
224*4882a593Smuzhiyun {
225*4882a593Smuzhiyun volume[0] = snd_pmac_burgundy_rcb(chip, address + off);
226*4882a593Smuzhiyun if (volume[0] >= BURGUNDY_VOLUME_OFFSET)
227*4882a593Smuzhiyun volume[0] -= BURGUNDY_VOLUME_OFFSET;
228*4882a593Smuzhiyun else
229*4882a593Smuzhiyun volume[0] = 0;
230*4882a593Smuzhiyun volume[1] = snd_pmac_burgundy_rcb(chip, address + off + 0x100);
231*4882a593Smuzhiyun if (volume[1] >= BURGUNDY_VOLUME_OFFSET)
232*4882a593Smuzhiyun volume[1] -= BURGUNDY_VOLUME_OFFSET;
233*4882a593Smuzhiyun else
234*4882a593Smuzhiyun volume[1] = 0;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun
snd_pmac_burgundy_info_volume_2b(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)237*4882a593Smuzhiyun static int snd_pmac_burgundy_info_volume_2b(struct snd_kcontrol *kcontrol,
238*4882a593Smuzhiyun struct snd_ctl_elem_info *uinfo)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
241*4882a593Smuzhiyun uinfo->count = 2;
242*4882a593Smuzhiyun uinfo->value.integer.min = 0;
243*4882a593Smuzhiyun uinfo->value.integer.max = 100;
244*4882a593Smuzhiyun return 0;
245*4882a593Smuzhiyun }
246*4882a593Smuzhiyun
snd_pmac_burgundy_get_volume_2b(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)247*4882a593Smuzhiyun static int snd_pmac_burgundy_get_volume_2b(struct snd_kcontrol *kcontrol,
248*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
251*4882a593Smuzhiyun unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
252*4882a593Smuzhiyun int off = kcontrol->private_value & 0x300;
253*4882a593Smuzhiyun snd_pmac_burgundy_read_volume_2b(chip, addr,
254*4882a593Smuzhiyun ucontrol->value.integer.value, off);
255*4882a593Smuzhiyun return 0;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun
snd_pmac_burgundy_put_volume_2b(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)258*4882a593Smuzhiyun static int snd_pmac_burgundy_put_volume_2b(struct snd_kcontrol *kcontrol,
259*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
262*4882a593Smuzhiyun unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
263*4882a593Smuzhiyun int off = kcontrol->private_value & 0x300;
264*4882a593Smuzhiyun long nvoices[2];
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun snd_pmac_burgundy_write_volume_2b(chip, addr,
267*4882a593Smuzhiyun ucontrol->value.integer.value, off);
268*4882a593Smuzhiyun snd_pmac_burgundy_read_volume_2b(chip, addr, nvoices, off);
269*4882a593Smuzhiyun return (nvoices[0] != ucontrol->value.integer.value[0] ||
270*4882a593Smuzhiyun nvoices[1] != ucontrol->value.integer.value[1]);
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun #define BURGUNDY_VOLUME_2B(xname, xindex, addr, off) \
274*4882a593Smuzhiyun { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
275*4882a593Smuzhiyun .info = snd_pmac_burgundy_info_volume_2b,\
276*4882a593Smuzhiyun .get = snd_pmac_burgundy_get_volume_2b,\
277*4882a593Smuzhiyun .put = snd_pmac_burgundy_put_volume_2b,\
278*4882a593Smuzhiyun .private_value = ((ADDR2BASE(addr) & 0xff) | ((off) << 8)) }
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun /*
281*4882a593Smuzhiyun * Burgundy gain/attenuation: 0 - 15, mono/stereo, byte reg
282*4882a593Smuzhiyun */
snd_pmac_burgundy_info_gain(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)283*4882a593Smuzhiyun static int snd_pmac_burgundy_info_gain(struct snd_kcontrol *kcontrol,
284*4882a593Smuzhiyun struct snd_ctl_elem_info *uinfo)
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun int stereo = (kcontrol->private_value >> 24) & 1;
287*4882a593Smuzhiyun uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
288*4882a593Smuzhiyun uinfo->count = stereo + 1;
289*4882a593Smuzhiyun uinfo->value.integer.min = 0;
290*4882a593Smuzhiyun uinfo->value.integer.max = 15;
291*4882a593Smuzhiyun return 0;
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun
snd_pmac_burgundy_get_gain(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)294*4882a593Smuzhiyun static int snd_pmac_burgundy_get_gain(struct snd_kcontrol *kcontrol,
295*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
298*4882a593Smuzhiyun unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
299*4882a593Smuzhiyun int stereo = (kcontrol->private_value >> 24) & 1;
300*4882a593Smuzhiyun int atten = (kcontrol->private_value >> 25) & 1;
301*4882a593Smuzhiyun int oval;
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun oval = snd_pmac_burgundy_rcb(chip, addr);
304*4882a593Smuzhiyun if (atten)
305*4882a593Smuzhiyun oval = ~oval & 0xff;
306*4882a593Smuzhiyun ucontrol->value.integer.value[0] = oval & 0xf;
307*4882a593Smuzhiyun if (stereo)
308*4882a593Smuzhiyun ucontrol->value.integer.value[1] = (oval >> 4) & 0xf;
309*4882a593Smuzhiyun return 0;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun
snd_pmac_burgundy_put_gain(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)312*4882a593Smuzhiyun static int snd_pmac_burgundy_put_gain(struct snd_kcontrol *kcontrol,
313*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
316*4882a593Smuzhiyun unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff);
317*4882a593Smuzhiyun int stereo = (kcontrol->private_value >> 24) & 1;
318*4882a593Smuzhiyun int atten = (kcontrol->private_value >> 25) & 1;
319*4882a593Smuzhiyun int oval, val;
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun oval = snd_pmac_burgundy_rcb(chip, addr);
322*4882a593Smuzhiyun if (atten)
323*4882a593Smuzhiyun oval = ~oval & 0xff;
324*4882a593Smuzhiyun val = ucontrol->value.integer.value[0];
325*4882a593Smuzhiyun if (stereo)
326*4882a593Smuzhiyun val |= ucontrol->value.integer.value[1] << 4;
327*4882a593Smuzhiyun else
328*4882a593Smuzhiyun val |= ucontrol->value.integer.value[0] << 4;
329*4882a593Smuzhiyun if (atten)
330*4882a593Smuzhiyun val = ~val & 0xff;
331*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, addr, val);
332*4882a593Smuzhiyun return val != oval;
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun #define BURGUNDY_VOLUME_B(xname, xindex, addr, stereo, atten) \
336*4882a593Smuzhiyun { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
337*4882a593Smuzhiyun .info = snd_pmac_burgundy_info_gain,\
338*4882a593Smuzhiyun .get = snd_pmac_burgundy_get_gain,\
339*4882a593Smuzhiyun .put = snd_pmac_burgundy_put_gain,\
340*4882a593Smuzhiyun .private_value = (ADDR2BASE(addr) | ((stereo) << 24) | ((atten) << 25)) }
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun /*
343*4882a593Smuzhiyun * Burgundy switch: 0/1, mono/stereo, word reg
344*4882a593Smuzhiyun */
snd_pmac_burgundy_info_switch_w(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)345*4882a593Smuzhiyun static int snd_pmac_burgundy_info_switch_w(struct snd_kcontrol *kcontrol,
346*4882a593Smuzhiyun struct snd_ctl_elem_info *uinfo)
347*4882a593Smuzhiyun {
348*4882a593Smuzhiyun int stereo = (kcontrol->private_value >> 24) & 1;
349*4882a593Smuzhiyun uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
350*4882a593Smuzhiyun uinfo->count = stereo + 1;
351*4882a593Smuzhiyun uinfo->value.integer.min = 0;
352*4882a593Smuzhiyun uinfo->value.integer.max = 1;
353*4882a593Smuzhiyun return 0;
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun
snd_pmac_burgundy_get_switch_w(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)356*4882a593Smuzhiyun static int snd_pmac_burgundy_get_switch_w(struct snd_kcontrol *kcontrol,
357*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
360*4882a593Smuzhiyun unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff);
361*4882a593Smuzhiyun int lmask = 1 << (kcontrol->private_value & 0xff);
362*4882a593Smuzhiyun int rmask = 1 << ((kcontrol->private_value >> 8) & 0xff);
363*4882a593Smuzhiyun int stereo = (kcontrol->private_value >> 24) & 1;
364*4882a593Smuzhiyun int val = snd_pmac_burgundy_rcw(chip, addr);
365*4882a593Smuzhiyun ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0;
366*4882a593Smuzhiyun if (stereo)
367*4882a593Smuzhiyun ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0;
368*4882a593Smuzhiyun return 0;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun
snd_pmac_burgundy_put_switch_w(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)371*4882a593Smuzhiyun static int snd_pmac_burgundy_put_switch_w(struct snd_kcontrol *kcontrol,
372*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
375*4882a593Smuzhiyun unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff);
376*4882a593Smuzhiyun int lmask = 1 << (kcontrol->private_value & 0xff);
377*4882a593Smuzhiyun int rmask = 1 << ((kcontrol->private_value >> 8) & 0xff);
378*4882a593Smuzhiyun int stereo = (kcontrol->private_value >> 24) & 1;
379*4882a593Smuzhiyun int val, oval;
380*4882a593Smuzhiyun oval = snd_pmac_burgundy_rcw(chip, addr);
381*4882a593Smuzhiyun val = oval & ~(lmask | (stereo ? rmask : 0));
382*4882a593Smuzhiyun if (ucontrol->value.integer.value[0])
383*4882a593Smuzhiyun val |= lmask;
384*4882a593Smuzhiyun if (stereo && ucontrol->value.integer.value[1])
385*4882a593Smuzhiyun val |= rmask;
386*4882a593Smuzhiyun snd_pmac_burgundy_wcw(chip, addr, val);
387*4882a593Smuzhiyun return val != oval;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun #define BURGUNDY_SWITCH_W(xname, xindex, addr, lbit, rbit, stereo) \
391*4882a593Smuzhiyun { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
392*4882a593Smuzhiyun .info = snd_pmac_burgundy_info_switch_w,\
393*4882a593Smuzhiyun .get = snd_pmac_burgundy_get_switch_w,\
394*4882a593Smuzhiyun .put = snd_pmac_burgundy_put_switch_w,\
395*4882a593Smuzhiyun .private_value = ((lbit) | ((rbit) << 8)\
396*4882a593Smuzhiyun | (ADDR2BASE(addr) << 16) | ((stereo) << 24)) }
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun /*
399*4882a593Smuzhiyun * Burgundy switch: 0/1, mono/stereo, byte reg, bit mask
400*4882a593Smuzhiyun */
snd_pmac_burgundy_info_switch_b(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)401*4882a593Smuzhiyun static int snd_pmac_burgundy_info_switch_b(struct snd_kcontrol *kcontrol,
402*4882a593Smuzhiyun struct snd_ctl_elem_info *uinfo)
403*4882a593Smuzhiyun {
404*4882a593Smuzhiyun int stereo = (kcontrol->private_value >> 24) & 1;
405*4882a593Smuzhiyun uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
406*4882a593Smuzhiyun uinfo->count = stereo + 1;
407*4882a593Smuzhiyun uinfo->value.integer.min = 0;
408*4882a593Smuzhiyun uinfo->value.integer.max = 1;
409*4882a593Smuzhiyun return 0;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun
snd_pmac_burgundy_get_switch_b(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)412*4882a593Smuzhiyun static int snd_pmac_burgundy_get_switch_b(struct snd_kcontrol *kcontrol,
413*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
414*4882a593Smuzhiyun {
415*4882a593Smuzhiyun struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
416*4882a593Smuzhiyun unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff);
417*4882a593Smuzhiyun int lmask = kcontrol->private_value & 0xff;
418*4882a593Smuzhiyun int rmask = (kcontrol->private_value >> 8) & 0xff;
419*4882a593Smuzhiyun int stereo = (kcontrol->private_value >> 24) & 1;
420*4882a593Smuzhiyun int val = snd_pmac_burgundy_rcb(chip, addr);
421*4882a593Smuzhiyun ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0;
422*4882a593Smuzhiyun if (stereo)
423*4882a593Smuzhiyun ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0;
424*4882a593Smuzhiyun return 0;
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun
snd_pmac_burgundy_put_switch_b(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)427*4882a593Smuzhiyun static int snd_pmac_burgundy_put_switch_b(struct snd_kcontrol *kcontrol,
428*4882a593Smuzhiyun struct snd_ctl_elem_value *ucontrol)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
431*4882a593Smuzhiyun unsigned int addr = BASE2ADDR((kcontrol->private_value >> 16) & 0xff);
432*4882a593Smuzhiyun int lmask = kcontrol->private_value & 0xff;
433*4882a593Smuzhiyun int rmask = (kcontrol->private_value >> 8) & 0xff;
434*4882a593Smuzhiyun int stereo = (kcontrol->private_value >> 24) & 1;
435*4882a593Smuzhiyun int val, oval;
436*4882a593Smuzhiyun oval = snd_pmac_burgundy_rcb(chip, addr);
437*4882a593Smuzhiyun val = oval & ~(lmask | rmask);
438*4882a593Smuzhiyun if (ucontrol->value.integer.value[0])
439*4882a593Smuzhiyun val |= lmask;
440*4882a593Smuzhiyun if (stereo && ucontrol->value.integer.value[1])
441*4882a593Smuzhiyun val |= rmask;
442*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, addr, val);
443*4882a593Smuzhiyun return val != oval;
444*4882a593Smuzhiyun }
445*4882a593Smuzhiyun
446*4882a593Smuzhiyun #define BURGUNDY_SWITCH_B(xname, xindex, addr, lmask, rmask, stereo) \
447*4882a593Smuzhiyun { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
448*4882a593Smuzhiyun .info = snd_pmac_burgundy_info_switch_b,\
449*4882a593Smuzhiyun .get = snd_pmac_burgundy_get_switch_b,\
450*4882a593Smuzhiyun .put = snd_pmac_burgundy_put_switch_b,\
451*4882a593Smuzhiyun .private_value = ((lmask) | ((rmask) << 8)\
452*4882a593Smuzhiyun | (ADDR2BASE(addr) << 16) | ((stereo) << 24)) }
453*4882a593Smuzhiyun
454*4882a593Smuzhiyun /*
455*4882a593Smuzhiyun * Burgundy mixers
456*4882a593Smuzhiyun */
457*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_pmac_burgundy_mixers[] = {
458*4882a593Smuzhiyun BURGUNDY_VOLUME_W("Master Playback Volume", 0,
459*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8),
460*4882a593Smuzhiyun BURGUNDY_VOLUME_W("CD Capture Volume", 0,
461*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_VOLCD, 16),
462*4882a593Smuzhiyun BURGUNDY_VOLUME_2B("Input Capture Volume", 0,
463*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_VOLMIX01, 2),
464*4882a593Smuzhiyun BURGUNDY_VOLUME_2B("Mixer Playback Volume", 0,
465*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_VOLMIX23, 0),
466*4882a593Smuzhiyun BURGUNDY_VOLUME_B("CD Gain Capture Volume", 0,
467*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_GAINCD, 1, 0),
468*4882a593Smuzhiyun BURGUNDY_SWITCH_W("Master Capture Switch", 0,
469*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_OUTPUTENABLES, 24, 0, 0),
470*4882a593Smuzhiyun BURGUNDY_SWITCH_W("CD Capture Switch", 0,
471*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_CAPTURESELECTS, 0, 16, 1),
472*4882a593Smuzhiyun BURGUNDY_SWITCH_W("CD Playback Switch", 0,
473*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 0, 16, 1),
474*4882a593Smuzhiyun /* BURGUNDY_SWITCH_W("Loop Capture Switch", 0,
475*4882a593Smuzhiyun * MASK_ADDR_BURGUNDY_CAPTURESELECTS, 8, 24, 1),
476*4882a593Smuzhiyun * BURGUNDY_SWITCH_B("Mixer out Capture Switch", 0,
477*4882a593Smuzhiyun * MASK_ADDR_BURGUNDY_HOSTIFAD, 0x02, 0, 0),
478*4882a593Smuzhiyun * BURGUNDY_SWITCH_B("Mixer Capture Switch", 0,
479*4882a593Smuzhiyun * MASK_ADDR_BURGUNDY_HOSTIFAD, 0x01, 0, 0),
480*4882a593Smuzhiyun * BURGUNDY_SWITCH_B("PCM out Capture Switch", 0,
481*4882a593Smuzhiyun * MASK_ADDR_BURGUNDY_HOSTIFEH, 0x02, 0, 0),
482*4882a593Smuzhiyun */ BURGUNDY_SWITCH_B("PCM Capture Switch", 0,
483*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_HOSTIFEH, 0x01, 0, 0)
484*4882a593Smuzhiyun };
485*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] = {
486*4882a593Smuzhiyun BURGUNDY_VOLUME_W("Line in Capture Volume", 0,
487*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_VOLLINE, 16),
488*4882a593Smuzhiyun BURGUNDY_VOLUME_W("Mic Capture Volume", 0,
489*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_VOLMIC, 16),
490*4882a593Smuzhiyun BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0,
491*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_GAINLINE, 1, 0),
492*4882a593Smuzhiyun BURGUNDY_VOLUME_B("Mic Gain Capture Volume", 0,
493*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_GAINMIC, 1, 0),
494*4882a593Smuzhiyun BURGUNDY_VOLUME_B("Speaker Playback Volume", 0,
495*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1),
496*4882a593Smuzhiyun BURGUNDY_VOLUME_B("Line out Playback Volume", 0,
497*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1, 1),
498*4882a593Smuzhiyun BURGUNDY_VOLUME_B("Headphone Playback Volume", 0,
499*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_ATTENHP, 1, 1),
500*4882a593Smuzhiyun BURGUNDY_SWITCH_W("Line in Capture Switch", 0,
501*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_CAPTURESELECTS, 1, 17, 1),
502*4882a593Smuzhiyun BURGUNDY_SWITCH_W("Mic Capture Switch", 0,
503*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_CAPTURESELECTS, 2, 18, 1),
504*4882a593Smuzhiyun BURGUNDY_SWITCH_W("Line in Playback Switch", 0,
505*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 1, 17, 1),
506*4882a593Smuzhiyun BURGUNDY_SWITCH_W("Mic Playback Switch", 0,
507*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 2, 18, 1),
508*4882a593Smuzhiyun BURGUNDY_SWITCH_B("Mic Boost Capture Switch", 0,
509*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1)
510*4882a593Smuzhiyun };
511*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] = {
512*4882a593Smuzhiyun BURGUNDY_VOLUME_W("Line in Capture Volume", 0,
513*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_VOLMIC, 16),
514*4882a593Smuzhiyun BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0,
515*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_GAINMIC, 1, 0),
516*4882a593Smuzhiyun BURGUNDY_VOLUME_B("Speaker Playback Volume", 0,
517*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_ATTENMONO, 0, 1),
518*4882a593Smuzhiyun BURGUNDY_VOLUME_B("Line out Playback Volume", 0,
519*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1, 1),
520*4882a593Smuzhiyun BURGUNDY_SWITCH_W("Line in Capture Switch", 0,
521*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_CAPTURESELECTS, 2, 18, 1),
522*4882a593Smuzhiyun BURGUNDY_SWITCH_W("Line in Playback Switch", 0,
523*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_OUTPUTSELECTS, 2, 18, 1),
524*4882a593Smuzhiyun /* BURGUNDY_SWITCH_B("Line in Boost Capture Switch", 0,
525*4882a593Smuzhiyun * MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) */
526*4882a593Smuzhiyun };
527*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac =
528*4882a593Smuzhiyun BURGUNDY_SWITCH_B("Master Playback Switch", 0,
529*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
530*4882a593Smuzhiyun BURGUNDY_OUTPUT_LEFT | BURGUNDY_LINEOUT_LEFT | BURGUNDY_HP_LEFT,
531*4882a593Smuzhiyun BURGUNDY_OUTPUT_RIGHT | BURGUNDY_LINEOUT_RIGHT | BURGUNDY_HP_RIGHT, 1);
532*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac =
533*4882a593Smuzhiyun BURGUNDY_SWITCH_B("Master Playback Switch", 0,
534*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
535*4882a593Smuzhiyun BURGUNDY_OUTPUT_INTERN
536*4882a593Smuzhiyun | BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
537*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac =
538*4882a593Smuzhiyun BURGUNDY_SWITCH_B("Speaker Playback Switch", 0,
539*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
540*4882a593Smuzhiyun BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
541*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac =
542*4882a593Smuzhiyun BURGUNDY_SWITCH_B("Speaker Playback Switch", 0,
543*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
544*4882a593Smuzhiyun BURGUNDY_OUTPUT_INTERN, 0, 0);
545*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac =
546*4882a593Smuzhiyun BURGUNDY_SWITCH_B("Line out Playback Switch", 0,
547*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
548*4882a593Smuzhiyun BURGUNDY_LINEOUT_LEFT, BURGUNDY_LINEOUT_RIGHT, 1);
549*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac =
550*4882a593Smuzhiyun BURGUNDY_SWITCH_B("Line out Playback Switch", 0,
551*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
552*4882a593Smuzhiyun BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
553*4882a593Smuzhiyun static const struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac =
554*4882a593Smuzhiyun BURGUNDY_SWITCH_B("Headphone Playback Switch", 0,
555*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
556*4882a593Smuzhiyun BURGUNDY_HP_LEFT, BURGUNDY_HP_RIGHT, 1);
557*4882a593Smuzhiyun
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun #ifdef PMAC_SUPPORT_AUTOMUTE
560*4882a593Smuzhiyun /*
561*4882a593Smuzhiyun * auto-mute stuffs
562*4882a593Smuzhiyun */
snd_pmac_burgundy_detect_headphone(struct snd_pmac * chip)563*4882a593Smuzhiyun static int snd_pmac_burgundy_detect_headphone(struct snd_pmac *chip)
564*4882a593Smuzhiyun {
565*4882a593Smuzhiyun return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0;
566*4882a593Smuzhiyun }
567*4882a593Smuzhiyun
snd_pmac_burgundy_update_automute(struct snd_pmac * chip,int do_notify)568*4882a593Smuzhiyun static void snd_pmac_burgundy_update_automute(struct snd_pmac *chip, int do_notify)
569*4882a593Smuzhiyun {
570*4882a593Smuzhiyun if (chip->auto_mute) {
571*4882a593Smuzhiyun int imac = of_machine_is_compatible("iMac");
572*4882a593Smuzhiyun int reg, oreg;
573*4882a593Smuzhiyun reg = oreg = snd_pmac_burgundy_rcb(chip,
574*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES);
575*4882a593Smuzhiyun reg &= imac ? ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT
576*4882a593Smuzhiyun | BURGUNDY_HP_LEFT | BURGUNDY_HP_RIGHT)
577*4882a593Smuzhiyun : ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT
578*4882a593Smuzhiyun | BURGUNDY_OUTPUT_INTERN);
579*4882a593Smuzhiyun if (snd_pmac_burgundy_detect_headphone(chip))
580*4882a593Smuzhiyun reg |= imac ? (BURGUNDY_HP_LEFT | BURGUNDY_HP_RIGHT)
581*4882a593Smuzhiyun : (BURGUNDY_OUTPUT_LEFT
582*4882a593Smuzhiyun | BURGUNDY_OUTPUT_RIGHT);
583*4882a593Smuzhiyun else
584*4882a593Smuzhiyun reg |= imac ? (BURGUNDY_OUTPUT_LEFT
585*4882a593Smuzhiyun | BURGUNDY_OUTPUT_RIGHT)
586*4882a593Smuzhiyun : (BURGUNDY_OUTPUT_INTERN);
587*4882a593Smuzhiyun if (do_notify && reg == oreg)
588*4882a593Smuzhiyun return;
589*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip,
590*4882a593Smuzhiyun MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, reg);
591*4882a593Smuzhiyun if (do_notify) {
592*4882a593Smuzhiyun snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
593*4882a593Smuzhiyun &chip->master_sw_ctl->id);
594*4882a593Smuzhiyun snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
595*4882a593Smuzhiyun &chip->speaker_sw_ctl->id);
596*4882a593Smuzhiyun snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
597*4882a593Smuzhiyun &chip->hp_detect_ctl->id);
598*4882a593Smuzhiyun }
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun }
601*4882a593Smuzhiyun #endif /* PMAC_SUPPORT_AUTOMUTE */
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun /*
605*4882a593Smuzhiyun * initialize burgundy
606*4882a593Smuzhiyun */
snd_pmac_burgundy_init(struct snd_pmac * chip)607*4882a593Smuzhiyun int snd_pmac_burgundy_init(struct snd_pmac *chip)
608*4882a593Smuzhiyun {
609*4882a593Smuzhiyun int imac = of_machine_is_compatible("iMac");
610*4882a593Smuzhiyun int i, err;
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun /* Checks to see the chip is alive and kicking */
613*4882a593Smuzhiyun if ((in_le32(&chip->awacs->codec_ctrl) & MASK_ERRCODE) == 0xf0000) {
614*4882a593Smuzhiyun printk(KERN_WARNING "pmac burgundy: disabled by MacOS :-(\n");
615*4882a593Smuzhiyun return 1;
616*4882a593Smuzhiyun }
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES,
619*4882a593Smuzhiyun DEF_BURGUNDY_OUTPUTENABLES);
620*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
621*4882a593Smuzhiyun DEF_BURGUNDY_MORE_OUTPUTENABLES);
622*4882a593Smuzhiyun snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTSELECTS,
623*4882a593Smuzhiyun DEF_BURGUNDY_OUTPUTSELECTS);
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL21,
626*4882a593Smuzhiyun DEF_BURGUNDY_INPSEL21);
627*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL3,
628*4882a593Smuzhiyun imac ? DEF_BURGUNDY_INPSEL3_IMAC
629*4882a593Smuzhiyun : DEF_BURGUNDY_INPSEL3_PMAC);
630*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINCD,
631*4882a593Smuzhiyun DEF_BURGUNDY_GAINCD);
632*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINLINE,
633*4882a593Smuzhiyun DEF_BURGUNDY_GAINLINE);
634*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMIC,
635*4882a593Smuzhiyun DEF_BURGUNDY_GAINMIC);
636*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMODEM,
637*4882a593Smuzhiyun DEF_BURGUNDY_GAINMODEM);
638*4882a593Smuzhiyun
639*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENSPEAKER,
640*4882a593Smuzhiyun DEF_BURGUNDY_ATTENSPEAKER);
641*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENLINEOUT,
642*4882a593Smuzhiyun DEF_BURGUNDY_ATTENLINEOUT);
643*4882a593Smuzhiyun snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENHP,
644*4882a593Smuzhiyun DEF_BURGUNDY_ATTENHP);
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_MASTER_VOLUME,
647*4882a593Smuzhiyun DEF_BURGUNDY_MASTER_VOLUME);
648*4882a593Smuzhiyun snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLCD,
649*4882a593Smuzhiyun DEF_BURGUNDY_VOLCD);
650*4882a593Smuzhiyun snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLLINE,
651*4882a593Smuzhiyun DEF_BURGUNDY_VOLLINE);
652*4882a593Smuzhiyun snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLMIC,
653*4882a593Smuzhiyun DEF_BURGUNDY_VOLMIC);
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun if (chip->hp_stat_mask == 0) {
656*4882a593Smuzhiyun /* set headphone-jack detection bit */
657*4882a593Smuzhiyun if (imac)
658*4882a593Smuzhiyun chip->hp_stat_mask = BURGUNDY_HPDETECT_IMAC_UPPER
659*4882a593Smuzhiyun | BURGUNDY_HPDETECT_IMAC_LOWER
660*4882a593Smuzhiyun | BURGUNDY_HPDETECT_IMAC_SIDE;
661*4882a593Smuzhiyun else
662*4882a593Smuzhiyun chip->hp_stat_mask = BURGUNDY_HPDETECT_PMAC_BACK;
663*4882a593Smuzhiyun }
664*4882a593Smuzhiyun /*
665*4882a593Smuzhiyun * build burgundy mixers
666*4882a593Smuzhiyun */
667*4882a593Smuzhiyun strcpy(chip->card->mixername, "PowerMac Burgundy");
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(snd_pmac_burgundy_mixers); i++) {
670*4882a593Smuzhiyun err = snd_ctl_add(chip->card,
671*4882a593Smuzhiyun snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip));
672*4882a593Smuzhiyun if (err < 0)
673*4882a593Smuzhiyun return err;
674*4882a593Smuzhiyun }
675*4882a593Smuzhiyun for (i = 0; i < (imac ? ARRAY_SIZE(snd_pmac_burgundy_mixers_imac)
676*4882a593Smuzhiyun : ARRAY_SIZE(snd_pmac_burgundy_mixers_pmac)); i++) {
677*4882a593Smuzhiyun err = snd_ctl_add(chip->card,
678*4882a593Smuzhiyun snd_ctl_new1(imac ? &snd_pmac_burgundy_mixers_imac[i]
679*4882a593Smuzhiyun : &snd_pmac_burgundy_mixers_pmac[i], chip));
680*4882a593Smuzhiyun if (err < 0)
681*4882a593Smuzhiyun return err;
682*4882a593Smuzhiyun }
683*4882a593Smuzhiyun chip->master_sw_ctl = snd_ctl_new1(imac
684*4882a593Smuzhiyun ? &snd_pmac_burgundy_master_sw_imac
685*4882a593Smuzhiyun : &snd_pmac_burgundy_master_sw_pmac, chip);
686*4882a593Smuzhiyun err = snd_ctl_add(chip->card, chip->master_sw_ctl);
687*4882a593Smuzhiyun if (err < 0)
688*4882a593Smuzhiyun return err;
689*4882a593Smuzhiyun chip->master_sw_ctl = snd_ctl_new1(imac
690*4882a593Smuzhiyun ? &snd_pmac_burgundy_line_sw_imac
691*4882a593Smuzhiyun : &snd_pmac_burgundy_line_sw_pmac, chip);
692*4882a593Smuzhiyun err = snd_ctl_add(chip->card, chip->master_sw_ctl);
693*4882a593Smuzhiyun if (err < 0)
694*4882a593Smuzhiyun return err;
695*4882a593Smuzhiyun if (imac) {
696*4882a593Smuzhiyun chip->master_sw_ctl = snd_ctl_new1(
697*4882a593Smuzhiyun &snd_pmac_burgundy_hp_sw_imac, chip);
698*4882a593Smuzhiyun err = snd_ctl_add(chip->card, chip->master_sw_ctl);
699*4882a593Smuzhiyun if (err < 0)
700*4882a593Smuzhiyun return err;
701*4882a593Smuzhiyun }
702*4882a593Smuzhiyun chip->speaker_sw_ctl = snd_ctl_new1(imac
703*4882a593Smuzhiyun ? &snd_pmac_burgundy_speaker_sw_imac
704*4882a593Smuzhiyun : &snd_pmac_burgundy_speaker_sw_pmac, chip);
705*4882a593Smuzhiyun err = snd_ctl_add(chip->card, chip->speaker_sw_ctl);
706*4882a593Smuzhiyun if (err < 0)
707*4882a593Smuzhiyun return err;
708*4882a593Smuzhiyun #ifdef PMAC_SUPPORT_AUTOMUTE
709*4882a593Smuzhiyun err = snd_pmac_add_automute(chip);
710*4882a593Smuzhiyun if (err < 0)
711*4882a593Smuzhiyun return err;
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun chip->detect_headphone = snd_pmac_burgundy_detect_headphone;
714*4882a593Smuzhiyun chip->update_automute = snd_pmac_burgundy_update_automute;
715*4882a593Smuzhiyun snd_pmac_burgundy_update_automute(chip, 0); /* update the status only */
716*4882a593Smuzhiyun #endif
717*4882a593Smuzhiyun
718*4882a593Smuzhiyun return 0;
719*4882a593Smuzhiyun }
720