blob: b15bfb6f7011f0c5364fbedd609807725889f34f [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * PMac AWACS lowlevel functions
3 *
4 * Copyright (c) by Takashi Iwai <tiwai@suse.de>
5 * code based on dmasound.c.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22
23#include <sound/driver.h>
24#include <asm/io.h>
25#include <asm/nvram.h>
26#include <linux/init.h>
27#include <linux/delay.h>
28#include <linux/slab.h>
29#include <sound/core.h>
30#include "pmac.h"
31
32
33#ifdef CONFIG_ADB_CUDA
34#define PMAC_AMP_AVAIL
35#endif
36
37#ifdef PMAC_AMP_AVAIL
Takashi Iwai65b29f52005-11-17 15:09:46 +010038struct awacs_amp {
Linus Torvalds1da177e2005-04-16 15:20:36 -070039 unsigned char amp_master;
40 unsigned char amp_vol[2][2];
41 unsigned char amp_tone[2];
Takashi Iwai65b29f52005-11-17 15:09:46 +010042};
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44#define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA)
45
46#endif /* PMAC_AMP_AVAIL */
47
48
Takashi Iwai65b29f52005-11-17 15:09:46 +010049static void snd_pmac_screamer_wait(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -070050{
51 long timeout = 2000;
52 while (!(in_le32(&chip->awacs->codec_stat) & MASK_VALID)) {
53 mdelay(1);
54 if (! --timeout) {
55 snd_printd("snd_pmac_screamer_wait timeout\n");
56 break;
57 }
58 }
59}
60
61/*
62 * write AWACS register
63 */
64static void
Takashi Iwai65b29f52005-11-17 15:09:46 +010065snd_pmac_awacs_write(struct snd_pmac *chip, int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -070066{
67 long timeout = 5000000;
68
69 if (chip->model == PMAC_SCREAMER)
70 snd_pmac_screamer_wait(chip);
71 out_le32(&chip->awacs->codec_ctrl, val | (chip->subframe << 22));
72 while (in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) {
73 if (! --timeout) {
74 snd_printd("snd_pmac_awacs_write timeout\n");
75 break;
76 }
77 }
78}
79
80static void
Takashi Iwai65b29f52005-11-17 15:09:46 +010081snd_pmac_awacs_write_reg(struct snd_pmac *chip, int reg, int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082{
83 snd_pmac_awacs_write(chip, val | (reg << 12));
84 chip->awacs_reg[reg] = val;
85}
86
87static void
Takashi Iwai65b29f52005-11-17 15:09:46 +010088snd_pmac_awacs_write_noreg(struct snd_pmac *chip, int reg, int val)
Linus Torvalds1da177e2005-04-16 15:20:36 -070089{
90 snd_pmac_awacs_write(chip, val | (reg << 12));
91}
92
Benjamin Herrenschmidt8c870932005-06-27 14:36:34 -070093#ifdef CONFIG_PM
Linus Torvalds1da177e2005-04-16 15:20:36 -070094/* Recalibrate chip */
Takashi Iwai65b29f52005-11-17 15:09:46 +010095static void screamer_recalibrate(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -070096{
97 if (chip->model != PMAC_SCREAMER)
98 return;
99
100 /* Sorry for the horrible delays... I hope to get that improved
101 * by making the whole PM process asynchronous in a future version
102 */
103 snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
104 if (chip->manufacturer == 0x1)
105 /* delay for broken crystal part */
Nishanth Aravamudan989a0b22005-07-09 10:53:24 +0200106 msleep(750);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 snd_pmac_awacs_write_noreg(chip, 1,
Takashi Iwai65b29f52005-11-17 15:09:46 +0100108 chip->awacs_reg[1] | MASK_RECALIBRATE |
109 MASK_CMUTE | MASK_AMUTE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
111 snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
112}
113
114#else
115#define screamer_recalibrate(chip) /* NOP */
116#endif
117
118
119/*
120 * additional callback to set the pcm format
121 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100122static void snd_pmac_awacs_set_format(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123{
124 chip->awacs_reg[1] &= ~MASK_SAMPLERATE;
125 chip->awacs_reg[1] |= chip->rate_index << 3;
126 snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]);
127}
128
129
130/*
131 * AWACS volume callbacks
132 */
133/*
134 * volumes: 0-15 stereo
135 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100136static int snd_pmac_awacs_info_volume(struct snd_kcontrol *kcontrol,
137 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138{
139 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
140 uinfo->count = 2;
141 uinfo->value.integer.min = 0;
142 uinfo->value.integer.max = 15;
143 return 0;
144}
145
Takashi Iwai65b29f52005-11-17 15:09:46 +0100146static int snd_pmac_awacs_get_volume(struct snd_kcontrol *kcontrol,
147 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100149 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 int reg = kcontrol->private_value & 0xff;
151 int lshift = (kcontrol->private_value >> 8) & 0xff;
152 int inverted = (kcontrol->private_value >> 16) & 1;
153 unsigned long flags;
154 int vol[2];
155
156 spin_lock_irqsave(&chip->reg_lock, flags);
157 vol[0] = (chip->awacs_reg[reg] >> lshift) & 0xf;
158 vol[1] = chip->awacs_reg[reg] & 0xf;
159 spin_unlock_irqrestore(&chip->reg_lock, flags);
160 if (inverted) {
161 vol[0] = 0x0f - vol[0];
162 vol[1] = 0x0f - vol[1];
163 }
164 ucontrol->value.integer.value[0] = vol[0];
165 ucontrol->value.integer.value[1] = vol[1];
166 return 0;
167}
168
Takashi Iwai65b29f52005-11-17 15:09:46 +0100169static int snd_pmac_awacs_put_volume(struct snd_kcontrol *kcontrol,
170 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100172 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 int reg = kcontrol->private_value & 0xff;
174 int lshift = (kcontrol->private_value >> 8) & 0xff;
175 int inverted = (kcontrol->private_value >> 16) & 1;
176 int val, oldval;
177 unsigned long flags;
Takashi Iwaid4079ac2007-11-15 16:14:12 +0100178 unsigned int vol[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
180 vol[0] = ucontrol->value.integer.value[0];
181 vol[1] = ucontrol->value.integer.value[1];
Takashi Iwaid4079ac2007-11-15 16:14:12 +0100182 if (vol[0] > 0x0f || vol[1] > 0x0f)
183 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 if (inverted) {
185 vol[0] = 0x0f - vol[0];
186 vol[1] = 0x0f - vol[1];
187 }
188 vol[0] &= 0x0f;
189 vol[1] &= 0x0f;
190 spin_lock_irqsave(&chip->reg_lock, flags);
191 oldval = chip->awacs_reg[reg];
192 val = oldval & ~(0xf | (0xf << lshift));
193 val |= vol[0] << lshift;
194 val |= vol[1];
195 if (oldval != val)
196 snd_pmac_awacs_write_reg(chip, reg, val);
197 spin_unlock_irqrestore(&chip->reg_lock, flags);
198 return oldval != reg;
199}
200
201
202#define AWACS_VOLUME(xname, xreg, xshift, xinverted) \
203{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
204 .info = snd_pmac_awacs_info_volume, \
205 .get = snd_pmac_awacs_get_volume, \
206 .put = snd_pmac_awacs_put_volume, \
207 .private_value = (xreg) | ((xshift) << 8) | ((xinverted) << 16) }
208
209/*
210 * mute master/ogain for AWACS: mono
211 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100212static int snd_pmac_awacs_get_switch(struct snd_kcontrol *kcontrol,
213 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100215 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 int reg = kcontrol->private_value & 0xff;
217 int shift = (kcontrol->private_value >> 8) & 0xff;
218 int invert = (kcontrol->private_value >> 16) & 1;
219 int val;
220 unsigned long flags;
221
222 spin_lock_irqsave(&chip->reg_lock, flags);
223 val = (chip->awacs_reg[reg] >> shift) & 1;
224 spin_unlock_irqrestore(&chip->reg_lock, flags);
225 if (invert)
226 val = 1 - val;
227 ucontrol->value.integer.value[0] = val;
228 return 0;
229}
230
Takashi Iwai65b29f52005-11-17 15:09:46 +0100231static int snd_pmac_awacs_put_switch(struct snd_kcontrol *kcontrol,
232 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100234 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 int reg = kcontrol->private_value & 0xff;
236 int shift = (kcontrol->private_value >> 8) & 0xff;
237 int invert = (kcontrol->private_value >> 16) & 1;
238 int mask = 1 << shift;
239 int val, changed;
240 unsigned long flags;
241
242 spin_lock_irqsave(&chip->reg_lock, flags);
243 val = chip->awacs_reg[reg] & ~mask;
244 if (ucontrol->value.integer.value[0] != invert)
245 val |= mask;
246 changed = chip->awacs_reg[reg] != val;
247 if (changed)
248 snd_pmac_awacs_write_reg(chip, reg, val);
249 spin_unlock_irqrestore(&chip->reg_lock, flags);
250 return changed;
251}
252
253#define AWACS_SWITCH(xname, xreg, xshift, xinvert) \
254{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
255 .info = snd_pmac_boolean_mono_info, \
256 .get = snd_pmac_awacs_get_switch, \
257 .put = snd_pmac_awacs_put_switch, \
258 .private_value = (xreg) | ((xshift) << 8) | ((xinvert) << 16) }
259
260
261#ifdef PMAC_AMP_AVAIL
262/*
263 * controls for perch/whisper extension cards, e.g. G3 desktop
264 *
265 * TDA7433 connected via i2c address 0x45 (= 0x8a),
266 * accessed through cuda
267 */
268static void awacs_set_cuda(int reg, int val)
269{
270 struct adb_request req;
271 cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, reg, val);
272 while (! req.complete)
273 cuda_poll();
274}
275
276/*
277 * level = 0 - 14, 7 = 0 dB
278 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100279static void awacs_amp_set_tone(struct awacs_amp *amp, int bass, int treble)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280{
281 amp->amp_tone[0] = bass;
282 amp->amp_tone[1] = treble;
283 if (bass > 7)
284 bass = (14 - bass) + 8;
285 if (treble > 7)
286 treble = (14 - treble) + 8;
287 awacs_set_cuda(2, (bass << 4) | treble);
288}
289
290/*
291 * vol = 0 - 31 (attenuation), 32 = mute bit, stereo
292 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100293static int awacs_amp_set_vol(struct awacs_amp *amp, int index, int lvol, int rvol,
294 int do_check)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295{
296 if (do_check && amp->amp_vol[index][0] == lvol &&
297 amp->amp_vol[index][1] == rvol)
298 return 0;
299 awacs_set_cuda(3 + index, lvol);
300 awacs_set_cuda(5 + index, rvol);
301 amp->amp_vol[index][0] = lvol;
302 amp->amp_vol[index][1] = rvol;
303 return 1;
304}
305
306/*
307 * 0 = -79 dB, 79 = 0 dB, 99 = +20 dB
308 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100309static void awacs_amp_set_master(struct awacs_amp *amp, int vol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310{
311 amp->amp_master = vol;
312 if (vol <= 79)
313 vol = 32 + (79 - vol);
314 else
315 vol = 32 - (vol - 79);
316 awacs_set_cuda(1, vol);
317}
318
Takashi Iwai65b29f52005-11-17 15:09:46 +0100319static void awacs_amp_free(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100321 struct awacs_amp *amp = chip->mixer_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 snd_assert(amp, return);
323 kfree(amp);
324 chip->mixer_data = NULL;
325 chip->mixer_free = NULL;
326}
327
328
329/*
330 * mixer controls
331 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100332static int snd_pmac_awacs_info_volume_amp(struct snd_kcontrol *kcontrol,
333 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334{
335 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
336 uinfo->count = 2;
337 uinfo->value.integer.min = 0;
338 uinfo->value.integer.max = 31;
339 return 0;
340}
341
Takashi Iwai65b29f52005-11-17 15:09:46 +0100342static int snd_pmac_awacs_get_volume_amp(struct snd_kcontrol *kcontrol,
343 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100345 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 int index = kcontrol->private_value;
Takashi Iwai65b29f52005-11-17 15:09:46 +0100347 struct awacs_amp *amp = chip->mixer_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 snd_assert(amp, return -EINVAL);
349 snd_assert(index >= 0 && index <= 1, return -EINVAL);
350 ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31);
351 ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31);
352 return 0;
353}
354
Takashi Iwai65b29f52005-11-17 15:09:46 +0100355static int snd_pmac_awacs_put_volume_amp(struct snd_kcontrol *kcontrol,
356 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100358 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 int index = kcontrol->private_value;
360 int vol[2];
Takashi Iwai65b29f52005-11-17 15:09:46 +0100361 struct awacs_amp *amp = chip->mixer_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 snd_assert(amp, return -EINVAL);
363 snd_assert(index >= 0 && index <= 1, return -EINVAL);
364
365 vol[0] = (31 - (ucontrol->value.integer.value[0] & 31)) | (amp->amp_vol[index][0] & 32);
366 vol[1] = (31 - (ucontrol->value.integer.value[1] & 31)) | (amp->amp_vol[index][1] & 32);
367 return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
368}
369
Takashi Iwai65b29f52005-11-17 15:09:46 +0100370static int snd_pmac_awacs_get_switch_amp(struct snd_kcontrol *kcontrol,
371 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100373 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 int index = kcontrol->private_value;
Takashi Iwai65b29f52005-11-17 15:09:46 +0100375 struct awacs_amp *amp = chip->mixer_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 snd_assert(amp, return -EINVAL);
377 snd_assert(index >= 0 && index <= 1, return -EINVAL);
378 ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32) ? 0 : 1;
379 ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) ? 0 : 1;
380 return 0;
381}
382
Takashi Iwai65b29f52005-11-17 15:09:46 +0100383static int snd_pmac_awacs_put_switch_amp(struct snd_kcontrol *kcontrol,
384 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100386 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 int index = kcontrol->private_value;
388 int vol[2];
Takashi Iwai65b29f52005-11-17 15:09:46 +0100389 struct awacs_amp *amp = chip->mixer_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 snd_assert(amp, return -EINVAL);
391 snd_assert(index >= 0 && index <= 1, return -EINVAL);
392
393 vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32) | (amp->amp_vol[index][0] & 31);
394 vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32) | (amp->amp_vol[index][1] & 31);
395 return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
396}
397
Takashi Iwai65b29f52005-11-17 15:09:46 +0100398static int snd_pmac_awacs_info_tone_amp(struct snd_kcontrol *kcontrol,
399 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400{
401 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
402 uinfo->count = 1;
403 uinfo->value.integer.min = 0;
404 uinfo->value.integer.max = 14;
405 return 0;
406}
407
Takashi Iwai65b29f52005-11-17 15:09:46 +0100408static int snd_pmac_awacs_get_tone_amp(struct snd_kcontrol *kcontrol,
409 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100411 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 int index = kcontrol->private_value;
Takashi Iwai65b29f52005-11-17 15:09:46 +0100413 struct awacs_amp *amp = chip->mixer_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 snd_assert(amp, return -EINVAL);
415 snd_assert(index >= 0 && index <= 1, return -EINVAL);
416 ucontrol->value.integer.value[0] = amp->amp_tone[index];
417 return 0;
418}
419
Takashi Iwai65b29f52005-11-17 15:09:46 +0100420static int snd_pmac_awacs_put_tone_amp(struct snd_kcontrol *kcontrol,
421 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100423 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 int index = kcontrol->private_value;
Takashi Iwai65b29f52005-11-17 15:09:46 +0100425 struct awacs_amp *amp = chip->mixer_data;
Takashi Iwaid4079ac2007-11-15 16:14:12 +0100426 unsigned int val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 snd_assert(amp, return -EINVAL);
428 snd_assert(index >= 0 && index <= 1, return -EINVAL);
Takashi Iwaid4079ac2007-11-15 16:14:12 +0100429 val = ucontrol->value.integer.value[0];
430 if (val > 14)
431 return -EINVAL;
432 if (val != amp->amp_tone[index]) {
433 amp->amp_tone[index] = val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
435 return 1;
436 }
437 return 0;
438}
439
Takashi Iwai65b29f52005-11-17 15:09:46 +0100440static int snd_pmac_awacs_info_master_amp(struct snd_kcontrol *kcontrol,
441 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442{
443 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
444 uinfo->count = 1;
445 uinfo->value.integer.min = 0;
446 uinfo->value.integer.max = 99;
447 return 0;
448}
449
Takashi Iwai65b29f52005-11-17 15:09:46 +0100450static int snd_pmac_awacs_get_master_amp(struct snd_kcontrol *kcontrol,
451 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100453 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
454 struct awacs_amp *amp = chip->mixer_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 snd_assert(amp, return -EINVAL);
456 ucontrol->value.integer.value[0] = amp->amp_master;
457 return 0;
458}
459
Takashi Iwai65b29f52005-11-17 15:09:46 +0100460static int snd_pmac_awacs_put_master_amp(struct snd_kcontrol *kcontrol,
461 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100463 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
464 struct awacs_amp *amp = chip->mixer_data;
Takashi Iwaid4079ac2007-11-15 16:14:12 +0100465 unsigned int val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 snd_assert(amp, return -EINVAL);
Takashi Iwaid4079ac2007-11-15 16:14:12 +0100467 val = ucontrol->value.integer.value[0];
468 if (val > 99)
469 return -EINVAL;
470 if (val != amp->amp_master) {
471 amp->amp_master = val;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 awacs_amp_set_master(amp, amp->amp_master);
473 return 1;
474 }
475 return 0;
476}
477
478#define AMP_CH_SPK 0
479#define AMP_CH_HD 1
480
Takashi Iwai65b29f52005-11-17 15:09:46 +0100481static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] __initdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
483 .name = "PC Speaker Playback Volume",
484 .info = snd_pmac_awacs_info_volume_amp,
485 .get = snd_pmac_awacs_get_volume_amp,
486 .put = snd_pmac_awacs_put_volume_amp,
487 .private_value = AMP_CH_SPK,
488 },
489 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
490 .name = "Headphone Playback Volume",
491 .info = snd_pmac_awacs_info_volume_amp,
492 .get = snd_pmac_awacs_get_volume_amp,
493 .put = snd_pmac_awacs_put_volume_amp,
494 .private_value = AMP_CH_HD,
495 },
496 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
497 .name = "Tone Control - Bass",
498 .info = snd_pmac_awacs_info_tone_amp,
499 .get = snd_pmac_awacs_get_tone_amp,
500 .put = snd_pmac_awacs_put_tone_amp,
501 .private_value = 0,
502 },
503 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
504 .name = "Tone Control - Treble",
505 .info = snd_pmac_awacs_info_tone_amp,
506 .get = snd_pmac_awacs_get_tone_amp,
507 .put = snd_pmac_awacs_put_tone_amp,
508 .private_value = 1,
509 },
510 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
511 .name = "Amp Master Playback Volume",
512 .info = snd_pmac_awacs_info_master_amp,
513 .get = snd_pmac_awacs_get_master_amp,
514 .put = snd_pmac_awacs_put_master_amp,
515 },
516};
517
Takashi Iwai65b29f52005-11-17 15:09:46 +0100518static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw __initdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
520 .name = "Headphone Playback Switch",
521 .info = snd_pmac_boolean_stereo_info,
522 .get = snd_pmac_awacs_get_switch_amp,
523 .put = snd_pmac_awacs_put_switch_amp,
524 .private_value = AMP_CH_HD,
525};
526
Takashi Iwai65b29f52005-11-17 15:09:46 +0100527static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw __initdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
529 .name = "PC Speaker Playback Switch",
530 .info = snd_pmac_boolean_stereo_info,
531 .get = snd_pmac_awacs_get_switch_amp,
532 .put = snd_pmac_awacs_put_switch_amp,
533 .private_value = AMP_CH_SPK,
534};
535
536#endif /* PMAC_AMP_AVAIL */
537
538
539/*
540 * mic boost for screamer
541 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100542static int snd_pmac_screamer_mic_boost_info(struct snd_kcontrol *kcontrol,
543 struct snd_ctl_elem_info *uinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544{
545 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
546 uinfo->count = 1;
547 uinfo->value.integer.min = 0;
548 uinfo->value.integer.max = 2;
549 return 0;
550}
551
Takashi Iwai65b29f52005-11-17 15:09:46 +0100552static int snd_pmac_screamer_mic_boost_get(struct snd_kcontrol *kcontrol,
553 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100555 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 int val;
557 unsigned long flags;
558
559 spin_lock_irqsave(&chip->reg_lock, flags);
560 if (chip->awacs_reg[6] & MASK_MIC_BOOST)
561 val = 2;
562 else if (chip->awacs_reg[0] & MASK_GAINLINE)
563 val = 1;
564 else
565 val = 0;
566 spin_unlock_irqrestore(&chip->reg_lock, flags);
567 ucontrol->value.integer.value[0] = val;
568 return 0;
569}
570
Takashi Iwai65b29f52005-11-17 15:09:46 +0100571static int snd_pmac_screamer_mic_boost_put(struct snd_kcontrol *kcontrol,
572 struct snd_ctl_elem_value *ucontrol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573{
Takashi Iwai65b29f52005-11-17 15:09:46 +0100574 struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 int changed = 0;
576 int val0, val6;
577 unsigned long flags;
578
579 spin_lock_irqsave(&chip->reg_lock, flags);
580 val0 = chip->awacs_reg[0] & ~MASK_GAINLINE;
581 val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST;
582 if (ucontrol->value.integer.value[0] > 0) {
583 val0 |= MASK_GAINLINE;
584 if (ucontrol->value.integer.value[0] > 1)
585 val6 |= MASK_MIC_BOOST;
586 }
587 if (val0 != chip->awacs_reg[0]) {
588 snd_pmac_awacs_write_reg(chip, 0, val0);
589 changed = 1;
590 }
591 if (val6 != chip->awacs_reg[6]) {
592 snd_pmac_awacs_write_reg(chip, 6, val6);
593 changed = 1;
594 }
595 spin_unlock_irqrestore(&chip->reg_lock, flags);
596 return changed;
597}
598
599/*
600 * lists of mixer elements
601 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100602static struct snd_kcontrol_new snd_pmac_awacs_mixers[] __initdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
604 AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0),
605 AWACS_VOLUME("Capture Volume", 0, 4, 0),
606 AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
607};
608
609/* FIXME: is this correct order?
610 * screamer (powerbook G3 pismo) seems to have different bits...
611 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100612static struct snd_kcontrol_new snd_pmac_awacs_mixers2[] __initdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0),
614 AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0),
615};
616
Takashi Iwai65b29f52005-11-17 15:09:46 +0100617static struct snd_kcontrol_new snd_pmac_screamer_mixers2[] __initdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
619 AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0),
620};
621
Takashi Iwai65b29f52005-11-17 15:09:46 +0100622static struct snd_kcontrol_new snd_pmac_awacs_master_sw __initdata =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1);
624
Takashi Iwai65b29f52005-11-17 15:09:46 +0100625static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] __initdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 AWACS_SWITCH("Mic Boost", 0, SHIFT_GAINLINE, 0),
627};
628
Takashi Iwai65b29f52005-11-17 15:09:46 +0100629static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __initdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
631 .name = "Mic Boost",
632 .info = snd_pmac_screamer_mic_boost_info,
633 .get = snd_pmac_screamer_mic_boost_get,
634 .put = snd_pmac_screamer_mic_boost_put,
635 },
636};
637
Takashi Iwai65b29f52005-11-17 15:09:46 +0100638static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __initdata = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1),
640};
Takashi Iwai65b29f52005-11-17 15:09:46 +0100641static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __initdata =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
643
644
645/*
646 * add new mixer elements to the card
647 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100648static int build_mixers(struct snd_pmac *chip, int nums, struct snd_kcontrol_new *mixers)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649{
650 int i, err;
651
652 for (i = 0; i < nums; i++) {
653 if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0)
654 return err;
655 }
656 return 0;
657}
658
659
660/*
661 * restore all registers
662 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100663static void awacs_restore_all_regs(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664{
665 snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]);
666 snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
667 snd_pmac_awacs_write_noreg(chip, 2, chip->awacs_reg[2]);
668 snd_pmac_awacs_write_noreg(chip, 4, chip->awacs_reg[4]);
669 if (chip->model == PMAC_SCREAMER) {
670 snd_pmac_awacs_write_noreg(chip, 5, chip->awacs_reg[5]);
671 snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
672 snd_pmac_awacs_write_noreg(chip, 7, chip->awacs_reg[7]);
673 }
674}
675
Benjamin Herrenschmidt8c870932005-06-27 14:36:34 -0700676#ifdef CONFIG_PM
Takashi Iwai65b29f52005-11-17 15:09:46 +0100677static void snd_pmac_awacs_suspend(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678{
679 snd_pmac_awacs_write_noreg(chip, 1, (chip->awacs_reg[1]
680 | MASK_AMUTE | MASK_CMUTE));
681}
682
Takashi Iwai65b29f52005-11-17 15:09:46 +0100683static void snd_pmac_awacs_resume(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684{
685 if (machine_is_compatible("PowerBook3,1")
686 || machine_is_compatible("PowerBook3,2")) {
Nishanth Aravamudan989a0b22005-07-09 10:53:24 +0200687 msleep(100);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 snd_pmac_awacs_write_reg(chip, 1,
689 chip->awacs_reg[1] & ~MASK_PAROUT);
Nishanth Aravamudan989a0b22005-07-09 10:53:24 +0200690 msleep(300);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691 }
692
693 awacs_restore_all_regs(chip);
694 if (chip->model == PMAC_SCREAMER) {
695 /* reset power bits in reg 6 */
696 mdelay(5);
697 snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
698 }
699 screamer_recalibrate(chip);
700#ifdef PMAC_AMP_AVAIL
701 if (chip->mixer_data) {
Takashi Iwai65b29f52005-11-17 15:09:46 +0100702 struct awacs_amp *amp = chip->mixer_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 awacs_amp_set_vol(amp, 0, amp->amp_vol[0][0], amp->amp_vol[0][1], 0);
704 awacs_amp_set_vol(amp, 1, amp->amp_vol[1][0], amp->amp_vol[1][1], 0);
705 awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]);
706 awacs_amp_set_master(amp, amp->amp_master);
707 }
708#endif
709}
Benjamin Herrenschmidt8c870932005-06-27 14:36:34 -0700710#endif /* CONFIG_PM */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711
712#ifdef PMAC_SUPPORT_AUTOMUTE
713/*
714 * auto-mute stuffs
715 */
Takashi Iwai65b29f52005-11-17 15:09:46 +0100716static int snd_pmac_awacs_detect_headphone(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717{
718 return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0;
719}
720
721#ifdef PMAC_AMP_AVAIL
Takashi Iwai65b29f52005-11-17 15:09:46 +0100722static int toggle_amp_mute(struct awacs_amp *amp, int index, int mute)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723{
724 int vol[2];
725 vol[0] = amp->amp_vol[index][0] & 31;
726 vol[1] = amp->amp_vol[index][1] & 31;
727 if (mute) {
728 vol[0] |= 32;
729 vol[1] |= 32;
730 }
731 return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1);
732}
733#endif
734
Takashi Iwai65b29f52005-11-17 15:09:46 +0100735static void snd_pmac_awacs_update_automute(struct snd_pmac *chip, int do_notify)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736{
737 if (chip->auto_mute) {
738#ifdef PMAC_AMP_AVAIL
739 if (chip->mixer_data) {
Takashi Iwai65b29f52005-11-17 15:09:46 +0100740 struct awacs_amp *amp = chip->mixer_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 int changed;
742 if (snd_pmac_awacs_detect_headphone(chip)) {
743 changed = toggle_amp_mute(amp, AMP_CH_HD, 0);
744 changed |= toggle_amp_mute(amp, AMP_CH_SPK, 1);
745 } else {
746 changed = toggle_amp_mute(amp, AMP_CH_HD, 1);
747 changed |= toggle_amp_mute(amp, AMP_CH_SPK, 0);
748 }
749 if (do_notify && ! changed)
750 return;
751 } else
752#endif
753 {
754 int reg = chip->awacs_reg[1] | (MASK_HDMUTE|MASK_SPKMUTE);
755 if (snd_pmac_awacs_detect_headphone(chip))
756 reg &= ~MASK_HDMUTE;
757 else
758 reg &= ~MASK_SPKMUTE;
759 if (do_notify && reg == chip->awacs_reg[1])
760 return;
761 snd_pmac_awacs_write_reg(chip, 1, reg);
762 }
763 if (do_notify) {
764 snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
765 &chip->master_sw_ctl->id);
766 snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
767 &chip->speaker_sw_ctl->id);
768 snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
769 &chip->hp_detect_ctl->id);
770 }
771 }
772}
773#endif /* PMAC_SUPPORT_AUTOMUTE */
774
775
776/*
777 * initialize chip
778 */
779int __init
Takashi Iwai65b29f52005-11-17 15:09:46 +0100780snd_pmac_awacs_init(struct snd_pmac *chip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781{
782 int err, vol;
783
784 /* looks like MASK_GAINLINE triggers something, so we set here
785 * as start-up
786 */
787 chip->awacs_reg[0] = MASK_MUX_CD | 0xff | MASK_GAINLINE;
788 chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE;
789 /* FIXME: Only machines with external SRS module need MASK_PAROUT */
790 if (chip->has_iic || chip->device_id == 0x5 ||
791 /*chip->_device_id == 0x8 || */
792 chip->device_id == 0xb)
793 chip->awacs_reg[1] |= MASK_PAROUT;
794 /* get default volume from nvram */
795 // vol = (~nvram_read_byte(0x1308) & 7) << 1;
796 // vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 );
797 vol = 0x0f; /* no, on alsa, muted as default */
798 vol = vol + (vol << 6);
799 chip->awacs_reg[2] = vol;
800 chip->awacs_reg[4] = vol;
801 if (chip->model == PMAC_SCREAMER) {
802 chip->awacs_reg[5] = vol; /* FIXME: screamer has loopthru vol control */
803 chip->awacs_reg[6] = MASK_MIC_BOOST; /* FIXME: maybe should be vol << 3 for PCMCIA speaker */
804 chip->awacs_reg[7] = 0;
805 }
806
807 awacs_restore_all_regs(chip);
808 chip->manufacturer = (in_le32(&chip->awacs->codec_stat) >> 8) & 0xf;
809 screamer_recalibrate(chip);
810
811 chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf;
812#ifdef PMAC_AMP_AVAIL
813 if (chip->revision == 3 && chip->has_iic && CHECK_CUDA_AMP()) {
Panagiotis Issaris59feddb2006-07-25 15:28:03 +0200814 struct awacs_amp *amp = kzalloc(sizeof(*amp), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 if (! amp)
816 return -ENOMEM;
817 chip->mixer_data = amp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 chip->mixer_free = awacs_amp_free;
819 awacs_amp_set_vol(amp, 0, 63, 63, 0); /* mute and zero vol */
820 awacs_amp_set_vol(amp, 1, 63, 63, 0);
821 awacs_amp_set_tone(amp, 7, 7); /* 0 dB */
822 awacs_amp_set_master(amp, 79); /* 0 dB */
823 }
824#endif /* PMAC_AMP_AVAIL */
825
826 if (chip->hp_stat_mask == 0) {
827 /* set headphone-jack detection bit */
828 switch (chip->model) {
829 case PMAC_AWACS:
830 chip->hp_stat_mask = 0x04;
831 break;
832 case PMAC_SCREAMER:
833 switch (chip->device_id) {
834 case 0x08:
835 /* 1 = side jack, 2 = front jack */
836 chip->hp_stat_mask = 0x03;
837 break;
838 case 0x00:
839 case 0x05:
840 chip->hp_stat_mask = 0x04;
841 break;
842 default:
843 chip->hp_stat_mask = 0x08;
844 break;
845 }
846 break;
847 default:
848 snd_BUG();
849 break;
850 }
851 }
852
853 /*
854 * build mixers
855 */
856 strcpy(chip->card->mixername, "PowerMac AWACS");
857
858 if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers),
859 snd_pmac_awacs_mixers)) < 0)
860 return err;
861 if (chip->model == PMAC_SCREAMER)
862 err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2),
863 snd_pmac_screamer_mixers2);
864 else
865 err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2),
866 snd_pmac_awacs_mixers2);
867 if (err < 0)
868 return err;
869 chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_master_sw, chip);
870 if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0)
871 return err;
872#ifdef PMAC_AMP_AVAIL
873 if (chip->mixer_data) {
874 /* use amplifier. the signal is connected from route A
875 * to the amp. the amp has its headphone and speaker
876 * volumes and mute switches, so we use them instead of
877 * screamer registers.
878 * in this case, it seems the route C is not used.
879 */
880 if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol),
881 snd_pmac_awacs_amp_vol)) < 0)
882 return err;
883 /* overwrite */
884 chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw, chip);
885 if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0)
886 return err;
887 chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw, chip);
888 if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0)
889 return err;
890 } else
891#endif /* PMAC_AMP_AVAIL */
892 {
893 /* route A = headphone, route C = speaker */
894 if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol),
895 snd_pmac_awacs_speaker_vol)) < 0)
896 return err;
897 chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_speaker_sw, chip);
898 if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0)
899 return err;
900 }
901
902 if (chip->model == PMAC_SCREAMER) {
903 if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mic_boost),
904 snd_pmac_screamer_mic_boost)) < 0)
905 return err;
906 } else {
907 if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost),
908 snd_pmac_awacs_mic_boost)) < 0)
909 return err;
910 }
911
912 /*
913 * set lowlevel callbacks
914 */
915 chip->set_format = snd_pmac_awacs_set_format;
Benjamin Herrenschmidt8c870932005-06-27 14:36:34 -0700916#ifdef CONFIG_PM
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 chip->suspend = snd_pmac_awacs_suspend;
918 chip->resume = snd_pmac_awacs_resume;
919#endif
920#ifdef PMAC_SUPPORT_AUTOMUTE
921 if ((err = snd_pmac_add_automute(chip)) < 0)
922 return err;
923 chip->detect_headphone = snd_pmac_awacs_detect_headphone;
924 chip->update_automute = snd_pmac_awacs_update_automute;
925 snd_pmac_awacs_update_automute(chip, 0); /* update the status only */
926#endif
927 if (chip->model == PMAC_SCREAMER) {
928 snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
929 snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]);
930 }
931
932 return 0;
933}