diff options
Diffstat (limited to 'sound/ppc')
-rw-r--r-- | sound/ppc/Kconfig | 23 | ||||
-rw-r--r-- | sound/ppc/Makefile | 9 | ||||
-rw-r--r-- | sound/ppc/awacs.c | 903 | ||||
-rw-r--r-- | sound/ppc/awacs.h | 192 | ||||
-rw-r--r-- | sound/ppc/beep.c | 262 | ||||
-rw-r--r-- | sound/ppc/burgundy.c | 439 | ||||
-rw-r--r-- | sound/ppc/burgundy.h | 95 | ||||
-rw-r--r-- | sound/ppc/daca.c | 283 | ||||
-rw-r--r-- | sound/ppc/keywest.c | 143 | ||||
-rw-r--r-- | sound/ppc/pmac.c | 1328 | ||||
-rw-r--r-- | sound/ppc/pmac.h | 214 | ||||
-rw-r--r-- | sound/ppc/powermac.c | 159 | ||||
-rw-r--r-- | sound/ppc/tumbler.c | 1175 | ||||
-rw-r--r-- | sound/ppc/tumbler_volume.h | 250 |
14 files changed, 5475 insertions, 0 deletions
diff --git a/sound/ppc/Kconfig b/sound/ppc/Kconfig new file mode 100644 index 000000000000..b0a9ebf8bf3b --- /dev/null +++ b/sound/ppc/Kconfig | |||
@@ -0,0 +1,23 @@ | |||
1 | # ALSA PowerMac drivers | ||
2 | |||
3 | menu "ALSA PowerMac devices" | ||
4 | depends on SND!=n && PPC | ||
5 | |||
6 | comment "ALSA PowerMac requires I2C" | ||
7 | depends on SND && I2C=n | ||
8 | |||
9 | comment "ALSA PowerMac requires INPUT" | ||
10 | depends on SND && INPUT=n | ||
11 | |||
12 | config SND_POWERMAC | ||
13 | tristate "PowerMac (AWACS, DACA, Burgundy, Tumbler, Keywest)" | ||
14 | depends on SND && I2C && INPUT | ||
15 | select SND_PCM | ||
16 | help | ||
17 | Say Y here to include support for the integrated sound device. | ||
18 | |||
19 | To compile this driver as a module, choose M here: the module | ||
20 | will be called snd-powermac. | ||
21 | |||
22 | endmenu | ||
23 | |||
diff --git a/sound/ppc/Makefile b/sound/ppc/Makefile new file mode 100644 index 000000000000..4d95c652c8ca --- /dev/null +++ b/sound/ppc/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # Makefile for ALSA | ||
3 | # Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz> | ||
4 | # | ||
5 | |||
6 | snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o keywest.o beep.o | ||
7 | |||
8 | # Toplevel Module Dependency | ||
9 | obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o | ||
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c new file mode 100644 index 000000000000..e052bd071e5b --- /dev/null +++ b/sound/ppc/awacs.c | |||
@@ -0,0 +1,903 @@ | |||
1 | /* | ||
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 | ||
38 | typedef struct awacs_amp { | ||
39 | unsigned char amp_master; | ||
40 | unsigned char amp_vol[2][2]; | ||
41 | unsigned char amp_tone[2]; | ||
42 | } awacs_amp_t; | ||
43 | |||
44 | #define CHECK_CUDA_AMP() (sys_ctrler == SYS_CTRLER_CUDA) | ||
45 | |||
46 | #endif /* PMAC_AMP_AVAIL */ | ||
47 | |||
48 | |||
49 | static void snd_pmac_screamer_wait(pmac_t *chip) | ||
50 | { | ||
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 | */ | ||
64 | static void | ||
65 | snd_pmac_awacs_write(pmac_t *chip, int val) | ||
66 | { | ||
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 | |||
80 | static void | ||
81 | snd_pmac_awacs_write_reg(pmac_t *chip, int reg, int val) | ||
82 | { | ||
83 | snd_pmac_awacs_write(chip, val | (reg << 12)); | ||
84 | chip->awacs_reg[reg] = val; | ||
85 | } | ||
86 | |||
87 | static void | ||
88 | snd_pmac_awacs_write_noreg(pmac_t *chip, int reg, int val) | ||
89 | { | ||
90 | snd_pmac_awacs_write(chip, val | (reg << 12)); | ||
91 | } | ||
92 | |||
93 | #ifdef CONFIG_PMAC_PBOOK | ||
94 | /* Recalibrate chip */ | ||
95 | static void screamer_recalibrate(pmac_t *chip) | ||
96 | { | ||
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 */ | ||
106 | big_mdelay(750); | ||
107 | snd_pmac_awacs_write_noreg(chip, 1, | ||
108 | chip->awacs_reg[1] | MASK_RECALIBRATE | MASK_CMUTE | MASK_AMUTE); | ||
109 | snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); | ||
110 | snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); | ||
111 | } | ||
112 | |||
113 | #else | ||
114 | #define screamer_recalibrate(chip) /* NOP */ | ||
115 | #endif | ||
116 | |||
117 | |||
118 | /* | ||
119 | * additional callback to set the pcm format | ||
120 | */ | ||
121 | static void snd_pmac_awacs_set_format(pmac_t *chip) | ||
122 | { | ||
123 | chip->awacs_reg[1] &= ~MASK_SAMPLERATE; | ||
124 | chip->awacs_reg[1] |= chip->rate_index << 3; | ||
125 | snd_pmac_awacs_write_reg(chip, 1, chip->awacs_reg[1]); | ||
126 | } | ||
127 | |||
128 | |||
129 | /* | ||
130 | * AWACS volume callbacks | ||
131 | */ | ||
132 | /* | ||
133 | * volumes: 0-15 stereo | ||
134 | */ | ||
135 | static int snd_pmac_awacs_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
136 | { | ||
137 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
138 | uinfo->count = 2; | ||
139 | uinfo->value.integer.min = 0; | ||
140 | uinfo->value.integer.max = 15; | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int snd_pmac_awacs_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
145 | { | ||
146 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
147 | int reg = kcontrol->private_value & 0xff; | ||
148 | int lshift = (kcontrol->private_value >> 8) & 0xff; | ||
149 | int inverted = (kcontrol->private_value >> 16) & 1; | ||
150 | unsigned long flags; | ||
151 | int vol[2]; | ||
152 | |||
153 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
154 | vol[0] = (chip->awacs_reg[reg] >> lshift) & 0xf; | ||
155 | vol[1] = chip->awacs_reg[reg] & 0xf; | ||
156 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
157 | if (inverted) { | ||
158 | vol[0] = 0x0f - vol[0]; | ||
159 | vol[1] = 0x0f - vol[1]; | ||
160 | } | ||
161 | ucontrol->value.integer.value[0] = vol[0]; | ||
162 | ucontrol->value.integer.value[1] = vol[1]; | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int snd_pmac_awacs_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
167 | { | ||
168 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
169 | int reg = kcontrol->private_value & 0xff; | ||
170 | int lshift = (kcontrol->private_value >> 8) & 0xff; | ||
171 | int inverted = (kcontrol->private_value >> 16) & 1; | ||
172 | int val, oldval; | ||
173 | unsigned long flags; | ||
174 | int vol[2]; | ||
175 | |||
176 | vol[0] = ucontrol->value.integer.value[0]; | ||
177 | vol[1] = ucontrol->value.integer.value[1]; | ||
178 | if (inverted) { | ||
179 | vol[0] = 0x0f - vol[0]; | ||
180 | vol[1] = 0x0f - vol[1]; | ||
181 | } | ||
182 | vol[0] &= 0x0f; | ||
183 | vol[1] &= 0x0f; | ||
184 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
185 | oldval = chip->awacs_reg[reg]; | ||
186 | val = oldval & ~(0xf | (0xf << lshift)); | ||
187 | val |= vol[0] << lshift; | ||
188 | val |= vol[1]; | ||
189 | if (oldval != val) | ||
190 | snd_pmac_awacs_write_reg(chip, reg, val); | ||
191 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
192 | return oldval != reg; | ||
193 | } | ||
194 | |||
195 | |||
196 | #define AWACS_VOLUME(xname, xreg, xshift, xinverted) \ | ||
197 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ | ||
198 | .info = snd_pmac_awacs_info_volume, \ | ||
199 | .get = snd_pmac_awacs_get_volume, \ | ||
200 | .put = snd_pmac_awacs_put_volume, \ | ||
201 | .private_value = (xreg) | ((xshift) << 8) | ((xinverted) << 16) } | ||
202 | |||
203 | /* | ||
204 | * mute master/ogain for AWACS: mono | ||
205 | */ | ||
206 | static int snd_pmac_awacs_get_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
207 | { | ||
208 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
209 | int reg = kcontrol->private_value & 0xff; | ||
210 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
211 | int invert = (kcontrol->private_value >> 16) & 1; | ||
212 | int val; | ||
213 | unsigned long flags; | ||
214 | |||
215 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
216 | val = (chip->awacs_reg[reg] >> shift) & 1; | ||
217 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
218 | if (invert) | ||
219 | val = 1 - val; | ||
220 | ucontrol->value.integer.value[0] = val; | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int snd_pmac_awacs_put_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
225 | { | ||
226 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
227 | int reg = kcontrol->private_value & 0xff; | ||
228 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
229 | int invert = (kcontrol->private_value >> 16) & 1; | ||
230 | int mask = 1 << shift; | ||
231 | int val, changed; | ||
232 | unsigned long flags; | ||
233 | |||
234 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
235 | val = chip->awacs_reg[reg] & ~mask; | ||
236 | if (ucontrol->value.integer.value[0] != invert) | ||
237 | val |= mask; | ||
238 | changed = chip->awacs_reg[reg] != val; | ||
239 | if (changed) | ||
240 | snd_pmac_awacs_write_reg(chip, reg, val); | ||
241 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
242 | return changed; | ||
243 | } | ||
244 | |||
245 | #define AWACS_SWITCH(xname, xreg, xshift, xinvert) \ | ||
246 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \ | ||
247 | .info = snd_pmac_boolean_mono_info, \ | ||
248 | .get = snd_pmac_awacs_get_switch, \ | ||
249 | .put = snd_pmac_awacs_put_switch, \ | ||
250 | .private_value = (xreg) | ((xshift) << 8) | ((xinvert) << 16) } | ||
251 | |||
252 | |||
253 | #ifdef PMAC_AMP_AVAIL | ||
254 | /* | ||
255 | * controls for perch/whisper extension cards, e.g. G3 desktop | ||
256 | * | ||
257 | * TDA7433 connected via i2c address 0x45 (= 0x8a), | ||
258 | * accessed through cuda | ||
259 | */ | ||
260 | static void awacs_set_cuda(int reg, int val) | ||
261 | { | ||
262 | struct adb_request req; | ||
263 | cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC, 0x8a, reg, val); | ||
264 | while (! req.complete) | ||
265 | cuda_poll(); | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * level = 0 - 14, 7 = 0 dB | ||
270 | */ | ||
271 | static void awacs_amp_set_tone(awacs_amp_t *amp, int bass, int treble) | ||
272 | { | ||
273 | amp->amp_tone[0] = bass; | ||
274 | amp->amp_tone[1] = treble; | ||
275 | if (bass > 7) | ||
276 | bass = (14 - bass) + 8; | ||
277 | if (treble > 7) | ||
278 | treble = (14 - treble) + 8; | ||
279 | awacs_set_cuda(2, (bass << 4) | treble); | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * vol = 0 - 31 (attenuation), 32 = mute bit, stereo | ||
284 | */ | ||
285 | static int awacs_amp_set_vol(awacs_amp_t *amp, int index, int lvol, int rvol, int do_check) | ||
286 | { | ||
287 | if (do_check && amp->amp_vol[index][0] == lvol && | ||
288 | amp->amp_vol[index][1] == rvol) | ||
289 | return 0; | ||
290 | awacs_set_cuda(3 + index, lvol); | ||
291 | awacs_set_cuda(5 + index, rvol); | ||
292 | amp->amp_vol[index][0] = lvol; | ||
293 | amp->amp_vol[index][1] = rvol; | ||
294 | return 1; | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * 0 = -79 dB, 79 = 0 dB, 99 = +20 dB | ||
299 | */ | ||
300 | static void awacs_amp_set_master(awacs_amp_t *amp, int vol) | ||
301 | { | ||
302 | amp->amp_master = vol; | ||
303 | if (vol <= 79) | ||
304 | vol = 32 + (79 - vol); | ||
305 | else | ||
306 | vol = 32 - (vol - 79); | ||
307 | awacs_set_cuda(1, vol); | ||
308 | } | ||
309 | |||
310 | static void awacs_amp_free(pmac_t *chip) | ||
311 | { | ||
312 | awacs_amp_t *amp = chip->mixer_data; | ||
313 | snd_assert(amp, return); | ||
314 | kfree(amp); | ||
315 | chip->mixer_data = NULL; | ||
316 | chip->mixer_free = NULL; | ||
317 | } | ||
318 | |||
319 | |||
320 | /* | ||
321 | * mixer controls | ||
322 | */ | ||
323 | static int snd_pmac_awacs_info_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
324 | { | ||
325 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
326 | uinfo->count = 2; | ||
327 | uinfo->value.integer.min = 0; | ||
328 | uinfo->value.integer.max = 31; | ||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static int snd_pmac_awacs_get_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
333 | { | ||
334 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
335 | int index = kcontrol->private_value; | ||
336 | awacs_amp_t *amp = chip->mixer_data; | ||
337 | snd_assert(amp, return -EINVAL); | ||
338 | snd_assert(index >= 0 && index <= 1, return -EINVAL); | ||
339 | ucontrol->value.integer.value[0] = 31 - (amp->amp_vol[index][0] & 31); | ||
340 | ucontrol->value.integer.value[1] = 31 - (amp->amp_vol[index][1] & 31); | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static int snd_pmac_awacs_put_volume_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
345 | { | ||
346 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
347 | int index = kcontrol->private_value; | ||
348 | int vol[2]; | ||
349 | awacs_amp_t *amp = chip->mixer_data; | ||
350 | snd_assert(amp, return -EINVAL); | ||
351 | snd_assert(index >= 0 && index <= 1, return -EINVAL); | ||
352 | |||
353 | vol[0] = (31 - (ucontrol->value.integer.value[0] & 31)) | (amp->amp_vol[index][0] & 32); | ||
354 | vol[1] = (31 - (ucontrol->value.integer.value[1] & 31)) | (amp->amp_vol[index][1] & 32); | ||
355 | return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); | ||
356 | } | ||
357 | |||
358 | static int snd_pmac_awacs_get_switch_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
359 | { | ||
360 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
361 | int index = kcontrol->private_value; | ||
362 | awacs_amp_t *amp = chip->mixer_data; | ||
363 | snd_assert(amp, return -EINVAL); | ||
364 | snd_assert(index >= 0 && index <= 1, return -EINVAL); | ||
365 | ucontrol->value.integer.value[0] = (amp->amp_vol[index][0] & 32) ? 0 : 1; | ||
366 | ucontrol->value.integer.value[1] = (amp->amp_vol[index][1] & 32) ? 0 : 1; | ||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static int snd_pmac_awacs_put_switch_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
371 | { | ||
372 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
373 | int index = kcontrol->private_value; | ||
374 | int vol[2]; | ||
375 | awacs_amp_t *amp = chip->mixer_data; | ||
376 | snd_assert(amp, return -EINVAL); | ||
377 | snd_assert(index >= 0 && index <= 1, return -EINVAL); | ||
378 | |||
379 | vol[0] = (ucontrol->value.integer.value[0] ? 0 : 32) | (amp->amp_vol[index][0] & 31); | ||
380 | vol[1] = (ucontrol->value.integer.value[1] ? 0 : 32) | (amp->amp_vol[index][1] & 31); | ||
381 | return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); | ||
382 | } | ||
383 | |||
384 | static int snd_pmac_awacs_info_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
385 | { | ||
386 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
387 | uinfo->count = 1; | ||
388 | uinfo->value.integer.min = 0; | ||
389 | uinfo->value.integer.max = 14; | ||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | static int snd_pmac_awacs_get_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
394 | { | ||
395 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
396 | int index = kcontrol->private_value; | ||
397 | awacs_amp_t *amp = chip->mixer_data; | ||
398 | snd_assert(amp, return -EINVAL); | ||
399 | snd_assert(index >= 0 && index <= 1, return -EINVAL); | ||
400 | ucontrol->value.integer.value[0] = amp->amp_tone[index]; | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int snd_pmac_awacs_put_tone_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
405 | { | ||
406 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
407 | int index = kcontrol->private_value; | ||
408 | awacs_amp_t *amp = chip->mixer_data; | ||
409 | snd_assert(amp, return -EINVAL); | ||
410 | snd_assert(index >= 0 && index <= 1, return -EINVAL); | ||
411 | if (ucontrol->value.integer.value[0] != amp->amp_tone[index]) { | ||
412 | amp->amp_tone[index] = ucontrol->value.integer.value[0]; | ||
413 | awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); | ||
414 | return 1; | ||
415 | } | ||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | static int snd_pmac_awacs_info_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
420 | { | ||
421 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
422 | uinfo->count = 1; | ||
423 | uinfo->value.integer.min = 0; | ||
424 | uinfo->value.integer.max = 99; | ||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | static int snd_pmac_awacs_get_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
429 | { | ||
430 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
431 | awacs_amp_t *amp = chip->mixer_data; | ||
432 | snd_assert(amp, return -EINVAL); | ||
433 | ucontrol->value.integer.value[0] = amp->amp_master; | ||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | static int snd_pmac_awacs_put_master_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
438 | { | ||
439 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
440 | awacs_amp_t *amp = chip->mixer_data; | ||
441 | snd_assert(amp, return -EINVAL); | ||
442 | if (ucontrol->value.integer.value[0] != amp->amp_master) { | ||
443 | amp->amp_master = ucontrol->value.integer.value[0]; | ||
444 | awacs_amp_set_master(amp, amp->amp_master); | ||
445 | return 1; | ||
446 | } | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | #define AMP_CH_SPK 0 | ||
451 | #define AMP_CH_HD 1 | ||
452 | |||
453 | static snd_kcontrol_new_t snd_pmac_awacs_amp_vol[] __initdata = { | ||
454 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
455 | .name = "PC Speaker Playback Volume", | ||
456 | .info = snd_pmac_awacs_info_volume_amp, | ||
457 | .get = snd_pmac_awacs_get_volume_amp, | ||
458 | .put = snd_pmac_awacs_put_volume_amp, | ||
459 | .private_value = AMP_CH_SPK, | ||
460 | }, | ||
461 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
462 | .name = "Headphone Playback Volume", | ||
463 | .info = snd_pmac_awacs_info_volume_amp, | ||
464 | .get = snd_pmac_awacs_get_volume_amp, | ||
465 | .put = snd_pmac_awacs_put_volume_amp, | ||
466 | .private_value = AMP_CH_HD, | ||
467 | }, | ||
468 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
469 | .name = "Tone Control - Bass", | ||
470 | .info = snd_pmac_awacs_info_tone_amp, | ||
471 | .get = snd_pmac_awacs_get_tone_amp, | ||
472 | .put = snd_pmac_awacs_put_tone_amp, | ||
473 | .private_value = 0, | ||
474 | }, | ||
475 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
476 | .name = "Tone Control - Treble", | ||
477 | .info = snd_pmac_awacs_info_tone_amp, | ||
478 | .get = snd_pmac_awacs_get_tone_amp, | ||
479 | .put = snd_pmac_awacs_put_tone_amp, | ||
480 | .private_value = 1, | ||
481 | }, | ||
482 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
483 | .name = "Amp Master Playback Volume", | ||
484 | .info = snd_pmac_awacs_info_master_amp, | ||
485 | .get = snd_pmac_awacs_get_master_amp, | ||
486 | .put = snd_pmac_awacs_put_master_amp, | ||
487 | }, | ||
488 | }; | ||
489 | |||
490 | static snd_kcontrol_new_t snd_pmac_awacs_amp_hp_sw __initdata = { | ||
491 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
492 | .name = "Headphone Playback Switch", | ||
493 | .info = snd_pmac_boolean_stereo_info, | ||
494 | .get = snd_pmac_awacs_get_switch_amp, | ||
495 | .put = snd_pmac_awacs_put_switch_amp, | ||
496 | .private_value = AMP_CH_HD, | ||
497 | }; | ||
498 | |||
499 | static snd_kcontrol_new_t snd_pmac_awacs_amp_spk_sw __initdata = { | ||
500 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
501 | .name = "PC Speaker Playback Switch", | ||
502 | .info = snd_pmac_boolean_stereo_info, | ||
503 | .get = snd_pmac_awacs_get_switch_amp, | ||
504 | .put = snd_pmac_awacs_put_switch_amp, | ||
505 | .private_value = AMP_CH_SPK, | ||
506 | }; | ||
507 | |||
508 | #endif /* PMAC_AMP_AVAIL */ | ||
509 | |||
510 | |||
511 | /* | ||
512 | * mic boost for screamer | ||
513 | */ | ||
514 | static int snd_pmac_screamer_mic_boost_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
515 | { | ||
516 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
517 | uinfo->count = 1; | ||
518 | uinfo->value.integer.min = 0; | ||
519 | uinfo->value.integer.max = 2; | ||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | static int snd_pmac_screamer_mic_boost_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
524 | { | ||
525 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
526 | int val; | ||
527 | unsigned long flags; | ||
528 | |||
529 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
530 | if (chip->awacs_reg[6] & MASK_MIC_BOOST) | ||
531 | val = 2; | ||
532 | else if (chip->awacs_reg[0] & MASK_GAINLINE) | ||
533 | val = 1; | ||
534 | else | ||
535 | val = 0; | ||
536 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
537 | ucontrol->value.integer.value[0] = val; | ||
538 | return 0; | ||
539 | } | ||
540 | |||
541 | static int snd_pmac_screamer_mic_boost_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
542 | { | ||
543 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
544 | int changed = 0; | ||
545 | int val0, val6; | ||
546 | unsigned long flags; | ||
547 | |||
548 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
549 | val0 = chip->awacs_reg[0] & ~MASK_GAINLINE; | ||
550 | val6 = chip->awacs_reg[6] & ~MASK_MIC_BOOST; | ||
551 | if (ucontrol->value.integer.value[0] > 0) { | ||
552 | val0 |= MASK_GAINLINE; | ||
553 | if (ucontrol->value.integer.value[0] > 1) | ||
554 | val6 |= MASK_MIC_BOOST; | ||
555 | } | ||
556 | if (val0 != chip->awacs_reg[0]) { | ||
557 | snd_pmac_awacs_write_reg(chip, 0, val0); | ||
558 | changed = 1; | ||
559 | } | ||
560 | if (val6 != chip->awacs_reg[6]) { | ||
561 | snd_pmac_awacs_write_reg(chip, 6, val6); | ||
562 | changed = 1; | ||
563 | } | ||
564 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
565 | return changed; | ||
566 | } | ||
567 | |||
568 | /* | ||
569 | * lists of mixer elements | ||
570 | */ | ||
571 | static snd_kcontrol_new_t snd_pmac_awacs_mixers[] __initdata = { | ||
572 | AWACS_VOLUME("Master Playback Volume", 2, 6, 1), | ||
573 | AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0), | ||
574 | AWACS_VOLUME("Capture Volume", 0, 4, 0), | ||
575 | AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0), | ||
576 | }; | ||
577 | |||
578 | /* FIXME: is this correct order? | ||
579 | * screamer (powerbook G3 pismo) seems to have different bits... | ||
580 | */ | ||
581 | static snd_kcontrol_new_t snd_pmac_awacs_mixers2[] __initdata = { | ||
582 | AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0), | ||
583 | AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0), | ||
584 | }; | ||
585 | |||
586 | static snd_kcontrol_new_t snd_pmac_screamer_mixers2[] __initdata = { | ||
587 | AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0), | ||
588 | AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0), | ||
589 | }; | ||
590 | |||
591 | static snd_kcontrol_new_t snd_pmac_awacs_master_sw __initdata = | ||
592 | AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1); | ||
593 | |||
594 | static snd_kcontrol_new_t snd_pmac_awacs_mic_boost[] __initdata = { | ||
595 | AWACS_SWITCH("Mic Boost", 0, SHIFT_GAINLINE, 0), | ||
596 | }; | ||
597 | |||
598 | static snd_kcontrol_new_t snd_pmac_screamer_mic_boost[] __initdata = { | ||
599 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
600 | .name = "Mic Boost", | ||
601 | .info = snd_pmac_screamer_mic_boost_info, | ||
602 | .get = snd_pmac_screamer_mic_boost_get, | ||
603 | .put = snd_pmac_screamer_mic_boost_put, | ||
604 | }, | ||
605 | }; | ||
606 | |||
607 | static snd_kcontrol_new_t snd_pmac_awacs_speaker_vol[] __initdata = { | ||
608 | AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1), | ||
609 | }; | ||
610 | static snd_kcontrol_new_t snd_pmac_awacs_speaker_sw __initdata = | ||
611 | AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1); | ||
612 | |||
613 | |||
614 | /* | ||
615 | * add new mixer elements to the card | ||
616 | */ | ||
617 | static int build_mixers(pmac_t *chip, int nums, snd_kcontrol_new_t *mixers) | ||
618 | { | ||
619 | int i, err; | ||
620 | |||
621 | for (i = 0; i < nums; i++) { | ||
622 | if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixers[i], chip))) < 0) | ||
623 | return err; | ||
624 | } | ||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | |||
629 | /* | ||
630 | * restore all registers | ||
631 | */ | ||
632 | static void awacs_restore_all_regs(pmac_t *chip) | ||
633 | { | ||
634 | snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]); | ||
635 | snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]); | ||
636 | snd_pmac_awacs_write_noreg(chip, 2, chip->awacs_reg[2]); | ||
637 | snd_pmac_awacs_write_noreg(chip, 4, chip->awacs_reg[4]); | ||
638 | if (chip->model == PMAC_SCREAMER) { | ||
639 | snd_pmac_awacs_write_noreg(chip, 5, chip->awacs_reg[5]); | ||
640 | snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); | ||
641 | snd_pmac_awacs_write_noreg(chip, 7, chip->awacs_reg[7]); | ||
642 | } | ||
643 | } | ||
644 | |||
645 | #ifdef CONFIG_PMAC_PBOOK | ||
646 | static void snd_pmac_awacs_suspend(pmac_t *chip) | ||
647 | { | ||
648 | snd_pmac_awacs_write_noreg(chip, 1, (chip->awacs_reg[1] | ||
649 | | MASK_AMUTE | MASK_CMUTE)); | ||
650 | } | ||
651 | |||
652 | static void snd_pmac_awacs_resume(pmac_t *chip) | ||
653 | { | ||
654 | if (machine_is_compatible("PowerBook3,1") | ||
655 | || machine_is_compatible("PowerBook3,2")) { | ||
656 | big_mdelay(100); | ||
657 | snd_pmac_awacs_write_reg(chip, 1, | ||
658 | chip->awacs_reg[1] & ~MASK_PAROUT); | ||
659 | big_mdelay(300); | ||
660 | } | ||
661 | |||
662 | awacs_restore_all_regs(chip); | ||
663 | if (chip->model == PMAC_SCREAMER) { | ||
664 | /* reset power bits in reg 6 */ | ||
665 | mdelay(5); | ||
666 | snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); | ||
667 | } | ||
668 | screamer_recalibrate(chip); | ||
669 | #ifdef PMAC_AMP_AVAIL | ||
670 | if (chip->mixer_data) { | ||
671 | awacs_amp_t *amp = chip->mixer_data; | ||
672 | awacs_amp_set_vol(amp, 0, amp->amp_vol[0][0], amp->amp_vol[0][1], 0); | ||
673 | awacs_amp_set_vol(amp, 1, amp->amp_vol[1][0], amp->amp_vol[1][1], 0); | ||
674 | awacs_amp_set_tone(amp, amp->amp_tone[0], amp->amp_tone[1]); | ||
675 | awacs_amp_set_master(amp, amp->amp_master); | ||
676 | } | ||
677 | #endif | ||
678 | } | ||
679 | #endif /* CONFIG_PMAC_PBOOK */ | ||
680 | |||
681 | #ifdef PMAC_SUPPORT_AUTOMUTE | ||
682 | /* | ||
683 | * auto-mute stuffs | ||
684 | */ | ||
685 | static int snd_pmac_awacs_detect_headphone(pmac_t *chip) | ||
686 | { | ||
687 | return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0; | ||
688 | } | ||
689 | |||
690 | #ifdef PMAC_AMP_AVAIL | ||
691 | static int toggle_amp_mute(awacs_amp_t *amp, int index, int mute) | ||
692 | { | ||
693 | int vol[2]; | ||
694 | vol[0] = amp->amp_vol[index][0] & 31; | ||
695 | vol[1] = amp->amp_vol[index][1] & 31; | ||
696 | if (mute) { | ||
697 | vol[0] |= 32; | ||
698 | vol[1] |= 32; | ||
699 | } | ||
700 | return awacs_amp_set_vol(amp, index, vol[0], vol[1], 1); | ||
701 | } | ||
702 | #endif | ||
703 | |||
704 | static void snd_pmac_awacs_update_automute(pmac_t *chip, int do_notify) | ||
705 | { | ||
706 | if (chip->auto_mute) { | ||
707 | #ifdef PMAC_AMP_AVAIL | ||
708 | if (chip->mixer_data) { | ||
709 | awacs_amp_t *amp = chip->mixer_data; | ||
710 | int changed; | ||
711 | if (snd_pmac_awacs_detect_headphone(chip)) { | ||
712 | changed = toggle_amp_mute(amp, AMP_CH_HD, 0); | ||
713 | changed |= toggle_amp_mute(amp, AMP_CH_SPK, 1); | ||
714 | } else { | ||
715 | changed = toggle_amp_mute(amp, AMP_CH_HD, 1); | ||
716 | changed |= toggle_amp_mute(amp, AMP_CH_SPK, 0); | ||
717 | } | ||
718 | if (do_notify && ! changed) | ||
719 | return; | ||
720 | } else | ||
721 | #endif | ||
722 | { | ||
723 | int reg = chip->awacs_reg[1] | (MASK_HDMUTE|MASK_SPKMUTE); | ||
724 | if (snd_pmac_awacs_detect_headphone(chip)) | ||
725 | reg &= ~MASK_HDMUTE; | ||
726 | else | ||
727 | reg &= ~MASK_SPKMUTE; | ||
728 | if (do_notify && reg == chip->awacs_reg[1]) | ||
729 | return; | ||
730 | snd_pmac_awacs_write_reg(chip, 1, reg); | ||
731 | } | ||
732 | if (do_notify) { | ||
733 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
734 | &chip->master_sw_ctl->id); | ||
735 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
736 | &chip->speaker_sw_ctl->id); | ||
737 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
738 | &chip->hp_detect_ctl->id); | ||
739 | } | ||
740 | } | ||
741 | } | ||
742 | #endif /* PMAC_SUPPORT_AUTOMUTE */ | ||
743 | |||
744 | |||
745 | /* | ||
746 | * initialize chip | ||
747 | */ | ||
748 | int __init | ||
749 | snd_pmac_awacs_init(pmac_t *chip) | ||
750 | { | ||
751 | int err, vol; | ||
752 | |||
753 | /* looks like MASK_GAINLINE triggers something, so we set here | ||
754 | * as start-up | ||
755 | */ | ||
756 | chip->awacs_reg[0] = MASK_MUX_CD | 0xff | MASK_GAINLINE; | ||
757 | chip->awacs_reg[1] = MASK_CMUTE | MASK_AMUTE; | ||
758 | /* FIXME: Only machines with external SRS module need MASK_PAROUT */ | ||
759 | if (chip->has_iic || chip->device_id == 0x5 || | ||
760 | /*chip->_device_id == 0x8 || */ | ||
761 | chip->device_id == 0xb) | ||
762 | chip->awacs_reg[1] |= MASK_PAROUT; | ||
763 | /* get default volume from nvram */ | ||
764 | // vol = (~nvram_read_byte(0x1308) & 7) << 1; | ||
765 | // vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 ); | ||
766 | vol = 0x0f; /* no, on alsa, muted as default */ | ||
767 | vol = vol + (vol << 6); | ||
768 | chip->awacs_reg[2] = vol; | ||
769 | chip->awacs_reg[4] = vol; | ||
770 | if (chip->model == PMAC_SCREAMER) { | ||
771 | chip->awacs_reg[5] = vol; /* FIXME: screamer has loopthru vol control */ | ||
772 | chip->awacs_reg[6] = MASK_MIC_BOOST; /* FIXME: maybe should be vol << 3 for PCMCIA speaker */ | ||
773 | chip->awacs_reg[7] = 0; | ||
774 | } | ||
775 | |||
776 | awacs_restore_all_regs(chip); | ||
777 | chip->manufacturer = (in_le32(&chip->awacs->codec_stat) >> 8) & 0xf; | ||
778 | screamer_recalibrate(chip); | ||
779 | |||
780 | chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf; | ||
781 | #ifdef PMAC_AMP_AVAIL | ||
782 | if (chip->revision == 3 && chip->has_iic && CHECK_CUDA_AMP()) { | ||
783 | awacs_amp_t *amp = kmalloc(sizeof(*amp), GFP_KERNEL); | ||
784 | if (! amp) | ||
785 | return -ENOMEM; | ||
786 | chip->mixer_data = amp; | ||
787 | memset(amp, 0, sizeof(*amp)); | ||
788 | chip->mixer_free = awacs_amp_free; | ||
789 | awacs_amp_set_vol(amp, 0, 63, 63, 0); /* mute and zero vol */ | ||
790 | awacs_amp_set_vol(amp, 1, 63, 63, 0); | ||
791 | awacs_amp_set_tone(amp, 7, 7); /* 0 dB */ | ||
792 | awacs_amp_set_master(amp, 79); /* 0 dB */ | ||
793 | } | ||
794 | #endif /* PMAC_AMP_AVAIL */ | ||
795 | |||
796 | if (chip->hp_stat_mask == 0) { | ||
797 | /* set headphone-jack detection bit */ | ||
798 | switch (chip->model) { | ||
799 | case PMAC_AWACS: | ||
800 | chip->hp_stat_mask = 0x04; | ||
801 | break; | ||
802 | case PMAC_SCREAMER: | ||
803 | switch (chip->device_id) { | ||
804 | case 0x08: | ||
805 | /* 1 = side jack, 2 = front jack */ | ||
806 | chip->hp_stat_mask = 0x03; | ||
807 | break; | ||
808 | case 0x00: | ||
809 | case 0x05: | ||
810 | chip->hp_stat_mask = 0x04; | ||
811 | break; | ||
812 | default: | ||
813 | chip->hp_stat_mask = 0x08; | ||
814 | break; | ||
815 | } | ||
816 | break; | ||
817 | default: | ||
818 | snd_BUG(); | ||
819 | break; | ||
820 | } | ||
821 | } | ||
822 | |||
823 | /* | ||
824 | * build mixers | ||
825 | */ | ||
826 | strcpy(chip->card->mixername, "PowerMac AWACS"); | ||
827 | |||
828 | if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers), | ||
829 | snd_pmac_awacs_mixers)) < 0) | ||
830 | return err; | ||
831 | if (chip->model == PMAC_SCREAMER) | ||
832 | err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mixers2), | ||
833 | snd_pmac_screamer_mixers2); | ||
834 | else | ||
835 | err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mixers2), | ||
836 | snd_pmac_awacs_mixers2); | ||
837 | if (err < 0) | ||
838 | return err; | ||
839 | chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_master_sw, chip); | ||
840 | if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) | ||
841 | return err; | ||
842 | #ifdef PMAC_AMP_AVAIL | ||
843 | if (chip->mixer_data) { | ||
844 | /* use amplifier. the signal is connected from route A | ||
845 | * to the amp. the amp has its headphone and speaker | ||
846 | * volumes and mute switches, so we use them instead of | ||
847 | * screamer registers. | ||
848 | * in this case, it seems the route C is not used. | ||
849 | */ | ||
850 | if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_amp_vol), | ||
851 | snd_pmac_awacs_amp_vol)) < 0) | ||
852 | return err; | ||
853 | /* overwrite */ | ||
854 | chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_hp_sw, chip); | ||
855 | if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) | ||
856 | return err; | ||
857 | chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_amp_spk_sw, chip); | ||
858 | if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) | ||
859 | return err; | ||
860 | } else | ||
861 | #endif /* PMAC_AMP_AVAIL */ | ||
862 | { | ||
863 | /* route A = headphone, route C = speaker */ | ||
864 | if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_speaker_vol), | ||
865 | snd_pmac_awacs_speaker_vol)) < 0) | ||
866 | return err; | ||
867 | chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_awacs_speaker_sw, chip); | ||
868 | if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) | ||
869 | return err; | ||
870 | } | ||
871 | |||
872 | if (chip->model == PMAC_SCREAMER) { | ||
873 | if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_screamer_mic_boost), | ||
874 | snd_pmac_screamer_mic_boost)) < 0) | ||
875 | return err; | ||
876 | } else { | ||
877 | if ((err = build_mixers(chip, ARRAY_SIZE(snd_pmac_awacs_mic_boost), | ||
878 | snd_pmac_awacs_mic_boost)) < 0) | ||
879 | return err; | ||
880 | } | ||
881 | |||
882 | /* | ||
883 | * set lowlevel callbacks | ||
884 | */ | ||
885 | chip->set_format = snd_pmac_awacs_set_format; | ||
886 | #ifdef CONFIG_PMAC_PBOOK | ||
887 | chip->suspend = snd_pmac_awacs_suspend; | ||
888 | chip->resume = snd_pmac_awacs_resume; | ||
889 | #endif | ||
890 | #ifdef PMAC_SUPPORT_AUTOMUTE | ||
891 | if ((err = snd_pmac_add_automute(chip)) < 0) | ||
892 | return err; | ||
893 | chip->detect_headphone = snd_pmac_awacs_detect_headphone; | ||
894 | chip->update_automute = snd_pmac_awacs_update_automute; | ||
895 | snd_pmac_awacs_update_automute(chip, 0); /* update the status only */ | ||
896 | #endif | ||
897 | if (chip->model == PMAC_SCREAMER) { | ||
898 | snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]); | ||
899 | snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]); | ||
900 | } | ||
901 | |||
902 | return 0; | ||
903 | } | ||
diff --git a/sound/ppc/awacs.h b/sound/ppc/awacs.h new file mode 100644 index 000000000000..1b2cc44eda57 --- /dev/null +++ b/sound/ppc/awacs.h | |||
@@ -0,0 +1,192 @@ | |||
1 | /* | ||
2 | * Driver for PowerMac AWACS onboard soundchips | ||
3 | * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de> | ||
4 | * based on dmasound.c. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | |||
22 | #ifndef __AWACS_H | ||
23 | #define __AWACS_H | ||
24 | |||
25 | /*******************************/ | ||
26 | /* AWACs Audio Register Layout */ | ||
27 | /*******************************/ | ||
28 | |||
29 | struct awacs_regs { | ||
30 | unsigned control; /* Audio control register */ | ||
31 | unsigned pad0[3]; | ||
32 | unsigned codec_ctrl; /* Codec control register */ | ||
33 | unsigned pad1[3]; | ||
34 | unsigned codec_stat; /* Codec status register */ | ||
35 | unsigned pad2[3]; | ||
36 | unsigned clip_count; /* Clipping count register */ | ||
37 | unsigned pad3[3]; | ||
38 | unsigned byteswap; /* Data is little-endian if 1 */ | ||
39 | }; | ||
40 | |||
41 | /*******************/ | ||
42 | /* Audio Bit Masks */ | ||
43 | /*******************/ | ||
44 | |||
45 | /* Audio Control Reg Bit Masks */ | ||
46 | /* ----- ------- --- --- ----- */ | ||
47 | #define MASK_ISFSEL (0xf) /* Input SubFrame Select */ | ||
48 | #define MASK_OSFSEL (0xf << 4) /* Output SubFrame Select */ | ||
49 | #define MASK_RATE (0x7 << 8) /* Sound Rate */ | ||
50 | #define MASK_CNTLERR (0x1 << 11) /* Error */ | ||
51 | #define MASK_PORTCHG (0x1 << 12) /* Port Change */ | ||
52 | #define MASK_IEE (0x1 << 13) /* Enable Interrupt on Error */ | ||
53 | #define MASK_IEPC (0x1 << 14) /* Enable Interrupt on Port Change */ | ||
54 | #define MASK_SSFSEL (0x3 << 15) /* Status SubFrame Select */ | ||
55 | |||
56 | /* Audio Codec Control Reg Bit Masks */ | ||
57 | /* ----- ----- ------- --- --- ----- */ | ||
58 | #define MASK_NEWECMD (0x1 << 24) /* Lock: don't write to reg when 1 */ | ||
59 | #define MASK_EMODESEL (0x3 << 22) /* Send info out on which frame? */ | ||
60 | #define MASK_EXMODEADDR (0x3ff << 12) /* Extended Mode Address -- 10 bits */ | ||
61 | #define MASK_EXMODEDATA (0xfff) /* Extended Mode Data -- 12 bits */ | ||
62 | |||
63 | /* Audio Codec Control Address Values / Masks */ | ||
64 | /* ----- ----- ------- ------- ------ - ----- */ | ||
65 | #define MASK_ADDR0 (0x0 << 12) /* Expanded Data Mode Address 0 */ | ||
66 | #define MASK_ADDR_MUX MASK_ADDR0 /* Mux Control */ | ||
67 | #define MASK_ADDR_GAIN MASK_ADDR0 | ||
68 | |||
69 | #define MASK_ADDR1 (0x1 << 12) /* Expanded Data Mode Address 1 */ | ||
70 | #define MASK_ADDR_MUTE MASK_ADDR1 | ||
71 | #define MASK_ADDR_RATE MASK_ADDR1 | ||
72 | |||
73 | #define MASK_ADDR2 (0x2 << 12) /* Expanded Data Mode Address 2 */ | ||
74 | #define MASK_ADDR_VOLA MASK_ADDR2 /* Volume Control A -- Headphones */ | ||
75 | #define MASK_ADDR_VOLHD MASK_ADDR2 | ||
76 | |||
77 | #define MASK_ADDR4 (0x4 << 12) /* Expanded Data Mode Address 4 */ | ||
78 | #define MASK_ADDR_VOLC MASK_ADDR4 /* Volume Control C -- Speaker */ | ||
79 | #define MASK_ADDR_VOLSPK MASK_ADDR4 | ||
80 | |||
81 | /* additional registers of screamer */ | ||
82 | #define MASK_ADDR5 (0x5 << 12) /* Expanded Data Mode Address 5 */ | ||
83 | #define MASK_ADDR6 (0x6 << 12) /* Expanded Data Mode Address 6 */ | ||
84 | #define MASK_ADDR7 (0x7 << 12) /* Expanded Data Mode Address 7 */ | ||
85 | |||
86 | /* Address 0 Bit Masks & Macros */ | ||
87 | /* ------- - --- ----- - ------ */ | ||
88 | #define MASK_GAINRIGHT (0xf) /* Gain Right Mask */ | ||
89 | #define MASK_GAINLEFT (0xf << 4) /* Gain Left Mask */ | ||
90 | #define MASK_GAINLINE (0x1 << 8) /* Disable Mic preamp */ | ||
91 | #define MASK_GAINMIC (0x0 << 8) /* Enable Mic preamp */ | ||
92 | #define MASK_MUX_CD (0x1 << 9) /* Select CD in MUX */ | ||
93 | #define MASK_MUX_MIC (0x1 << 10) /* Select Mic in MUX */ | ||
94 | #define MASK_MUX_AUDIN (0x1 << 11) /* Select Audio In in MUX */ | ||
95 | #define MASK_MUX_LINE MASK_MUX_AUDIN | ||
96 | #define SHIFT_GAINLINE 8 | ||
97 | #define SHIFT_MUX_CD 9 | ||
98 | #define SHIFT_MUX_MIC 10 | ||
99 | #define SHIFT_MUX_LINE 11 | ||
100 | |||
101 | #define GAINRIGHT(x) ((x) & MASK_GAINRIGHT) | ||
102 | #define GAINLEFT(x) (((x) << 4) & MASK_GAINLEFT) | ||
103 | |||
104 | /* Address 1 Bit Masks */ | ||
105 | /* ------- - --- ----- */ | ||
106 | #define MASK_ADDR1RES1 (0x3) /* Reserved */ | ||
107 | #define MASK_RECALIBRATE (0x1 << 2) /* Recalibrate */ | ||
108 | #define MASK_SAMPLERATE (0x7 << 3) /* Sample Rate: */ | ||
109 | #define MASK_LOOPTHRU (0x1 << 6) /* Loopthrough Enable */ | ||
110 | #define SHIFT_LOOPTHRU 6 | ||
111 | #define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */ | ||
112 | #define MASK_SPKMUTE MASK_CMUTE | ||
113 | #define SHIFT_SPKMUTE 7 | ||
114 | #define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */ | ||
115 | #define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */ | ||
116 | #define MASK_HDMUTE MASK_AMUTE | ||
117 | #define SHIFT_HDMUTE 9 | ||
118 | #define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */ | ||
119 | |||
120 | #define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */ | ||
121 | #define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */ | ||
122 | #define SAMPLERATE_24000 (0x2 << 3) /* 24 or 22.05 kHz */ | ||
123 | #define SAMPLERATE_19200 (0x3 << 3) /* 19.2 or 17.64 kHz */ | ||
124 | #define SAMPLERATE_16000 (0x4 << 3) /* 16 or 14.7 kHz */ | ||
125 | #define SAMPLERATE_12000 (0x5 << 3) /* 12 or 11.025 kHz */ | ||
126 | #define SAMPLERATE_9600 (0x6 << 3) /* 9.6 or 8.82 kHz */ | ||
127 | #define SAMPLERATE_8000 (0x7 << 3) /* 8 or 7.35 kHz */ | ||
128 | |||
129 | /* Address 2 & 4 Bit Masks & Macros */ | ||
130 | /* ------- - - - --- ----- - ------ */ | ||
131 | #define MASK_OUTVOLRIGHT (0xf) /* Output Right Volume */ | ||
132 | #define MASK_ADDR2RES1 (0x2 << 4) /* Reserved */ | ||
133 | #define MASK_ADDR4RES1 MASK_ADDR2RES1 | ||
134 | #define MASK_OUTVOLLEFT (0xf << 6) /* Output Left Volume */ | ||
135 | #define MASK_ADDR2RES2 (0x2 << 10) /* Reserved */ | ||
136 | #define MASK_ADDR4RES2 MASK_ADDR2RES2 | ||
137 | |||
138 | #define VOLRIGHT(x) (((~(x)) & MASK_OUTVOLRIGHT)) | ||
139 | #define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT) | ||
140 | |||
141 | /* address 6 */ | ||
142 | #define MASK_MIC_BOOST (0x4) /* screamer mic boost */ | ||
143 | #define SHIFT_MIC_BOOST 2 | ||
144 | |||
145 | /* Audio Codec Status Reg Bit Masks */ | ||
146 | /* ----- ----- ------ --- --- ----- */ | ||
147 | #define MASK_EXTEND (0x1 << 23) /* Extend */ | ||
148 | #define MASK_VALID (0x1 << 22) /* Valid Data? */ | ||
149 | #define MASK_OFLEFT (0x1 << 21) /* Overflow Left */ | ||
150 | #define MASK_OFRIGHT (0x1 << 20) /* Overflow Right */ | ||
151 | #define MASK_ERRCODE (0xf << 16) /* Error Code */ | ||
152 | #define MASK_REVISION (0xf << 12) /* Revision Number */ | ||
153 | #define MASK_MFGID (0xf << 8) /* Mfg. ID */ | ||
154 | #define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */ | ||
155 | #define MASK_INPPORT (0xf) /* Input Port */ | ||
156 | #define MASK_HDPCONN 8 /* headphone plugged in */ | ||
157 | |||
158 | /* Clipping Count Reg Bit Masks */ | ||
159 | /* -------- ----- --- --- ----- */ | ||
160 | #define MASK_CLIPLEFT (0xff << 7) /* Clipping Count, Left Channel */ | ||
161 | #define MASK_CLIPRIGHT (0xff) /* Clipping Count, Right Channel */ | ||
162 | |||
163 | /* DBDMA ChannelStatus Bit Masks */ | ||
164 | /* ----- ------------- --- ----- */ | ||
165 | #define MASK_CSERR (0x1 << 7) /* Error */ | ||
166 | #define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */ | ||
167 | #define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */ | ||
168 | #define MASK_WAIT (0x1) /* Wait */ | ||
169 | |||
170 | /* Various Rates */ | ||
171 | /* ------- ----- */ | ||
172 | #define RATE_48000 (0x0 << 8) /* 48 kHz */ | ||
173 | #define RATE_44100 (0x0 << 8) /* 44.1 kHz */ | ||
174 | #define RATE_32000 (0x1 << 8) /* 32 kHz */ | ||
175 | #define RATE_29400 (0x1 << 8) /* 29.4 kHz */ | ||
176 | #define RATE_24000 (0x2 << 8) /* 24 kHz */ | ||
177 | #define RATE_22050 (0x2 << 8) /* 22.05 kHz */ | ||
178 | #define RATE_19200 (0x3 << 8) /* 19.2 kHz */ | ||
179 | #define RATE_17640 (0x3 << 8) /* 17.64 kHz */ | ||
180 | #define RATE_16000 (0x4 << 8) /* 16 kHz */ | ||
181 | #define RATE_14700 (0x4 << 8) /* 14.7 kHz */ | ||
182 | #define RATE_12000 (0x5 << 8) /* 12 kHz */ | ||
183 | #define RATE_11025 (0x5 << 8) /* 11.025 kHz */ | ||
184 | #define RATE_9600 (0x6 << 8) /* 9.6 kHz */ | ||
185 | #define RATE_8820 (0x6 << 8) /* 8.82 kHz */ | ||
186 | #define RATE_8000 (0x7 << 8) /* 8 kHz */ | ||
187 | #define RATE_7350 (0x7 << 8) /* 7.35 kHz */ | ||
188 | |||
189 | #define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */ | ||
190 | |||
191 | |||
192 | #endif /* __AWACS_H */ | ||
diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c new file mode 100644 index 000000000000..c23f601a37f9 --- /dev/null +++ b/sound/ppc/beep.c | |||
@@ -0,0 +1,262 @@ | |||
1 | /* | ||
2 | * Beep using pcm | ||
3 | * | ||
4 | * Copyright (c) by Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/irq.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/input.h> | ||
27 | #include <sound/core.h> | ||
28 | #include <sound/control.h> | ||
29 | #include "pmac.h" | ||
30 | |||
31 | struct snd_pmac_beep { | ||
32 | int running; /* boolean */ | ||
33 | int volume; /* mixer volume: 0-100 */ | ||
34 | int volume_play; /* currently playing volume */ | ||
35 | int hz; | ||
36 | int nsamples; | ||
37 | short *buf; /* allocated wave buffer */ | ||
38 | unsigned long addr; /* physical address of buffer */ | ||
39 | struct input_dev dev; | ||
40 | }; | ||
41 | |||
42 | /* | ||
43 | * stop beep if running | ||
44 | */ | ||
45 | void snd_pmac_beep_stop(pmac_t *chip) | ||
46 | { | ||
47 | pmac_beep_t *beep = chip->beep; | ||
48 | if (beep && beep->running) { | ||
49 | beep->running = 0; | ||
50 | snd_pmac_beep_dma_stop(chip); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * Stuff for outputting a beep. The values range from -327 to +327 | ||
56 | * so we can multiply by an amplitude in the range 0..100 to get a | ||
57 | * signed short value to put in the output buffer. | ||
58 | */ | ||
59 | static short beep_wform[256] = { | ||
60 | 0, 40, 79, 117, 153, 187, 218, 245, | ||
61 | 269, 288, 304, 316, 323, 327, 327, 324, | ||
62 | 318, 310, 299, 288, 275, 262, 249, 236, | ||
63 | 224, 213, 204, 196, 190, 186, 183, 182, | ||
64 | 182, 183, 186, 189, 192, 196, 200, 203, | ||
65 | 206, 208, 209, 209, 209, 207, 204, 201, | ||
66 | 197, 193, 188, 183, 179, 174, 170, 166, | ||
67 | 163, 161, 160, 159, 159, 160, 161, 162, | ||
68 | 164, 166, 168, 169, 171, 171, 171, 170, | ||
69 | 169, 167, 163, 159, 155, 150, 144, 139, | ||
70 | 133, 128, 122, 117, 113, 110, 107, 105, | ||
71 | 103, 103, 103, 103, 104, 104, 105, 105, | ||
72 | 105, 103, 101, 97, 92, 86, 78, 68, | ||
73 | 58, 45, 32, 18, 3, -11, -26, -41, | ||
74 | -55, -68, -79, -88, -95, -100, -102, -102, | ||
75 | -99, -93, -85, -75, -62, -48, -33, -16, | ||
76 | 0, 16, 33, 48, 62, 75, 85, 93, | ||
77 | 99, 102, 102, 100, 95, 88, 79, 68, | ||
78 | 55, 41, 26, 11, -3, -18, -32, -45, | ||
79 | -58, -68, -78, -86, -92, -97, -101, -103, | ||
80 | -105, -105, -105, -104, -104, -103, -103, -103, | ||
81 | -103, -105, -107, -110, -113, -117, -122, -128, | ||
82 | -133, -139, -144, -150, -155, -159, -163, -167, | ||
83 | -169, -170, -171, -171, -171, -169, -168, -166, | ||
84 | -164, -162, -161, -160, -159, -159, -160, -161, | ||
85 | -163, -166, -170, -174, -179, -183, -188, -193, | ||
86 | -197, -201, -204, -207, -209, -209, -209, -208, | ||
87 | -206, -203, -200, -196, -192, -189, -186, -183, | ||
88 | -182, -182, -183, -186, -190, -196, -204, -213, | ||
89 | -224, -236, -249, -262, -275, -288, -299, -310, | ||
90 | -318, -324, -327, -327, -323, -316, -304, -288, | ||
91 | -269, -245, -218, -187, -153, -117, -79, -40, | ||
92 | }; | ||
93 | |||
94 | #define BEEP_SRATE 22050 /* 22050 Hz sample rate */ | ||
95 | #define BEEP_BUFLEN 512 | ||
96 | #define BEEP_VOLUME 15 /* 0 - 100 */ | ||
97 | |||
98 | static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type, unsigned int code, int hz) | ||
99 | { | ||
100 | pmac_t *chip; | ||
101 | pmac_beep_t *beep; | ||
102 | unsigned long flags; | ||
103 | int beep_speed = 0; | ||
104 | int srate; | ||
105 | int period, ncycles, nsamples; | ||
106 | int i, j, f; | ||
107 | short *p; | ||
108 | |||
109 | if (type != EV_SND) | ||
110 | return -1; | ||
111 | |||
112 | switch (code) { | ||
113 | case SND_BELL: if (hz) hz = 1000; | ||
114 | case SND_TONE: break; | ||
115 | default: return -1; | ||
116 | } | ||
117 | |||
118 | chip = dev->private; | ||
119 | if (! chip || (beep = chip->beep) == NULL) | ||
120 | return -1; | ||
121 | |||
122 | if (! hz) { | ||
123 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
124 | if (beep->running) | ||
125 | snd_pmac_beep_stop(chip); | ||
126 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | beep_speed = snd_pmac_rate_index(chip, &chip->playback, BEEP_SRATE); | ||
131 | srate = chip->freq_table[beep_speed]; | ||
132 | |||
133 | if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) | ||
134 | hz = 1000; | ||
135 | |||
136 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
137 | if (chip->playback.running || chip->capture.running || beep->running) { | ||
138 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
139 | return 0; | ||
140 | } | ||
141 | beep->running = 1; | ||
142 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
143 | |||
144 | if (hz == beep->hz && beep->volume == beep->volume_play) { | ||
145 | nsamples = beep->nsamples; | ||
146 | } else { | ||
147 | period = srate * 256 / hz; /* fixed point */ | ||
148 | ncycles = BEEP_BUFLEN * 256 / period; | ||
149 | nsamples = (period * ncycles) >> 8; | ||
150 | f = ncycles * 65536 / nsamples; | ||
151 | j = 0; | ||
152 | p = beep->buf; | ||
153 | for (i = 0; i < nsamples; ++i, p += 2) { | ||
154 | p[0] = p[1] = beep_wform[j >> 8] * beep->volume; | ||
155 | j = (j + f) & 0xffff; | ||
156 | } | ||
157 | beep->hz = hz; | ||
158 | beep->volume_play = beep->volume; | ||
159 | beep->nsamples = nsamples; | ||
160 | } | ||
161 | |||
162 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
163 | snd_pmac_beep_dma_start(chip, beep->nsamples * 4, beep->addr, beep_speed); | ||
164 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | /* | ||
169 | * beep volume mixer | ||
170 | */ | ||
171 | |||
172 | #define chip_t pmac_t | ||
173 | |||
174 | static int snd_pmac_info_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
175 | { | ||
176 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
177 | uinfo->count = 1; | ||
178 | uinfo->value.integer.min = 0; | ||
179 | uinfo->value.integer.max = 100; | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static int snd_pmac_get_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
184 | { | ||
185 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
186 | snd_assert(chip->beep, return -ENXIO); | ||
187 | ucontrol->value.integer.value[0] = chip->beep->volume; | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static int snd_pmac_put_beep(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
192 | { | ||
193 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
194 | int oval; | ||
195 | snd_assert(chip->beep, return -ENXIO); | ||
196 | oval = chip->beep->volume; | ||
197 | chip->beep->volume = ucontrol->value.integer.value[0]; | ||
198 | return oval != chip->beep->volume; | ||
199 | } | ||
200 | |||
201 | static snd_kcontrol_new_t snd_pmac_beep_mixer = { | ||
202 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
203 | .name = "Beep Playback Volume", | ||
204 | .info = snd_pmac_info_beep, | ||
205 | .get = snd_pmac_get_beep, | ||
206 | .put = snd_pmac_put_beep, | ||
207 | }; | ||
208 | |||
209 | /* Initialize beep stuff */ | ||
210 | int __init snd_pmac_attach_beep(pmac_t *chip) | ||
211 | { | ||
212 | pmac_beep_t *beep; | ||
213 | int err; | ||
214 | |||
215 | beep = kmalloc(sizeof(*beep), GFP_KERNEL); | ||
216 | if (! beep) | ||
217 | return -ENOMEM; | ||
218 | |||
219 | memset(beep, 0, sizeof(*beep)); | ||
220 | beep->buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); | ||
221 | if (! beep->buf) { | ||
222 | kfree(beep); | ||
223 | return -ENOMEM; | ||
224 | } | ||
225 | beep->addr = virt_to_bus(beep->buf); | ||
226 | |||
227 | beep->dev.evbit[0] = BIT(EV_SND); | ||
228 | beep->dev.sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); | ||
229 | beep->dev.event = snd_pmac_beep_event; | ||
230 | beep->dev.private = chip; | ||
231 | |||
232 | /* FIXME: set more better values */ | ||
233 | beep->dev.name = "PowerMac Beep"; | ||
234 | beep->dev.phys = "powermac/beep"; | ||
235 | beep->dev.id.bustype = BUS_ADB; | ||
236 | beep->dev.id.vendor = 0x001f; | ||
237 | beep->dev.id.product = 0x0001; | ||
238 | beep->dev.id.version = 0x0100; | ||
239 | |||
240 | beep->volume = BEEP_VOLUME; | ||
241 | beep->running = 0; | ||
242 | if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_beep_mixer, chip))) < 0) { | ||
243 | kfree(beep->buf); | ||
244 | kfree(beep); | ||
245 | return err; | ||
246 | } | ||
247 | |||
248 | chip->beep = beep; | ||
249 | input_register_device(&beep->dev); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | void snd_pmac_detach_beep(pmac_t *chip) | ||
255 | { | ||
256 | if (chip->beep) { | ||
257 | input_unregister_device(&chip->beep->dev); | ||
258 | kfree(chip->beep->buf); | ||
259 | kfree(chip->beep); | ||
260 | chip->beep = NULL; | ||
261 | } | ||
262 | } | ||
diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c new file mode 100644 index 000000000000..3f837d9f3eb1 --- /dev/null +++ b/sound/ppc/burgundy.c | |||
@@ -0,0 +1,439 @@ | |||
1 | /* | ||
2 | * PMac Burgundy 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 | #include <sound/driver.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <sound/core.h> | ||
28 | #include "pmac.h" | ||
29 | #include "burgundy.h" | ||
30 | |||
31 | |||
32 | /* Waits for busy flag to clear */ | ||
33 | inline static void | ||
34 | snd_pmac_burgundy_busy_wait(pmac_t *chip) | ||
35 | { | ||
36 | int timeout = 50; | ||
37 | while ((in_le32(&chip->awacs->codec_ctrl) & MASK_NEWECMD) && timeout--) | ||
38 | udelay(1); | ||
39 | if (! timeout) | ||
40 | printk(KERN_DEBUG "burgundy_busy_wait: timeout\n"); | ||
41 | } | ||
42 | |||
43 | inline static void | ||
44 | snd_pmac_burgundy_extend_wait(pmac_t *chip) | ||
45 | { | ||
46 | int timeout; | ||
47 | timeout = 50; | ||
48 | while (!(in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--) | ||
49 | udelay(1); | ||
50 | if (! timeout) | ||
51 | printk(KERN_DEBUG "burgundy_extend_wait: timeout #1\n"); | ||
52 | timeout = 50; | ||
53 | while ((in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--) | ||
54 | udelay(1); | ||
55 | if (! timeout) | ||
56 | printk(KERN_DEBUG "burgundy_extend_wait: timeout #2\n"); | ||
57 | } | ||
58 | |||
59 | static void | ||
60 | snd_pmac_burgundy_wcw(pmac_t *chip, unsigned addr, unsigned val) | ||
61 | { | ||
62 | out_le32(&chip->awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff)); | ||
63 | snd_pmac_burgundy_busy_wait(chip); | ||
64 | out_le32(&chip->awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff)); | ||
65 | snd_pmac_burgundy_busy_wait(chip); | ||
66 | out_le32(&chip->awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff)); | ||
67 | snd_pmac_burgundy_busy_wait(chip); | ||
68 | out_le32(&chip->awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff)); | ||
69 | snd_pmac_burgundy_busy_wait(chip); | ||
70 | } | ||
71 | |||
72 | static unsigned | ||
73 | snd_pmac_burgundy_rcw(pmac_t *chip, unsigned addr) | ||
74 | { | ||
75 | unsigned val = 0; | ||
76 | unsigned long flags; | ||
77 | |||
78 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
79 | |||
80 | out_le32(&chip->awacs->codec_ctrl, addr + 0x100000); | ||
81 | snd_pmac_burgundy_busy_wait(chip); | ||
82 | snd_pmac_burgundy_extend_wait(chip); | ||
83 | val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff; | ||
84 | |||
85 | out_le32(&chip->awacs->codec_ctrl, addr + 0x100100); | ||
86 | snd_pmac_burgundy_busy_wait(chip); | ||
87 | snd_pmac_burgundy_extend_wait(chip); | ||
88 | val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<8; | ||
89 | |||
90 | out_le32(&chip->awacs->codec_ctrl, addr + 0x100200); | ||
91 | snd_pmac_burgundy_busy_wait(chip); | ||
92 | snd_pmac_burgundy_extend_wait(chip); | ||
93 | val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<16; | ||
94 | |||
95 | out_le32(&chip->awacs->codec_ctrl, addr + 0x100300); | ||
96 | snd_pmac_burgundy_busy_wait(chip); | ||
97 | snd_pmac_burgundy_extend_wait(chip); | ||
98 | val += ((in_le32(&chip->awacs->codec_stat)>>4) & 0xff) <<24; | ||
99 | |||
100 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
101 | |||
102 | return val; | ||
103 | } | ||
104 | |||
105 | static void | ||
106 | snd_pmac_burgundy_wcb(pmac_t *chip, unsigned int addr, unsigned int val) | ||
107 | { | ||
108 | out_le32(&chip->awacs->codec_ctrl, addr + 0x300000 + (val & 0xff)); | ||
109 | snd_pmac_burgundy_busy_wait(chip); | ||
110 | } | ||
111 | |||
112 | static unsigned | ||
113 | snd_pmac_burgundy_rcb(pmac_t *chip, unsigned int addr) | ||
114 | { | ||
115 | unsigned val = 0; | ||
116 | unsigned long flags; | ||
117 | |||
118 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
119 | |||
120 | out_le32(&chip->awacs->codec_ctrl, addr + 0x100000); | ||
121 | snd_pmac_burgundy_busy_wait(chip); | ||
122 | snd_pmac_burgundy_extend_wait(chip); | ||
123 | val += (in_le32(&chip->awacs->codec_stat) >> 4) & 0xff; | ||
124 | |||
125 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
126 | |||
127 | return val; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * Burgundy volume: 0 - 100, stereo | ||
132 | */ | ||
133 | static void | ||
134 | snd_pmac_burgundy_write_volume(pmac_t *chip, unsigned int address, long *volume, int shift) | ||
135 | { | ||
136 | int hardvolume, lvolume, rvolume; | ||
137 | |||
138 | lvolume = volume[0] ? volume[0] + BURGUNDY_VOLUME_OFFSET : 0; | ||
139 | rvolume = volume[1] ? volume[1] + BURGUNDY_VOLUME_OFFSET : 0; | ||
140 | |||
141 | hardvolume = lvolume + (rvolume << shift); | ||
142 | if (shift == 8) | ||
143 | hardvolume |= hardvolume << 16; | ||
144 | |||
145 | snd_pmac_burgundy_wcw(chip, address, hardvolume); | ||
146 | } | ||
147 | |||
148 | static void | ||
149 | snd_pmac_burgundy_read_volume(pmac_t *chip, unsigned int address, long *volume, int shift) | ||
150 | { | ||
151 | int wvolume; | ||
152 | |||
153 | wvolume = snd_pmac_burgundy_rcw(chip, address); | ||
154 | |||
155 | volume[0] = wvolume & 0xff; | ||
156 | if (volume[0] >= BURGUNDY_VOLUME_OFFSET) | ||
157 | volume[0] -= BURGUNDY_VOLUME_OFFSET; | ||
158 | else | ||
159 | volume[0] = 0; | ||
160 | volume[1] = (wvolume >> shift) & 0xff; | ||
161 | if (volume[1] >= BURGUNDY_VOLUME_OFFSET) | ||
162 | volume[1] -= BURGUNDY_VOLUME_OFFSET; | ||
163 | else | ||
164 | volume[1] = 0; | ||
165 | } | ||
166 | |||
167 | |||
168 | /* | ||
169 | */ | ||
170 | |||
171 | #define BASE2ADDR(base) ((base) << 12) | ||
172 | #define ADDR2BASE(addr) ((addr) >> 12) | ||
173 | |||
174 | static int snd_pmac_burgundy_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
175 | { | ||
176 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
177 | uinfo->count = 2; | ||
178 | uinfo->value.integer.min = 0; | ||
179 | uinfo->value.integer.max = 100; | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static int snd_pmac_burgundy_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
184 | { | ||
185 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
186 | unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); | ||
187 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
188 | snd_pmac_burgundy_read_volume(chip, addr, ucontrol->value.integer.value, shift); | ||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | static int snd_pmac_burgundy_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
193 | { | ||
194 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
195 | unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); | ||
196 | int shift = (kcontrol->private_value >> 8) & 0xff; | ||
197 | long nvoices[2]; | ||
198 | |||
199 | snd_pmac_burgundy_write_volume(chip, addr, ucontrol->value.integer.value, shift); | ||
200 | snd_pmac_burgundy_read_volume(chip, addr, nvoices, shift); | ||
201 | return (nvoices[0] != ucontrol->value.integer.value[0] || | ||
202 | nvoices[1] != ucontrol->value.integer.value[1]); | ||
203 | } | ||
204 | |||
205 | #define BURGUNDY_VOLUME(xname, xindex, addr, shift) \ | ||
206 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ | ||
207 | .info = snd_pmac_burgundy_info_volume,\ | ||
208 | .get = snd_pmac_burgundy_get_volume,\ | ||
209 | .put = snd_pmac_burgundy_put_volume,\ | ||
210 | .private_value = ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) } | ||
211 | |||
212 | /* lineout/speaker */ | ||
213 | |||
214 | static int snd_pmac_burgundy_info_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
215 | { | ||
216 | int stereo = (kcontrol->private_value >> 24) & 1; | ||
217 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
218 | uinfo->count = stereo + 1; | ||
219 | uinfo->value.integer.min = 0; | ||
220 | uinfo->value.integer.max = 1; | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int snd_pmac_burgundy_get_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
225 | { | ||
226 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
227 | int lmask = kcontrol->private_value & 0xff; | ||
228 | int rmask = (kcontrol->private_value >> 8) & 0xff; | ||
229 | int stereo = (kcontrol->private_value >> 24) & 1; | ||
230 | int val = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); | ||
231 | ucontrol->value.integer.value[0] = (val & lmask) ? 1 : 0; | ||
232 | if (stereo) | ||
233 | ucontrol->value.integer.value[1] = (val & rmask) ? 1 : 0; | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static int snd_pmac_burgundy_put_switch_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
238 | { | ||
239 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
240 | int lmask = kcontrol->private_value & 0xff; | ||
241 | int rmask = (kcontrol->private_value >> 8) & 0xff; | ||
242 | int stereo = (kcontrol->private_value >> 24) & 1; | ||
243 | int val, oval; | ||
244 | oval = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); | ||
245 | val = oval & ~(lmask | rmask); | ||
246 | if (ucontrol->value.integer.value[0]) | ||
247 | val |= lmask; | ||
248 | if (stereo && ucontrol->value.integer.value[1]) | ||
249 | val |= rmask; | ||
250 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, val); | ||
251 | return val != oval; | ||
252 | } | ||
253 | |||
254 | #define BURGUNDY_OUTPUT_SWITCH(xname, xindex, lmask, rmask, stereo) \ | ||
255 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ | ||
256 | .info = snd_pmac_burgundy_info_switch_out,\ | ||
257 | .get = snd_pmac_burgundy_get_switch_out,\ | ||
258 | .put = snd_pmac_burgundy_put_switch_out,\ | ||
259 | .private_value = ((lmask) | ((rmask) << 8) | ((stereo) << 24)) } | ||
260 | |||
261 | /* line/speaker output volume */ | ||
262 | static int snd_pmac_burgundy_info_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
263 | { | ||
264 | int stereo = (kcontrol->private_value >> 24) & 1; | ||
265 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
266 | uinfo->count = stereo + 1; | ||
267 | uinfo->value.integer.min = 0; | ||
268 | uinfo->value.integer.max = 15; | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int snd_pmac_burgundy_get_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
273 | { | ||
274 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
275 | unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); | ||
276 | int stereo = (kcontrol->private_value >> 24) & 1; | ||
277 | int oval; | ||
278 | |||
279 | oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; | ||
280 | ucontrol->value.integer.value[0] = oval & 0xf; | ||
281 | if (stereo) | ||
282 | ucontrol->value.integer.value[1] = (oval >> 4) & 0xf; | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | static int snd_pmac_burgundy_put_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
287 | { | ||
288 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
289 | unsigned int addr = BASE2ADDR(kcontrol->private_value & 0xff); | ||
290 | int stereo = (kcontrol->private_value >> 24) & 1; | ||
291 | int oval, val; | ||
292 | |||
293 | oval = ~snd_pmac_burgundy_rcb(chip, addr) & 0xff; | ||
294 | val = ucontrol->value.integer.value[0]; | ||
295 | if (stereo) | ||
296 | val |= ucontrol->value.integer.value[1] << 4; | ||
297 | else | ||
298 | val |= ucontrol->value.integer.value[0] << 4; | ||
299 | val = ~val & 0xff; | ||
300 | snd_pmac_burgundy_wcb(chip, addr, val); | ||
301 | return val != oval; | ||
302 | } | ||
303 | |||
304 | #define BURGUNDY_OUTPUT_VOLUME(xname, xindex, addr, stereo) \ | ||
305 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\ | ||
306 | .info = snd_pmac_burgundy_info_volume_out,\ | ||
307 | .get = snd_pmac_burgundy_get_volume_out,\ | ||
308 | .put = snd_pmac_burgundy_put_volume_out,\ | ||
309 | .private_value = (ADDR2BASE(addr) | ((stereo) << 24)) } | ||
310 | |||
311 | static snd_kcontrol_new_t snd_pmac_burgundy_mixers[] __initdata = { | ||
312 | BURGUNDY_VOLUME("Master Playback Volume", 0, MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8), | ||
313 | BURGUNDY_VOLUME("Line Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLLINE, 16), | ||
314 | BURGUNDY_VOLUME("CD Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLCD, 16), | ||
315 | BURGUNDY_VOLUME("Mic Playback Volume", 0, MASK_ADDR_BURGUNDY_VOLMIC, 16), | ||
316 | BURGUNDY_OUTPUT_VOLUME("PC Speaker Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENHP, 0), | ||
317 | /*BURGUNDY_OUTPUT_VOLUME("PCM Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENLINEOUT, 1),*/ | ||
318 | BURGUNDY_OUTPUT_VOLUME("Headphone Playback Volume", 0, MASK_ADDR_BURGUNDY_ATTENSPEAKER, 1), | ||
319 | }; | ||
320 | static snd_kcontrol_new_t snd_pmac_burgundy_master_sw __initdata = | ||
321 | BURGUNDY_OUTPUT_SWITCH("Headphone Playback Switch", 0, BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1); | ||
322 | static snd_kcontrol_new_t snd_pmac_burgundy_speaker_sw __initdata = | ||
323 | BURGUNDY_OUTPUT_SWITCH("PC Speaker Playback Switch", 0, BURGUNDY_OUTPUT_INTERN, 0, 0); | ||
324 | |||
325 | |||
326 | #ifdef PMAC_SUPPORT_AUTOMUTE | ||
327 | /* | ||
328 | * auto-mute stuffs | ||
329 | */ | ||
330 | static int snd_pmac_burgundy_detect_headphone(pmac_t *chip) | ||
331 | { | ||
332 | return (in_le32(&chip->awacs->codec_stat) & chip->hp_stat_mask) ? 1 : 0; | ||
333 | } | ||
334 | |||
335 | static void snd_pmac_burgundy_update_automute(pmac_t *chip, int do_notify) | ||
336 | { | ||
337 | if (chip->auto_mute) { | ||
338 | int reg, oreg; | ||
339 | reg = oreg = snd_pmac_burgundy_rcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES); | ||
340 | reg &= ~(BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT | BURGUNDY_OUTPUT_INTERN); | ||
341 | if (snd_pmac_burgundy_detect_headphone(chip)) | ||
342 | reg |= BURGUNDY_OUTPUT_LEFT | BURGUNDY_OUTPUT_RIGHT; | ||
343 | else | ||
344 | reg |= BURGUNDY_OUTPUT_INTERN; | ||
345 | if (do_notify && reg == oreg) | ||
346 | return; | ||
347 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, reg); | ||
348 | if (do_notify) { | ||
349 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
350 | &chip->master_sw_ctl->id); | ||
351 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
352 | &chip->speaker_sw_ctl->id); | ||
353 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
354 | &chip->hp_detect_ctl->id); | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | #endif /* PMAC_SUPPORT_AUTOMUTE */ | ||
359 | |||
360 | |||
361 | /* | ||
362 | * initialize burgundy | ||
363 | */ | ||
364 | int __init snd_pmac_burgundy_init(pmac_t *chip) | ||
365 | { | ||
366 | int i, err; | ||
367 | |||
368 | /* Checks to see the chip is alive and kicking */ | ||
369 | if ((in_le32(&chip->awacs->codec_ctrl) & MASK_ERRCODE) == 0xf0000) { | ||
370 | printk(KERN_WARNING "pmac burgundy: disabled by MacOS :-(\n"); | ||
371 | return 1; | ||
372 | } | ||
373 | |||
374 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_OUTPUTENABLES, | ||
375 | DEF_BURGUNDY_OUTPUTENABLES); | ||
376 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, | ||
377 | DEF_BURGUNDY_MORE_OUTPUTENABLES); | ||
378 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_OUTPUTSELECTS, | ||
379 | DEF_BURGUNDY_OUTPUTSELECTS); | ||
380 | |||
381 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL21, | ||
382 | DEF_BURGUNDY_INPSEL21); | ||
383 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_INPSEL3, | ||
384 | DEF_BURGUNDY_INPSEL3); | ||
385 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINCD, | ||
386 | DEF_BURGUNDY_GAINCD); | ||
387 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINLINE, | ||
388 | DEF_BURGUNDY_GAINLINE); | ||
389 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMIC, | ||
390 | DEF_BURGUNDY_GAINMIC); | ||
391 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_GAINMODEM, | ||
392 | DEF_BURGUNDY_GAINMODEM); | ||
393 | |||
394 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENSPEAKER, | ||
395 | DEF_BURGUNDY_ATTENSPEAKER); | ||
396 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENLINEOUT, | ||
397 | DEF_BURGUNDY_ATTENLINEOUT); | ||
398 | snd_pmac_burgundy_wcb(chip, MASK_ADDR_BURGUNDY_ATTENHP, | ||
399 | DEF_BURGUNDY_ATTENHP); | ||
400 | |||
401 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_MASTER_VOLUME, | ||
402 | DEF_BURGUNDY_MASTER_VOLUME); | ||
403 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLCD, | ||
404 | DEF_BURGUNDY_VOLCD); | ||
405 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLLINE, | ||
406 | DEF_BURGUNDY_VOLLINE); | ||
407 | snd_pmac_burgundy_wcw(chip, MASK_ADDR_BURGUNDY_VOLMIC, | ||
408 | DEF_BURGUNDY_VOLMIC); | ||
409 | |||
410 | if (chip->hp_stat_mask == 0) | ||
411 | /* set headphone-jack detection bit */ | ||
412 | chip->hp_stat_mask = 0x04; | ||
413 | |||
414 | /* | ||
415 | * build burgundy mixers | ||
416 | */ | ||
417 | strcpy(chip->card->mixername, "PowerMac Burgundy"); | ||
418 | |||
419 | for (i = 0; i < ARRAY_SIZE(snd_pmac_burgundy_mixers); i++) { | ||
420 | if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_pmac_burgundy_mixers[i], chip))) < 0) | ||
421 | return err; | ||
422 | } | ||
423 | chip->master_sw_ctl = snd_ctl_new1(&snd_pmac_burgundy_master_sw, chip); | ||
424 | if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) | ||
425 | return err; | ||
426 | chip->speaker_sw_ctl = snd_ctl_new1(&snd_pmac_burgundy_speaker_sw, chip); | ||
427 | if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) | ||
428 | return err; | ||
429 | #ifdef PMAC_SUPPORT_AUTOMUTE | ||
430 | if ((err = snd_pmac_add_automute(chip)) < 0) | ||
431 | return err; | ||
432 | |||
433 | chip->detect_headphone = snd_pmac_burgundy_detect_headphone; | ||
434 | chip->update_automute = snd_pmac_burgundy_update_automute; | ||
435 | snd_pmac_burgundy_update_automute(chip, 0); /* update the status only */ | ||
436 | #endif | ||
437 | |||
438 | return 0; | ||
439 | } | ||
diff --git a/sound/ppc/burgundy.h b/sound/ppc/burgundy.h new file mode 100644 index 000000000000..ebb457a8342c --- /dev/null +++ b/sound/ppc/burgundy.h | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * Driver for PowerMac Burgundy onboard soundchips | ||
3 | * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de> | ||
4 | * based on dmasound.c. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | |||
22 | #ifndef __BURGUNDY_H | ||
23 | #define __BURGUNDY_H | ||
24 | |||
25 | #define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12) | ||
26 | #define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12) | ||
27 | |||
28 | #define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12) | ||
29 | #define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12) | ||
30 | #define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12) | ||
31 | #define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12) | ||
32 | |||
33 | #define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12) | ||
34 | #define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12) | ||
35 | #define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12) | ||
36 | #define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12) | ||
37 | |||
38 | #define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12) | ||
39 | #define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12) | ||
40 | |||
41 | #define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12) | ||
42 | |||
43 | #define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12) | ||
44 | |||
45 | #define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12) | ||
46 | #define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12) | ||
47 | #define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12) | ||
48 | |||
49 | #define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1) | ||
50 | #define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2) | ||
51 | #define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3) | ||
52 | #define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4) | ||
53 | |||
54 | #define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1) | ||
55 | #define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2) | ||
56 | #define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3) | ||
57 | #define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4) | ||
58 | |||
59 | |||
60 | /* These are all default values for the burgundy */ | ||
61 | #define DEF_BURGUNDY_INPSEL21 (0xAA) | ||
62 | #define DEF_BURGUNDY_INPSEL3 (0x0A) | ||
63 | |||
64 | #define DEF_BURGUNDY_GAINCD (0x33) | ||
65 | #define DEF_BURGUNDY_GAINLINE (0x44) | ||
66 | #define DEF_BURGUNDY_GAINMIC (0x44) | ||
67 | #define DEF_BURGUNDY_GAINMODEM (0x06) | ||
68 | |||
69 | /* Remember: lowest volume here is 0x9b */ | ||
70 | #define DEF_BURGUNDY_VOLCD (0xCCCCCCCC) | ||
71 | #define DEF_BURGUNDY_VOLLINE (0x00000000) | ||
72 | #define DEF_BURGUNDY_VOLMIC (0x00000000) | ||
73 | #define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC) | ||
74 | |||
75 | #define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f) | ||
76 | #define DEF_BURGUNDY_OUTPUTENABLES (0x0A) | ||
77 | |||
78 | /* #define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF) */ /* too loud */ | ||
79 | #define DEF_BURGUNDY_MASTER_VOLUME (0xDDDDDDDD) | ||
80 | |||
81 | #define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E) | ||
82 | |||
83 | #define DEF_BURGUNDY_ATTENSPEAKER (0x44) | ||
84 | #define DEF_BURGUNDY_ATTENLINEOUT (0xCC) | ||
85 | #define DEF_BURGUNDY_ATTENHP (0xCC) | ||
86 | |||
87 | /* OUTPUTENABLES bits */ | ||
88 | #define BURGUNDY_OUTPUT_LEFT 0x02 | ||
89 | #define BURGUNDY_OUTPUT_RIGHT 0x04 | ||
90 | #define BURGUNDY_OUTPUT_INTERN 0x80 | ||
91 | |||
92 | /* volume offset */ | ||
93 | #define BURGUNDY_VOLUME_OFFSET 155 | ||
94 | |||
95 | #endif /* __BURGUNDY_H */ | ||
diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c new file mode 100644 index 000000000000..f24a91693616 --- /dev/null +++ b/sound/ppc/daca.c | |||
@@ -0,0 +1,283 @@ | |||
1 | /* | ||
2 | * PMac DACA lowlevel functions | ||
3 | * | ||
4 | * Copyright (c) by Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/i2c-dev.h> | ||
26 | #include <linux/kmod.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <sound/core.h> | ||
29 | #include "pmac.h" | ||
30 | |||
31 | /* i2c address */ | ||
32 | #define DACA_I2C_ADDR 0x4d | ||
33 | |||
34 | /* registers */ | ||
35 | #define DACA_REG_SR 0x01 | ||
36 | #define DACA_REG_AVOL 0x02 | ||
37 | #define DACA_REG_GCFG 0x03 | ||
38 | |||
39 | /* maximum volume value */ | ||
40 | #define DACA_VOL_MAX 0x38 | ||
41 | |||
42 | |||
43 | typedef struct pmac_daca_t { | ||
44 | pmac_keywest_t i2c; | ||
45 | int left_vol, right_vol; | ||
46 | unsigned int deemphasis : 1; | ||
47 | unsigned int amp_on : 1; | ||
48 | } pmac_daca_t; | ||
49 | |||
50 | |||
51 | /* | ||
52 | * initialize / detect DACA | ||
53 | */ | ||
54 | static int daca_init_client(pmac_keywest_t *i2c) | ||
55 | { | ||
56 | unsigned short wdata = 0x00; | ||
57 | /* SR: no swap, 1bit delay, 32-48kHz */ | ||
58 | /* GCFG: power amp inverted, DAC on */ | ||
59 | if (i2c_smbus_write_byte_data(i2c->client, DACA_REG_SR, 0x08) < 0 || | ||
60 | i2c_smbus_write_byte_data(i2c->client, DACA_REG_GCFG, 0x05) < 0) | ||
61 | return -EINVAL; | ||
62 | return i2c_smbus_write_block_data(i2c->client, DACA_REG_AVOL, | ||
63 | 2, (unsigned char*)&wdata); | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * update volume | ||
68 | */ | ||
69 | static int daca_set_volume(pmac_daca_t *mix) | ||
70 | { | ||
71 | unsigned char data[2]; | ||
72 | |||
73 | if (! mix->i2c.client) | ||
74 | return -ENODEV; | ||
75 | |||
76 | if (mix->left_vol > DACA_VOL_MAX) | ||
77 | data[0] = DACA_VOL_MAX; | ||
78 | else | ||
79 | data[0] = mix->left_vol; | ||
80 | if (mix->right_vol > DACA_VOL_MAX) | ||
81 | data[1] = DACA_VOL_MAX; | ||
82 | else | ||
83 | data[1] = mix->right_vol; | ||
84 | data[1] |= mix->deemphasis ? 0x40 : 0; | ||
85 | if (i2c_smbus_write_block_data(mix->i2c.client, DACA_REG_AVOL, | ||
86 | 2, data) < 0) { | ||
87 | snd_printk("failed to set volume \n"); | ||
88 | return -EINVAL; | ||
89 | } | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | |||
94 | /* deemphasis switch */ | ||
95 | static int daca_info_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
96 | { | ||
97 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
98 | uinfo->count = 1; | ||
99 | uinfo->value.integer.min = 0; | ||
100 | uinfo->value.integer.max = 1; | ||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static int daca_get_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
105 | { | ||
106 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
107 | pmac_daca_t *mix; | ||
108 | if (! (mix = chip->mixer_data)) | ||
109 | return -ENODEV; | ||
110 | ucontrol->value.integer.value[0] = mix->deemphasis ? 1 : 0; | ||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | static int daca_put_deemphasis(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
115 | { | ||
116 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
117 | pmac_daca_t *mix; | ||
118 | int change; | ||
119 | |||
120 | if (! (mix = chip->mixer_data)) | ||
121 | return -ENODEV; | ||
122 | change = mix->deemphasis != ucontrol->value.integer.value[0]; | ||
123 | if (change) { | ||
124 | mix->deemphasis = ucontrol->value.integer.value[0]; | ||
125 | daca_set_volume(mix); | ||
126 | } | ||
127 | return change; | ||
128 | } | ||
129 | |||
130 | /* output volume */ | ||
131 | static int daca_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
132 | { | ||
133 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
134 | uinfo->count = 2; | ||
135 | uinfo->value.integer.min = 0; | ||
136 | uinfo->value.integer.max = DACA_VOL_MAX; | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static int daca_get_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
141 | { | ||
142 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
143 | pmac_daca_t *mix; | ||
144 | if (! (mix = chip->mixer_data)) | ||
145 | return -ENODEV; | ||
146 | ucontrol->value.integer.value[0] = mix->left_vol; | ||
147 | ucontrol->value.integer.value[1] = mix->right_vol; | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static int daca_put_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
152 | { | ||
153 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
154 | pmac_daca_t *mix; | ||
155 | int change; | ||
156 | |||
157 | if (! (mix = chip->mixer_data)) | ||
158 | return -ENODEV; | ||
159 | change = mix->left_vol != ucontrol->value.integer.value[0] || | ||
160 | mix->right_vol != ucontrol->value.integer.value[1]; | ||
161 | if (change) { | ||
162 | mix->left_vol = ucontrol->value.integer.value[0]; | ||
163 | mix->right_vol = ucontrol->value.integer.value[1]; | ||
164 | daca_set_volume(mix); | ||
165 | } | ||
166 | return change; | ||
167 | } | ||
168 | |||
169 | /* amplifier switch */ | ||
170 | #define daca_info_amp daca_info_deemphasis | ||
171 | |||
172 | static int daca_get_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
173 | { | ||
174 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
175 | pmac_daca_t *mix; | ||
176 | if (! (mix = chip->mixer_data)) | ||
177 | return -ENODEV; | ||
178 | ucontrol->value.integer.value[0] = mix->amp_on ? 1 : 0; | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static int daca_put_amp(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
183 | { | ||
184 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
185 | pmac_daca_t *mix; | ||
186 | int change; | ||
187 | |||
188 | if (! (mix = chip->mixer_data)) | ||
189 | return -ENODEV; | ||
190 | change = mix->amp_on != ucontrol->value.integer.value[0]; | ||
191 | if (change) { | ||
192 | mix->amp_on = ucontrol->value.integer.value[0]; | ||
193 | i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG, | ||
194 | mix->amp_on ? 0x05 : 0x04); | ||
195 | } | ||
196 | return change; | ||
197 | } | ||
198 | |||
199 | static snd_kcontrol_new_t daca_mixers[] = { | ||
200 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
201 | .name = "Deemphasis Switch", | ||
202 | .info = daca_info_deemphasis, | ||
203 | .get = daca_get_deemphasis, | ||
204 | .put = daca_put_deemphasis | ||
205 | }, | ||
206 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
207 | .name = "Master Playback Volume", | ||
208 | .info = daca_info_volume, | ||
209 | .get = daca_get_volume, | ||
210 | .put = daca_put_volume | ||
211 | }, | ||
212 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
213 | .name = "Power Amplifier Switch", | ||
214 | .info = daca_info_amp, | ||
215 | .get = daca_get_amp, | ||
216 | .put = daca_put_amp | ||
217 | }, | ||
218 | }; | ||
219 | |||
220 | |||
221 | #ifdef CONFIG_PMAC_PBOOK | ||
222 | static void daca_resume(pmac_t *chip) | ||
223 | { | ||
224 | pmac_daca_t *mix = chip->mixer_data; | ||
225 | i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_SR, 0x08); | ||
226 | i2c_smbus_write_byte_data(mix->i2c.client, DACA_REG_GCFG, | ||
227 | mix->amp_on ? 0x05 : 0x04); | ||
228 | daca_set_volume(mix); | ||
229 | } | ||
230 | #endif /* CONFIG_PMAC_PBOOK */ | ||
231 | |||
232 | |||
233 | static void daca_cleanup(pmac_t *chip) | ||
234 | { | ||
235 | pmac_daca_t *mix = chip->mixer_data; | ||
236 | if (! mix) | ||
237 | return; | ||
238 | snd_pmac_keywest_cleanup(&mix->i2c); | ||
239 | kfree(mix); | ||
240 | chip->mixer_data = NULL; | ||
241 | } | ||
242 | |||
243 | /* exported */ | ||
244 | int __init snd_pmac_daca_init(pmac_t *chip) | ||
245 | { | ||
246 | int i, err; | ||
247 | pmac_daca_t *mix; | ||
248 | |||
249 | #ifdef CONFIG_KMOD | ||
250 | if (current->fs->root) | ||
251 | request_module("i2c-keywest"); | ||
252 | #endif /* CONFIG_KMOD */ | ||
253 | |||
254 | mix = kmalloc(sizeof(*mix), GFP_KERNEL); | ||
255 | if (! mix) | ||
256 | return -ENOMEM; | ||
257 | memset(mix, 0, sizeof(*mix)); | ||
258 | chip->mixer_data = mix; | ||
259 | chip->mixer_free = daca_cleanup; | ||
260 | mix->amp_on = 1; /* default on */ | ||
261 | |||
262 | mix->i2c.addr = DACA_I2C_ADDR; | ||
263 | mix->i2c.init_client = daca_init_client; | ||
264 | mix->i2c.name = "DACA"; | ||
265 | if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0) | ||
266 | return err; | ||
267 | |||
268 | /* | ||
269 | * build mixers | ||
270 | */ | ||
271 | strcpy(chip->card->mixername, "PowerMac DACA"); | ||
272 | |||
273 | for (i = 0; i < ARRAY_SIZE(daca_mixers); i++) { | ||
274 | if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&daca_mixers[i], chip))) < 0) | ||
275 | return err; | ||
276 | } | ||
277 | |||
278 | #ifdef CONFIG_PMAC_PBOOK | ||
279 | chip->resume = daca_resume; | ||
280 | #endif | ||
281 | |||
282 | return 0; | ||
283 | } | ||
diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c new file mode 100644 index 000000000000..df073a05b5d7 --- /dev/null +++ b/sound/ppc/keywest.c | |||
@@ -0,0 +1,143 @@ | |||
1 | /* | ||
2 | * common keywest i2c layer | ||
3 | * | ||
4 | * Copyright (c) by Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | |||
22 | #include <sound/driver.h> | ||
23 | #include <linux/init.h> | ||
24 | #include <linux/i2c.h> | ||
25 | #include <linux/delay.h> | ||
26 | #include <linux/i2c-dev.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <sound/core.h> | ||
29 | #include "pmac.h" | ||
30 | |||
31 | /* | ||
32 | * we have to keep a static variable here since i2c attach_adapter | ||
33 | * callback cannot pass a private data. | ||
34 | */ | ||
35 | static pmac_keywest_t *keywest_ctx; | ||
36 | |||
37 | |||
38 | #define I2C_DRIVERID_KEYWEST 0xFEBA | ||
39 | |||
40 | static int keywest_attach_adapter(struct i2c_adapter *adapter); | ||
41 | static int keywest_detach_client(struct i2c_client *client); | ||
42 | |||
43 | struct i2c_driver keywest_driver = { | ||
44 | .name = "PMac Keywest Audio", | ||
45 | .id = I2C_DRIVERID_KEYWEST, | ||
46 | .flags = I2C_DF_NOTIFY, | ||
47 | .attach_adapter = &keywest_attach_adapter, | ||
48 | .detach_client = &keywest_detach_client, | ||
49 | }; | ||
50 | |||
51 | |||
52 | #ifndef i2c_device_name | ||
53 | #define i2c_device_name(x) ((x)->name) | ||
54 | #endif | ||
55 | |||
56 | static int keywest_attach_adapter(struct i2c_adapter *adapter) | ||
57 | { | ||
58 | int err; | ||
59 | struct i2c_client *new_client; | ||
60 | |||
61 | if (! keywest_ctx) | ||
62 | return -EINVAL; | ||
63 | |||
64 | if (strncmp(i2c_device_name(adapter), "mac-io", 6)) | ||
65 | return 0; /* ignored */ | ||
66 | |||
67 | new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); | ||
68 | if (! new_client) | ||
69 | return -ENOMEM; | ||
70 | |||
71 | memset(new_client, 0, sizeof(*new_client)); | ||
72 | new_client->addr = keywest_ctx->addr; | ||
73 | i2c_set_clientdata(new_client, keywest_ctx); | ||
74 | new_client->adapter = adapter; | ||
75 | new_client->driver = &keywest_driver; | ||
76 | new_client->flags = 0; | ||
77 | |||
78 | strcpy(i2c_device_name(new_client), keywest_ctx->name); | ||
79 | keywest_ctx->client = new_client; | ||
80 | |||
81 | /* Tell the i2c layer a new client has arrived */ | ||
82 | if (i2c_attach_client(new_client)) { | ||
83 | snd_printk(KERN_ERR "tumbler: cannot attach i2c client\n"); | ||
84 | err = -ENODEV; | ||
85 | goto __err; | ||
86 | } | ||
87 | |||
88 | return 0; | ||
89 | |||
90 | __err: | ||
91 | kfree(new_client); | ||
92 | keywest_ctx->client = NULL; | ||
93 | return err; | ||
94 | } | ||
95 | |||
96 | static int keywest_detach_client(struct i2c_client *client) | ||
97 | { | ||
98 | if (! keywest_ctx) | ||
99 | return 0; | ||
100 | if (client == keywest_ctx->client) | ||
101 | keywest_ctx->client = NULL; | ||
102 | |||
103 | i2c_detach_client(client); | ||
104 | kfree(client); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | /* exported */ | ||
109 | void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c) | ||
110 | { | ||
111 | if (keywest_ctx && keywest_ctx == i2c) { | ||
112 | i2c_del_driver(&keywest_driver); | ||
113 | keywest_ctx = NULL; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | int __init snd_pmac_tumbler_post_init(void) | ||
118 | { | ||
119 | int err; | ||
120 | |||
121 | if ((err = keywest_ctx->init_client(keywest_ctx)) < 0) { | ||
122 | snd_printk(KERN_ERR "tumbler: %i :cannot initialize the MCS\n", err); | ||
123 | return err; | ||
124 | } | ||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | /* exported */ | ||
129 | int __init snd_pmac_keywest_init(pmac_keywest_t *i2c) | ||
130 | { | ||
131 | int err; | ||
132 | |||
133 | if (keywest_ctx) | ||
134 | return -EBUSY; | ||
135 | |||
136 | keywest_ctx = i2c; | ||
137 | |||
138 | if ((err = i2c_add_driver(&keywest_driver))) { | ||
139 | snd_printk(KERN_ERR "cannot register keywest i2c driver\n"); | ||
140 | return err; | ||
141 | } | ||
142 | return 0; | ||
143 | } | ||
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c new file mode 100644 index 000000000000..6c4ed90f490e --- /dev/null +++ b/sound/ppc/pmac.c | |||
@@ -0,0 +1,1328 @@ | |||
1 | /* | ||
2 | * PMac DBDMA 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/irq.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/delay.h> | ||
28 | #include <linux/slab.h> | ||
29 | #include <linux/interrupt.h> | ||
30 | #include <sound/core.h> | ||
31 | #include "pmac.h" | ||
32 | #include <sound/pcm_params.h> | ||
33 | #ifdef CONFIG_PPC_HAS_FEATURE_CALLS | ||
34 | #include <asm/pmac_feature.h> | ||
35 | #else | ||
36 | #include <asm/feature.h> | ||
37 | #endif | ||
38 | |||
39 | |||
40 | #if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) | ||
41 | static int snd_pmac_register_sleep_notifier(pmac_t *chip); | ||
42 | static int snd_pmac_unregister_sleep_notifier(pmac_t *chip); | ||
43 | static int snd_pmac_suspend(snd_card_t *card, pm_message_t state); | ||
44 | static int snd_pmac_resume(snd_card_t *card); | ||
45 | #endif | ||
46 | |||
47 | |||
48 | /* fixed frequency table for awacs, screamer, burgundy, DACA (44100 max) */ | ||
49 | static int awacs_freqs[8] = { | ||
50 | 44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350 | ||
51 | }; | ||
52 | /* fixed frequency table for tumbler */ | ||
53 | static int tumbler_freqs[1] = { | ||
54 | 44100 | ||
55 | }; | ||
56 | |||
57 | /* | ||
58 | * allocate DBDMA command arrays | ||
59 | */ | ||
60 | static int snd_pmac_dbdma_alloc(pmac_dbdma_t *rec, int size) | ||
61 | { | ||
62 | rec->space = kmalloc(sizeof(struct dbdma_cmd) * (size + 1), GFP_KERNEL); | ||
63 | if (rec->space == NULL) | ||
64 | return -ENOMEM; | ||
65 | rec->size = size; | ||
66 | memset(rec->space, 0, sizeof(struct dbdma_cmd) * (size + 1)); | ||
67 | rec->cmds = (void __iomem *)DBDMA_ALIGN(rec->space); | ||
68 | rec->addr = virt_to_bus(rec->cmds); | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static void snd_pmac_dbdma_free(pmac_dbdma_t *rec) | ||
73 | { | ||
74 | if (rec) | ||
75 | kfree(rec->space); | ||
76 | } | ||
77 | |||
78 | |||
79 | /* | ||
80 | * pcm stuff | ||
81 | */ | ||
82 | |||
83 | /* | ||
84 | * look up frequency table | ||
85 | */ | ||
86 | |||
87 | unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate) | ||
88 | { | ||
89 | int i, ok, found; | ||
90 | |||
91 | ok = rec->cur_freqs; | ||
92 | if (rate > chip->freq_table[0]) | ||
93 | return 0; | ||
94 | found = 0; | ||
95 | for (i = 0; i < chip->num_freqs; i++, ok >>= 1) { | ||
96 | if (! (ok & 1)) continue; | ||
97 | found = i; | ||
98 | if (rate >= chip->freq_table[i]) | ||
99 | break; | ||
100 | } | ||
101 | return found; | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * check whether another stream is active | ||
106 | */ | ||
107 | static inline int another_stream(int stream) | ||
108 | { | ||
109 | return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? | ||
110 | SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * allocate buffers | ||
115 | */ | ||
116 | static int snd_pmac_pcm_hw_params(snd_pcm_substream_t *subs, | ||
117 | snd_pcm_hw_params_t *hw_params) | ||
118 | { | ||
119 | return snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw_params)); | ||
120 | } | ||
121 | |||
122 | /* | ||
123 | * release buffers | ||
124 | */ | ||
125 | static int snd_pmac_pcm_hw_free(snd_pcm_substream_t *subs) | ||
126 | { | ||
127 | snd_pcm_lib_free_pages(subs); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * get a stream of the opposite direction | ||
133 | */ | ||
134 | static pmac_stream_t *snd_pmac_get_stream(pmac_t *chip, int stream) | ||
135 | { | ||
136 | switch (stream) { | ||
137 | case SNDRV_PCM_STREAM_PLAYBACK: | ||
138 | return &chip->playback; | ||
139 | case SNDRV_PCM_STREAM_CAPTURE: | ||
140 | return &chip->capture; | ||
141 | default: | ||
142 | snd_BUG(); | ||
143 | return NULL; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * wait while run status is on | ||
149 | */ | ||
150 | inline static void | ||
151 | snd_pmac_wait_ack(pmac_stream_t *rec) | ||
152 | { | ||
153 | int timeout = 50000; | ||
154 | while ((in_le32(&rec->dma->status) & RUN) && timeout-- > 0) | ||
155 | udelay(1); | ||
156 | } | ||
157 | |||
158 | /* | ||
159 | * set the format and rate to the chip. | ||
160 | * call the lowlevel function if defined (e.g. for AWACS). | ||
161 | */ | ||
162 | static void snd_pmac_pcm_set_format(pmac_t *chip) | ||
163 | { | ||
164 | /* set up frequency and format */ | ||
165 | out_le32(&chip->awacs->control, chip->control_mask | (chip->rate_index << 8)); | ||
166 | out_le32(&chip->awacs->byteswap, chip->format == SNDRV_PCM_FORMAT_S16_LE ? 1 : 0); | ||
167 | if (chip->set_format) | ||
168 | chip->set_format(chip); | ||
169 | } | ||
170 | |||
171 | /* | ||
172 | * stop the DMA transfer | ||
173 | */ | ||
174 | inline static void snd_pmac_dma_stop(pmac_stream_t *rec) | ||
175 | { | ||
176 | out_le32(&rec->dma->control, (RUN|WAKE|FLUSH|PAUSE) << 16); | ||
177 | snd_pmac_wait_ack(rec); | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * set the command pointer address | ||
182 | */ | ||
183 | inline static void snd_pmac_dma_set_command(pmac_stream_t *rec, pmac_dbdma_t *cmd) | ||
184 | { | ||
185 | out_le32(&rec->dma->cmdptr, cmd->addr); | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * start the DMA | ||
190 | */ | ||
191 | inline static void snd_pmac_dma_run(pmac_stream_t *rec, int status) | ||
192 | { | ||
193 | out_le32(&rec->dma->control, status | (status << 16)); | ||
194 | } | ||
195 | |||
196 | |||
197 | /* | ||
198 | * prepare playback/capture stream | ||
199 | */ | ||
200 | static int snd_pmac_pcm_prepare(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) | ||
201 | { | ||
202 | int i; | ||
203 | volatile struct dbdma_cmd __iomem *cp; | ||
204 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
205 | int rate_index; | ||
206 | long offset; | ||
207 | pmac_stream_t *astr; | ||
208 | |||
209 | rec->dma_size = snd_pcm_lib_buffer_bytes(subs); | ||
210 | rec->period_size = snd_pcm_lib_period_bytes(subs); | ||
211 | rec->nperiods = rec->dma_size / rec->period_size; | ||
212 | rec->cur_period = 0; | ||
213 | rate_index = snd_pmac_rate_index(chip, rec, runtime->rate); | ||
214 | |||
215 | /* set up constraints */ | ||
216 | astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); | ||
217 | snd_runtime_check(astr, return -EINVAL); | ||
218 | astr->cur_freqs = 1 << rate_index; | ||
219 | astr->cur_formats = 1 << runtime->format; | ||
220 | chip->rate_index = rate_index; | ||
221 | chip->format = runtime->format; | ||
222 | |||
223 | /* We really want to execute a DMA stop command, after the AWACS | ||
224 | * is initialized. | ||
225 | * For reasons I don't understand, it stops the hissing noise | ||
226 | * common to many PowerBook G3 systems and random noise otherwise | ||
227 | * captured on iBook2's about every third time. -ReneR | ||
228 | */ | ||
229 | spin_lock_irq(&chip->reg_lock); | ||
230 | snd_pmac_dma_stop(rec); | ||
231 | st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); | ||
232 | snd_pmac_dma_set_command(rec, &chip->extra_dma); | ||
233 | snd_pmac_dma_run(rec, RUN); | ||
234 | spin_unlock_irq(&chip->reg_lock); | ||
235 | mdelay(5); | ||
236 | spin_lock_irq(&chip->reg_lock); | ||
237 | /* continuous DMA memory type doesn't provide the physical address, | ||
238 | * so we need to resolve the address here... | ||
239 | */ | ||
240 | offset = virt_to_bus(runtime->dma_area); | ||
241 | for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) { | ||
242 | st_le32(&cp->phy_addr, offset); | ||
243 | st_le16(&cp->req_count, rec->period_size); | ||
244 | /*st_le16(&cp->res_count, 0);*/ | ||
245 | st_le16(&cp->xfer_status, 0); | ||
246 | offset += rec->period_size; | ||
247 | } | ||
248 | /* make loop */ | ||
249 | st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); | ||
250 | st_le32(&cp->cmd_dep, rec->cmd.addr); | ||
251 | |||
252 | snd_pmac_dma_stop(rec); | ||
253 | snd_pmac_dma_set_command(rec, &rec->cmd); | ||
254 | spin_unlock_irq(&chip->reg_lock); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | |||
260 | /* | ||
261 | * PCM trigger/stop | ||
262 | */ | ||
263 | static int snd_pmac_pcm_trigger(pmac_t *chip, pmac_stream_t *rec, | ||
264 | snd_pcm_substream_t *subs, int cmd) | ||
265 | { | ||
266 | volatile struct dbdma_cmd __iomem *cp; | ||
267 | int i, command; | ||
268 | |||
269 | switch (cmd) { | ||
270 | case SNDRV_PCM_TRIGGER_START: | ||
271 | case SNDRV_PCM_TRIGGER_RESUME: | ||
272 | if (rec->running) | ||
273 | return -EBUSY; | ||
274 | command = (subs->stream == SNDRV_PCM_STREAM_PLAYBACK ? | ||
275 | OUTPUT_MORE : INPUT_MORE) + INTR_ALWAYS; | ||
276 | spin_lock(&chip->reg_lock); | ||
277 | snd_pmac_beep_stop(chip); | ||
278 | snd_pmac_pcm_set_format(chip); | ||
279 | for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) | ||
280 | out_le16(&cp->command, command); | ||
281 | snd_pmac_dma_set_command(rec, &rec->cmd); | ||
282 | (void)in_le32(&rec->dma->status); | ||
283 | snd_pmac_dma_run(rec, RUN|WAKE); | ||
284 | rec->running = 1; | ||
285 | spin_unlock(&chip->reg_lock); | ||
286 | break; | ||
287 | |||
288 | case SNDRV_PCM_TRIGGER_STOP: | ||
289 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
290 | spin_lock(&chip->reg_lock); | ||
291 | rec->running = 0; | ||
292 | /*printk("stopped!!\n");*/ | ||
293 | snd_pmac_dma_stop(rec); | ||
294 | for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) | ||
295 | out_le16(&cp->command, DBDMA_STOP); | ||
296 | spin_unlock(&chip->reg_lock); | ||
297 | break; | ||
298 | |||
299 | default: | ||
300 | return -EINVAL; | ||
301 | } | ||
302 | |||
303 | return 0; | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * return the current pointer | ||
308 | */ | ||
309 | inline | ||
310 | static snd_pcm_uframes_t snd_pmac_pcm_pointer(pmac_t *chip, pmac_stream_t *rec, | ||
311 | snd_pcm_substream_t *subs) | ||
312 | { | ||
313 | int count = 0; | ||
314 | |||
315 | #if 1 /* hmm.. how can we get the current dma pointer?? */ | ||
316 | int stat; | ||
317 | volatile struct dbdma_cmd __iomem *cp = &rec->cmd.cmds[rec->cur_period]; | ||
318 | stat = ld_le16(&cp->xfer_status); | ||
319 | if (stat & (ACTIVE|DEAD)) { | ||
320 | count = in_le16(&cp->res_count); | ||
321 | if (count) | ||
322 | count = rec->period_size - count; | ||
323 | } | ||
324 | #endif | ||
325 | count += rec->cur_period * rec->period_size; | ||
326 | /*printk("pointer=%d\n", count);*/ | ||
327 | return bytes_to_frames(subs->runtime, count); | ||
328 | } | ||
329 | |||
330 | /* | ||
331 | * playback | ||
332 | */ | ||
333 | |||
334 | static int snd_pmac_playback_prepare(snd_pcm_substream_t *subs) | ||
335 | { | ||
336 | pmac_t *chip = snd_pcm_substream_chip(subs); | ||
337 | return snd_pmac_pcm_prepare(chip, &chip->playback, subs); | ||
338 | } | ||
339 | |||
340 | static int snd_pmac_playback_trigger(snd_pcm_substream_t *subs, | ||
341 | int cmd) | ||
342 | { | ||
343 | pmac_t *chip = snd_pcm_substream_chip(subs); | ||
344 | return snd_pmac_pcm_trigger(chip, &chip->playback, subs, cmd); | ||
345 | } | ||
346 | |||
347 | static snd_pcm_uframes_t snd_pmac_playback_pointer(snd_pcm_substream_t *subs) | ||
348 | { | ||
349 | pmac_t *chip = snd_pcm_substream_chip(subs); | ||
350 | return snd_pmac_pcm_pointer(chip, &chip->playback, subs); | ||
351 | } | ||
352 | |||
353 | |||
354 | /* | ||
355 | * capture | ||
356 | */ | ||
357 | |||
358 | static int snd_pmac_capture_prepare(snd_pcm_substream_t *subs) | ||
359 | { | ||
360 | pmac_t *chip = snd_pcm_substream_chip(subs); | ||
361 | return snd_pmac_pcm_prepare(chip, &chip->capture, subs); | ||
362 | } | ||
363 | |||
364 | static int snd_pmac_capture_trigger(snd_pcm_substream_t *subs, | ||
365 | int cmd) | ||
366 | { | ||
367 | pmac_t *chip = snd_pcm_substream_chip(subs); | ||
368 | return snd_pmac_pcm_trigger(chip, &chip->capture, subs, cmd); | ||
369 | } | ||
370 | |||
371 | static snd_pcm_uframes_t snd_pmac_capture_pointer(snd_pcm_substream_t *subs) | ||
372 | { | ||
373 | pmac_t *chip = snd_pcm_substream_chip(subs); | ||
374 | return snd_pmac_pcm_pointer(chip, &chip->capture, subs); | ||
375 | } | ||
376 | |||
377 | |||
378 | /* | ||
379 | * update playback/capture pointer from interrupts | ||
380 | */ | ||
381 | static void snd_pmac_pcm_update(pmac_t *chip, pmac_stream_t *rec) | ||
382 | { | ||
383 | volatile struct dbdma_cmd __iomem *cp; | ||
384 | int c; | ||
385 | int stat; | ||
386 | |||
387 | spin_lock(&chip->reg_lock); | ||
388 | if (rec->running) { | ||
389 | cp = &rec->cmd.cmds[rec->cur_period]; | ||
390 | for (c = 0; c < rec->nperiods; c++) { /* at most all fragments */ | ||
391 | stat = ld_le16(&cp->xfer_status); | ||
392 | if (! (stat & ACTIVE)) | ||
393 | break; | ||
394 | /*printk("update frag %d\n", rec->cur_period);*/ | ||
395 | st_le16(&cp->xfer_status, 0); | ||
396 | st_le16(&cp->req_count, rec->period_size); | ||
397 | /*st_le16(&cp->res_count, 0);*/ | ||
398 | rec->cur_period++; | ||
399 | if (rec->cur_period >= rec->nperiods) { | ||
400 | rec->cur_period = 0; | ||
401 | cp = rec->cmd.cmds; | ||
402 | } else | ||
403 | cp++; | ||
404 | spin_unlock(&chip->reg_lock); | ||
405 | snd_pcm_period_elapsed(rec->substream); | ||
406 | spin_lock(&chip->reg_lock); | ||
407 | } | ||
408 | } | ||
409 | spin_unlock(&chip->reg_lock); | ||
410 | } | ||
411 | |||
412 | |||
413 | /* | ||
414 | * hw info | ||
415 | */ | ||
416 | |||
417 | static snd_pcm_hardware_t snd_pmac_playback = | ||
418 | { | ||
419 | .info = (SNDRV_PCM_INFO_INTERLEAVED | | ||
420 | SNDRV_PCM_INFO_MMAP | | ||
421 | SNDRV_PCM_INFO_MMAP_VALID | | ||
422 | SNDRV_PCM_INFO_RESUME), | ||
423 | .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, | ||
424 | .rates = SNDRV_PCM_RATE_8000_44100, | ||
425 | .rate_min = 7350, | ||
426 | .rate_max = 44100, | ||
427 | .channels_min = 2, | ||
428 | .channels_max = 2, | ||
429 | .buffer_bytes_max = 131072, | ||
430 | .period_bytes_min = 256, | ||
431 | .period_bytes_max = 16384, | ||
432 | .periods_min = 3, | ||
433 | .periods_max = PMAC_MAX_FRAGS, | ||
434 | }; | ||
435 | |||
436 | static snd_pcm_hardware_t snd_pmac_capture = | ||
437 | { | ||
438 | .info = (SNDRV_PCM_INFO_INTERLEAVED | | ||
439 | SNDRV_PCM_INFO_MMAP | | ||
440 | SNDRV_PCM_INFO_MMAP_VALID | | ||
441 | SNDRV_PCM_INFO_RESUME), | ||
442 | .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, | ||
443 | .rates = SNDRV_PCM_RATE_8000_44100, | ||
444 | .rate_min = 7350, | ||
445 | .rate_max = 44100, | ||
446 | .channels_min = 2, | ||
447 | .channels_max = 2, | ||
448 | .buffer_bytes_max = 131072, | ||
449 | .period_bytes_min = 256, | ||
450 | .period_bytes_max = 16384, | ||
451 | .periods_min = 3, | ||
452 | .periods_max = PMAC_MAX_FRAGS, | ||
453 | }; | ||
454 | |||
455 | |||
456 | #if 0 // NYI | ||
457 | static int snd_pmac_hw_rule_rate(snd_pcm_hw_params_t *params, | ||
458 | snd_pcm_hw_rule_t *rule) | ||
459 | { | ||
460 | pmac_t *chip = rule->private; | ||
461 | pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]); | ||
462 | int i, freq_table[8], num_freqs; | ||
463 | |||
464 | snd_runtime_check(rec, return -EINVAL); | ||
465 | num_freqs = 0; | ||
466 | for (i = chip->num_freqs - 1; i >= 0; i--) { | ||
467 | if (rec->cur_freqs & (1 << i)) | ||
468 | freq_table[num_freqs++] = chip->freq_table[i]; | ||
469 | } | ||
470 | |||
471 | return snd_interval_list(hw_param_interval(params, rule->var), | ||
472 | num_freqs, freq_table, 0); | ||
473 | } | ||
474 | |||
475 | static int snd_pmac_hw_rule_format(snd_pcm_hw_params_t *params, | ||
476 | snd_pcm_hw_rule_t *rule) | ||
477 | { | ||
478 | pmac_t *chip = rule->private; | ||
479 | pmac_stream_t *rec = snd_pmac_get_stream(chip, rule->deps[0]); | ||
480 | |||
481 | snd_runtime_check(rec, return -EINVAL); | ||
482 | return snd_mask_refine_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), | ||
483 | rec->cur_formats); | ||
484 | } | ||
485 | #endif // NYI | ||
486 | |||
487 | static int snd_pmac_pcm_open(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) | ||
488 | { | ||
489 | snd_pcm_runtime_t *runtime = subs->runtime; | ||
490 | int i, j, fflags; | ||
491 | static int typical_freqs[] = { | ||
492 | 44100, | ||
493 | 22050, | ||
494 | 11025, | ||
495 | 0, | ||
496 | }; | ||
497 | static int typical_freq_flags[] = { | ||
498 | SNDRV_PCM_RATE_44100, | ||
499 | SNDRV_PCM_RATE_22050, | ||
500 | SNDRV_PCM_RATE_11025, | ||
501 | 0, | ||
502 | }; | ||
503 | |||
504 | /* look up frequency table and fill bit mask */ | ||
505 | runtime->hw.rates = 0; | ||
506 | fflags = chip->freqs_ok; | ||
507 | for (i = 0; typical_freqs[i]; i++) { | ||
508 | for (j = 0; j < chip->num_freqs; j++) { | ||
509 | if ((chip->freqs_ok & (1 << j)) && | ||
510 | chip->freq_table[j] == typical_freqs[i]) { | ||
511 | runtime->hw.rates |= typical_freq_flags[i]; | ||
512 | fflags &= ~(1 << j); | ||
513 | break; | ||
514 | } | ||
515 | } | ||
516 | } | ||
517 | if (fflags) /* rest */ | ||
518 | runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; | ||
519 | |||
520 | /* check for minimum and maximum rates */ | ||
521 | for (i = 0; i < chip->num_freqs; i++) { | ||
522 | if (chip->freqs_ok & (1 << i)) { | ||
523 | runtime->hw.rate_max = chip->freq_table[i]; | ||
524 | break; | ||
525 | } | ||
526 | } | ||
527 | for (i = chip->num_freqs - 1; i >= 0; i--) { | ||
528 | if (chip->freqs_ok & (1 << i)) { | ||
529 | runtime->hw.rate_min = chip->freq_table[i]; | ||
530 | break; | ||
531 | } | ||
532 | } | ||
533 | runtime->hw.formats = chip->formats_ok; | ||
534 | if (chip->can_capture) { | ||
535 | if (! chip->can_duplex) | ||
536 | runtime->hw.info |= SNDRV_PCM_INFO_HALF_DUPLEX; | ||
537 | runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; | ||
538 | } | ||
539 | runtime->private_data = rec; | ||
540 | rec->substream = subs; | ||
541 | |||
542 | #if 0 /* FIXME: still under development.. */ | ||
543 | snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, | ||
544 | snd_pmac_hw_rule_rate, chip, rec->stream, -1); | ||
545 | snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, | ||
546 | snd_pmac_hw_rule_format, chip, rec->stream, -1); | ||
547 | #endif | ||
548 | |||
549 | runtime->hw.periods_max = rec->cmd.size - 1; | ||
550 | |||
551 | if (chip->can_duplex) | ||
552 | snd_pcm_set_sync(subs); | ||
553 | |||
554 | /* constraints to fix choppy sound */ | ||
555 | snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); | ||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | static int snd_pmac_pcm_close(pmac_t *chip, pmac_stream_t *rec, snd_pcm_substream_t *subs) | ||
560 | { | ||
561 | pmac_stream_t *astr; | ||
562 | |||
563 | snd_pmac_dma_stop(rec); | ||
564 | |||
565 | astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); | ||
566 | snd_runtime_check(astr, return -EINVAL); | ||
567 | |||
568 | /* reset constraints */ | ||
569 | astr->cur_freqs = chip->freqs_ok; | ||
570 | astr->cur_formats = chip->formats_ok; | ||
571 | |||
572 | return 0; | ||
573 | } | ||
574 | |||
575 | static int snd_pmac_playback_open(snd_pcm_substream_t *subs) | ||
576 | { | ||
577 | pmac_t *chip = snd_pcm_substream_chip(subs); | ||
578 | |||
579 | subs->runtime->hw = snd_pmac_playback; | ||
580 | return snd_pmac_pcm_open(chip, &chip->playback, subs); | ||
581 | } | ||
582 | |||
583 | static int snd_pmac_capture_open(snd_pcm_substream_t *subs) | ||
584 | { | ||
585 | pmac_t *chip = snd_pcm_substream_chip(subs); | ||
586 | |||
587 | subs->runtime->hw = snd_pmac_capture; | ||
588 | return snd_pmac_pcm_open(chip, &chip->capture, subs); | ||
589 | } | ||
590 | |||
591 | static int snd_pmac_playback_close(snd_pcm_substream_t *subs) | ||
592 | { | ||
593 | pmac_t *chip = snd_pcm_substream_chip(subs); | ||
594 | |||
595 | return snd_pmac_pcm_close(chip, &chip->playback, subs); | ||
596 | } | ||
597 | |||
598 | static int snd_pmac_capture_close(snd_pcm_substream_t *subs) | ||
599 | { | ||
600 | pmac_t *chip = snd_pcm_substream_chip(subs); | ||
601 | |||
602 | return snd_pmac_pcm_close(chip, &chip->capture, subs); | ||
603 | } | ||
604 | |||
605 | /* | ||
606 | */ | ||
607 | |||
608 | static snd_pcm_ops_t snd_pmac_playback_ops = { | ||
609 | .open = snd_pmac_playback_open, | ||
610 | .close = snd_pmac_playback_close, | ||
611 | .ioctl = snd_pcm_lib_ioctl, | ||
612 | .hw_params = snd_pmac_pcm_hw_params, | ||
613 | .hw_free = snd_pmac_pcm_hw_free, | ||
614 | .prepare = snd_pmac_playback_prepare, | ||
615 | .trigger = snd_pmac_playback_trigger, | ||
616 | .pointer = snd_pmac_playback_pointer, | ||
617 | }; | ||
618 | |||
619 | static snd_pcm_ops_t snd_pmac_capture_ops = { | ||
620 | .open = snd_pmac_capture_open, | ||
621 | .close = snd_pmac_capture_close, | ||
622 | .ioctl = snd_pcm_lib_ioctl, | ||
623 | .hw_params = snd_pmac_pcm_hw_params, | ||
624 | .hw_free = snd_pmac_pcm_hw_free, | ||
625 | .prepare = snd_pmac_capture_prepare, | ||
626 | .trigger = snd_pmac_capture_trigger, | ||
627 | .pointer = snd_pmac_capture_pointer, | ||
628 | }; | ||
629 | |||
630 | static void pmac_pcm_free(snd_pcm_t *pcm) | ||
631 | { | ||
632 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
633 | } | ||
634 | |||
635 | int __init snd_pmac_pcm_new(pmac_t *chip) | ||
636 | { | ||
637 | snd_pcm_t *pcm; | ||
638 | int err; | ||
639 | int num_captures = 1; | ||
640 | |||
641 | if (! chip->can_capture) | ||
642 | num_captures = 0; | ||
643 | err = snd_pcm_new(chip->card, chip->card->driver, 0, 1, num_captures, &pcm); | ||
644 | if (err < 0) | ||
645 | return err; | ||
646 | |||
647 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pmac_playback_ops); | ||
648 | if (chip->can_capture) | ||
649 | snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pmac_capture_ops); | ||
650 | |||
651 | pcm->private_data = chip; | ||
652 | pcm->private_free = pmac_pcm_free; | ||
653 | pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; | ||
654 | strcpy(pcm->name, chip->card->shortname); | ||
655 | chip->pcm = pcm; | ||
656 | |||
657 | chip->formats_ok = SNDRV_PCM_FMTBIT_S16_BE; | ||
658 | if (chip->can_byte_swap) | ||
659 | chip->formats_ok |= SNDRV_PCM_FMTBIT_S16_LE; | ||
660 | |||
661 | chip->playback.cur_formats = chip->formats_ok; | ||
662 | chip->capture.cur_formats = chip->formats_ok; | ||
663 | chip->playback.cur_freqs = chip->freqs_ok; | ||
664 | chip->capture.cur_freqs = chip->freqs_ok; | ||
665 | |||
666 | /* preallocate 64k buffer */ | ||
667 | snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, | ||
668 | snd_dma_continuous_data(GFP_KERNEL), | ||
669 | 64 * 1024, 64 * 1024); | ||
670 | |||
671 | return 0; | ||
672 | } | ||
673 | |||
674 | |||
675 | static void snd_pmac_dbdma_reset(pmac_t *chip) | ||
676 | { | ||
677 | out_le32(&chip->playback.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); | ||
678 | snd_pmac_wait_ack(&chip->playback); | ||
679 | out_le32(&chip->capture.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); | ||
680 | snd_pmac_wait_ack(&chip->capture); | ||
681 | } | ||
682 | |||
683 | |||
684 | /* | ||
685 | * handling beep | ||
686 | */ | ||
687 | void snd_pmac_beep_dma_start(pmac_t *chip, int bytes, unsigned long addr, int speed) | ||
688 | { | ||
689 | pmac_stream_t *rec = &chip->playback; | ||
690 | |||
691 | snd_pmac_dma_stop(rec); | ||
692 | st_le16(&chip->extra_dma.cmds->req_count, bytes); | ||
693 | st_le16(&chip->extra_dma.cmds->xfer_status, 0); | ||
694 | st_le32(&chip->extra_dma.cmds->cmd_dep, chip->extra_dma.addr); | ||
695 | st_le32(&chip->extra_dma.cmds->phy_addr, addr); | ||
696 | st_le16(&chip->extra_dma.cmds->command, OUTPUT_MORE + BR_ALWAYS); | ||
697 | out_le32(&chip->awacs->control, | ||
698 | (in_le32(&chip->awacs->control) & ~0x1f00) | ||
699 | | (speed << 8)); | ||
700 | out_le32(&chip->awacs->byteswap, 0); | ||
701 | snd_pmac_dma_set_command(rec, &chip->extra_dma); | ||
702 | snd_pmac_dma_run(rec, RUN); | ||
703 | } | ||
704 | |||
705 | void snd_pmac_beep_dma_stop(pmac_t *chip) | ||
706 | { | ||
707 | snd_pmac_dma_stop(&chip->playback); | ||
708 | st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); | ||
709 | snd_pmac_pcm_set_format(chip); /* reset format */ | ||
710 | } | ||
711 | |||
712 | |||
713 | /* | ||
714 | * interrupt handlers | ||
715 | */ | ||
716 | static irqreturn_t | ||
717 | snd_pmac_tx_intr(int irq, void *devid, struct pt_regs *regs) | ||
718 | { | ||
719 | pmac_t *chip = devid; | ||
720 | snd_pmac_pcm_update(chip, &chip->playback); | ||
721 | return IRQ_HANDLED; | ||
722 | } | ||
723 | |||
724 | |||
725 | static irqreturn_t | ||
726 | snd_pmac_rx_intr(int irq, void *devid, struct pt_regs *regs) | ||
727 | { | ||
728 | pmac_t *chip = devid; | ||
729 | snd_pmac_pcm_update(chip, &chip->capture); | ||
730 | return IRQ_HANDLED; | ||
731 | } | ||
732 | |||
733 | |||
734 | static irqreturn_t | ||
735 | snd_pmac_ctrl_intr(int irq, void *devid, struct pt_regs *regs) | ||
736 | { | ||
737 | pmac_t *chip = devid; | ||
738 | int ctrl = in_le32(&chip->awacs->control); | ||
739 | |||
740 | /*printk("pmac: control interrupt.. 0x%x\n", ctrl);*/ | ||
741 | if (ctrl & MASK_PORTCHG) { | ||
742 | /* do something when headphone is plugged/unplugged? */ | ||
743 | if (chip->update_automute) | ||
744 | chip->update_automute(chip, 1); | ||
745 | } | ||
746 | if (ctrl & MASK_CNTLERR) { | ||
747 | int err = (in_le32(&chip->awacs->codec_stat) & MASK_ERRCODE) >> 16; | ||
748 | if (err && chip->model <= PMAC_SCREAMER) | ||
749 | snd_printk(KERN_DEBUG "error %x\n", err); | ||
750 | } | ||
751 | /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ | ||
752 | out_le32(&chip->awacs->control, ctrl); | ||
753 | return IRQ_HANDLED; | ||
754 | } | ||
755 | |||
756 | |||
757 | /* | ||
758 | * a wrapper to feature call for compatibility | ||
759 | */ | ||
760 | #if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) | ||
761 | static void snd_pmac_sound_feature(pmac_t *chip, int enable) | ||
762 | { | ||
763 | #ifdef CONFIG_PPC_HAS_FEATURE_CALLS | ||
764 | ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable); | ||
765 | #else | ||
766 | if (chip->is_pbook_G3) { | ||
767 | pmu_suspend(); | ||
768 | feature_clear(chip->node, FEATURE_Sound_power); | ||
769 | feature_clear(chip->node, FEATURE_Sound_CLK_enable); | ||
770 | big_mdelay(1000); /* XXX */ | ||
771 | pmu_resume(); | ||
772 | } | ||
773 | if (chip->is_pbook_3400) { | ||
774 | feature_set(chip->node, FEATURE_IOBUS_enable); | ||
775 | udelay(10); | ||
776 | } | ||
777 | #endif | ||
778 | } | ||
779 | #else /* CONFIG_PM && CONFIG_PMAC_PBOOK */ | ||
780 | #define snd_pmac_sound_feature(chip,enable) /**/ | ||
781 | #endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */ | ||
782 | |||
783 | /* | ||
784 | * release resources | ||
785 | */ | ||
786 | |||
787 | static int snd_pmac_free(pmac_t *chip) | ||
788 | { | ||
789 | int i; | ||
790 | |||
791 | /* stop sounds */ | ||
792 | if (chip->initialized) { | ||
793 | snd_pmac_dbdma_reset(chip); | ||
794 | /* disable interrupts from awacs interface */ | ||
795 | out_le32(&chip->awacs->control, in_le32(&chip->awacs->control) & 0xfff); | ||
796 | } | ||
797 | |||
798 | snd_pmac_sound_feature(chip, 0); | ||
799 | #if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) | ||
800 | snd_pmac_unregister_sleep_notifier(chip); | ||
801 | #endif | ||
802 | |||
803 | /* clean up mixer if any */ | ||
804 | if (chip->mixer_free) | ||
805 | chip->mixer_free(chip); | ||
806 | |||
807 | snd_pmac_detach_beep(chip); | ||
808 | |||
809 | /* release resources */ | ||
810 | if (chip->irq >= 0) | ||
811 | free_irq(chip->irq, (void*)chip); | ||
812 | if (chip->tx_irq >= 0) | ||
813 | free_irq(chip->tx_irq, (void*)chip); | ||
814 | if (chip->rx_irq >= 0) | ||
815 | free_irq(chip->rx_irq, (void*)chip); | ||
816 | snd_pmac_dbdma_free(&chip->playback.cmd); | ||
817 | snd_pmac_dbdma_free(&chip->capture.cmd); | ||
818 | snd_pmac_dbdma_free(&chip->extra_dma); | ||
819 | if (chip->macio_base) | ||
820 | iounmap(chip->macio_base); | ||
821 | if (chip->latch_base) | ||
822 | iounmap(chip->latch_base); | ||
823 | if (chip->awacs) | ||
824 | iounmap(chip->awacs); | ||
825 | if (chip->playback.dma) | ||
826 | iounmap(chip->playback.dma); | ||
827 | if (chip->capture.dma) | ||
828 | iounmap(chip->capture.dma); | ||
829 | if (chip->node) { | ||
830 | for (i = 0; i < 3; i++) { | ||
831 | if (chip->of_requested & (1 << i)) | ||
832 | release_OF_resource(chip->node, i); | ||
833 | } | ||
834 | } | ||
835 | kfree(chip); | ||
836 | return 0; | ||
837 | } | ||
838 | |||
839 | |||
840 | /* | ||
841 | * free the device | ||
842 | */ | ||
843 | static int snd_pmac_dev_free(snd_device_t *device) | ||
844 | { | ||
845 | pmac_t *chip = device->device_data; | ||
846 | return snd_pmac_free(chip); | ||
847 | } | ||
848 | |||
849 | |||
850 | /* | ||
851 | * check the machine support byteswap (little-endian) | ||
852 | */ | ||
853 | |||
854 | static void __init detect_byte_swap(pmac_t *chip) | ||
855 | { | ||
856 | struct device_node *mio; | ||
857 | |||
858 | /* if seems that Keylargo can't byte-swap */ | ||
859 | for (mio = chip->node->parent; mio; mio = mio->parent) { | ||
860 | if (strcmp(mio->name, "mac-io") == 0) { | ||
861 | if (device_is_compatible(mio, "Keylargo")) | ||
862 | chip->can_byte_swap = 0; | ||
863 | break; | ||
864 | } | ||
865 | } | ||
866 | |||
867 | /* it seems the Pismo & iBook can't byte-swap in hardware. */ | ||
868 | if (machine_is_compatible("PowerBook3,1") || | ||
869 | machine_is_compatible("PowerBook2,1")) | ||
870 | chip->can_byte_swap = 0 ; | ||
871 | |||
872 | if (machine_is_compatible("PowerBook2,1")) | ||
873 | chip->can_duplex = 0; | ||
874 | } | ||
875 | |||
876 | |||
877 | /* | ||
878 | * detect a sound chip | ||
879 | */ | ||
880 | static int __init snd_pmac_detect(pmac_t *chip) | ||
881 | { | ||
882 | struct device_node *sound; | ||
883 | unsigned int *prop, l; | ||
884 | |||
885 | if (_machine != _MACH_Pmac) | ||
886 | return -ENODEV; | ||
887 | |||
888 | chip->subframe = 0; | ||
889 | chip->revision = 0; | ||
890 | chip->freqs_ok = 0xff; /* all ok */ | ||
891 | chip->model = PMAC_AWACS; | ||
892 | chip->can_byte_swap = 1; | ||
893 | chip->can_duplex = 1; | ||
894 | chip->can_capture = 1; | ||
895 | chip->num_freqs = ARRAY_SIZE(awacs_freqs); | ||
896 | chip->freq_table = awacs_freqs; | ||
897 | |||
898 | chip->control_mask = MASK_IEPC | MASK_IEE | 0x11; /* default */ | ||
899 | |||
900 | /* check machine type */ | ||
901 | if (machine_is_compatible("AAPL,3400/2400") | ||
902 | || machine_is_compatible("AAPL,3500")) | ||
903 | chip->is_pbook_3400 = 1; | ||
904 | else if (machine_is_compatible("PowerBook1,1") | ||
905 | || machine_is_compatible("AAPL,PowerBook1998")) | ||
906 | chip->is_pbook_G3 = 1; | ||
907 | chip->node = find_devices("awacs"); | ||
908 | if (chip->node) | ||
909 | return 0; /* ok */ | ||
910 | |||
911 | /* | ||
912 | * powermac G3 models have a node called "davbus" | ||
913 | * with a child called "sound". | ||
914 | */ | ||
915 | chip->node = find_devices("davbus"); | ||
916 | /* | ||
917 | * if we didn't find a davbus device, try 'i2s-a' since | ||
918 | * this seems to be what iBooks have | ||
919 | */ | ||
920 | if (! chip->node) | ||
921 | chip->node = find_devices("i2s-a"); | ||
922 | if (! chip->node) | ||
923 | return -ENODEV; | ||
924 | sound = find_devices("sound"); | ||
925 | while (sound && sound->parent != chip->node) | ||
926 | sound = sound->next; | ||
927 | if (! sound) | ||
928 | return -ENODEV; | ||
929 | prop = (unsigned int *) get_property(sound, "sub-frame", NULL); | ||
930 | if (prop && *prop < 16) | ||
931 | chip->subframe = *prop; | ||
932 | /* This should be verified on older screamers */ | ||
933 | if (device_is_compatible(sound, "screamer")) { | ||
934 | chip->model = PMAC_SCREAMER; | ||
935 | // chip->can_byte_swap = 0; /* FIXME: check this */ | ||
936 | } | ||
937 | if (device_is_compatible(sound, "burgundy")) { | ||
938 | chip->model = PMAC_BURGUNDY; | ||
939 | chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ | ||
940 | } | ||
941 | if (device_is_compatible(sound, "daca")) { | ||
942 | chip->model = PMAC_DACA; | ||
943 | chip->can_capture = 0; /* no capture */ | ||
944 | chip->can_duplex = 0; | ||
945 | // chip->can_byte_swap = 0; /* FIXME: check this */ | ||
946 | chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ | ||
947 | } | ||
948 | if (device_is_compatible(sound, "tumbler")) { | ||
949 | chip->model = PMAC_TUMBLER; | ||
950 | chip->can_capture = 0; /* no capture */ | ||
951 | chip->can_duplex = 0; | ||
952 | // chip->can_byte_swap = 0; /* FIXME: check this */ | ||
953 | chip->num_freqs = ARRAY_SIZE(tumbler_freqs); | ||
954 | chip->freq_table = tumbler_freqs; | ||
955 | chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ | ||
956 | } | ||
957 | if (device_is_compatible(sound, "snapper")) { | ||
958 | chip->model = PMAC_SNAPPER; | ||
959 | // chip->can_byte_swap = 0; /* FIXME: check this */ | ||
960 | chip->num_freqs = ARRAY_SIZE(tumbler_freqs); | ||
961 | chip->freq_table = tumbler_freqs; | ||
962 | chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ | ||
963 | } | ||
964 | if (device_is_compatible(sound, "AOAKeylargo")) { | ||
965 | /* Seems to support the stock AWACS frequencies, but has | ||
966 | a snapper mixer */ | ||
967 | chip->model = PMAC_SNAPPER; | ||
968 | // chip->can_byte_swap = 0; /* FIXME: check this */ | ||
969 | chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */ | ||
970 | } | ||
971 | prop = (unsigned int *)get_property(sound, "device-id", NULL); | ||
972 | if (prop) | ||
973 | chip->device_id = *prop; | ||
974 | chip->has_iic = (find_devices("perch") != NULL); | ||
975 | |||
976 | detect_byte_swap(chip); | ||
977 | |||
978 | /* look for a property saying what sample rates | ||
979 | are available */ | ||
980 | prop = (unsigned int *) get_property(sound, "sample-rates", &l); | ||
981 | if (! prop) | ||
982 | prop = (unsigned int *) get_property(sound, "output-frame-rates", &l); | ||
983 | if (prop) { | ||
984 | int i; | ||
985 | chip->freqs_ok = 0; | ||
986 | for (l /= sizeof(int); l > 0; --l) { | ||
987 | unsigned int r = *prop++; | ||
988 | /* Apple 'Fixed' format */ | ||
989 | if (r >= 0x10000) | ||
990 | r >>= 16; | ||
991 | for (i = 0; i < chip->num_freqs; ++i) { | ||
992 | if (r == chip->freq_table[i]) { | ||
993 | chip->freqs_ok |= (1 << i); | ||
994 | break; | ||
995 | } | ||
996 | } | ||
997 | } | ||
998 | } else { | ||
999 | /* assume only 44.1khz */ | ||
1000 | chip->freqs_ok = 1; | ||
1001 | } | ||
1002 | |||
1003 | return 0; | ||
1004 | } | ||
1005 | |||
1006 | /* | ||
1007 | * exported - boolean info callbacks for ease of programming | ||
1008 | */ | ||
1009 | int snd_pmac_boolean_stereo_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
1010 | { | ||
1011 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
1012 | uinfo->count = 2; | ||
1013 | uinfo->value.integer.min = 0; | ||
1014 | uinfo->value.integer.max = 1; | ||
1015 | return 0; | ||
1016 | } | ||
1017 | |||
1018 | int snd_pmac_boolean_mono_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
1019 | { | ||
1020 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
1021 | uinfo->count = 1; | ||
1022 | uinfo->value.integer.min = 0; | ||
1023 | uinfo->value.integer.max = 1; | ||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1027 | #ifdef PMAC_SUPPORT_AUTOMUTE | ||
1028 | /* | ||
1029 | * auto-mute | ||
1030 | */ | ||
1031 | static int pmac_auto_mute_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
1032 | { | ||
1033 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
1034 | ucontrol->value.integer.value[0] = chip->auto_mute; | ||
1035 | return 0; | ||
1036 | } | ||
1037 | |||
1038 | static int pmac_auto_mute_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
1039 | { | ||
1040 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
1041 | if (ucontrol->value.integer.value[0] != chip->auto_mute) { | ||
1042 | chip->auto_mute = ucontrol->value.integer.value[0]; | ||
1043 | if (chip->update_automute) | ||
1044 | chip->update_automute(chip, 1); | ||
1045 | return 1; | ||
1046 | } | ||
1047 | return 0; | ||
1048 | } | ||
1049 | |||
1050 | static int pmac_hp_detect_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
1051 | { | ||
1052 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
1053 | if (chip->detect_headphone) | ||
1054 | ucontrol->value.integer.value[0] = chip->detect_headphone(chip); | ||
1055 | else | ||
1056 | ucontrol->value.integer.value[0] = 0; | ||
1057 | return 0; | ||
1058 | } | ||
1059 | |||
1060 | static snd_kcontrol_new_t auto_mute_controls[] __initdata = { | ||
1061 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1062 | .name = "Auto Mute Switch", | ||
1063 | .info = snd_pmac_boolean_mono_info, | ||
1064 | .get = pmac_auto_mute_get, | ||
1065 | .put = pmac_auto_mute_put, | ||
1066 | }, | ||
1067 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1068 | .name = "Headphone Detection", | ||
1069 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
1070 | .info = snd_pmac_boolean_mono_info, | ||
1071 | .get = pmac_hp_detect_get, | ||
1072 | }, | ||
1073 | }; | ||
1074 | |||
1075 | int __init snd_pmac_add_automute(pmac_t *chip) | ||
1076 | { | ||
1077 | int err; | ||
1078 | chip->auto_mute = 1; | ||
1079 | err = snd_ctl_add(chip->card, snd_ctl_new1(&auto_mute_controls[0], chip)); | ||
1080 | if (err < 0) | ||
1081 | return err; | ||
1082 | chip->hp_detect_ctl = snd_ctl_new1(&auto_mute_controls[1], chip); | ||
1083 | return snd_ctl_add(chip->card, chip->hp_detect_ctl); | ||
1084 | } | ||
1085 | #endif /* PMAC_SUPPORT_AUTOMUTE */ | ||
1086 | |||
1087 | /* | ||
1088 | * create and detect a pmac chip record | ||
1089 | */ | ||
1090 | int __init snd_pmac_new(snd_card_t *card, pmac_t **chip_return) | ||
1091 | { | ||
1092 | pmac_t *chip; | ||
1093 | struct device_node *np; | ||
1094 | int i, err; | ||
1095 | static snd_device_ops_t ops = { | ||
1096 | .dev_free = snd_pmac_dev_free, | ||
1097 | }; | ||
1098 | |||
1099 | snd_runtime_check(chip_return, return -EINVAL); | ||
1100 | *chip_return = NULL; | ||
1101 | |||
1102 | chip = kcalloc(1, sizeof(*chip), GFP_KERNEL); | ||
1103 | if (chip == NULL) | ||
1104 | return -ENOMEM; | ||
1105 | chip->card = card; | ||
1106 | |||
1107 | spin_lock_init(&chip->reg_lock); | ||
1108 | chip->irq = chip->tx_irq = chip->rx_irq = -1; | ||
1109 | |||
1110 | chip->playback.stream = SNDRV_PCM_STREAM_PLAYBACK; | ||
1111 | chip->capture.stream = SNDRV_PCM_STREAM_CAPTURE; | ||
1112 | |||
1113 | if ((err = snd_pmac_detect(chip)) < 0) | ||
1114 | goto __error; | ||
1115 | |||
1116 | if (snd_pmac_dbdma_alloc(&chip->playback.cmd, PMAC_MAX_FRAGS + 1) < 0 || | ||
1117 | snd_pmac_dbdma_alloc(&chip->capture.cmd, PMAC_MAX_FRAGS + 1) < 0 || | ||
1118 | snd_pmac_dbdma_alloc(&chip->extra_dma, 2) < 0) { | ||
1119 | err = -ENOMEM; | ||
1120 | goto __error; | ||
1121 | } | ||
1122 | |||
1123 | np = chip->node; | ||
1124 | if (np->n_addrs < 3 || np->n_intrs < 3) { | ||
1125 | err = -ENODEV; | ||
1126 | goto __error; | ||
1127 | } | ||
1128 | |||
1129 | for (i = 0; i < 3; i++) { | ||
1130 | static char *name[3] = { NULL, "- Tx DMA", "- Rx DMA" }; | ||
1131 | if (! request_OF_resource(np, i, name[i])) { | ||
1132 | snd_printk(KERN_ERR "pmac: can't request resource %d!\n", i); | ||
1133 | err = -ENODEV; | ||
1134 | goto __error; | ||
1135 | } | ||
1136 | chip->of_requested |= (1 << i); | ||
1137 | } | ||
1138 | |||
1139 | chip->awacs = ioremap(np->addrs[0].address, 0x1000); | ||
1140 | chip->playback.dma = ioremap(np->addrs[1].address, 0x100); | ||
1141 | chip->capture.dma = ioremap(np->addrs[2].address, 0x100); | ||
1142 | if (chip->model <= PMAC_BURGUNDY) { | ||
1143 | if (request_irq(np->intrs[0].line, snd_pmac_ctrl_intr, 0, | ||
1144 | "PMac", (void*)chip)) { | ||
1145 | snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[0].line); | ||
1146 | err = -EBUSY; | ||
1147 | goto __error; | ||
1148 | } | ||
1149 | chip->irq = np->intrs[0].line; | ||
1150 | } | ||
1151 | if (request_irq(np->intrs[1].line, snd_pmac_tx_intr, 0, | ||
1152 | "PMac Output", (void*)chip)) { | ||
1153 | snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[1].line); | ||
1154 | err = -EBUSY; | ||
1155 | goto __error; | ||
1156 | } | ||
1157 | chip->tx_irq = np->intrs[1].line; | ||
1158 | if (request_irq(np->intrs[2].line, snd_pmac_rx_intr, 0, | ||
1159 | "PMac Input", (void*)chip)) { | ||
1160 | snd_printk(KERN_ERR "pmac: unable to grab IRQ %d\n", np->intrs[2].line); | ||
1161 | err = -EBUSY; | ||
1162 | goto __error; | ||
1163 | } | ||
1164 | chip->rx_irq = np->intrs[2].line; | ||
1165 | |||
1166 | snd_pmac_sound_feature(chip, 1); | ||
1167 | |||
1168 | /* reset */ | ||
1169 | out_le32(&chip->awacs->control, 0x11); | ||
1170 | |||
1171 | /* Powerbooks have odd ways of enabling inputs such as | ||
1172 | an expansion-bay CD or sound from an internal modem | ||
1173 | or a PC-card modem. */ | ||
1174 | if (chip->is_pbook_3400) { | ||
1175 | /* Enable CD and PC-card sound inputs. */ | ||
1176 | /* This is done by reading from address | ||
1177 | * f301a000, + 0x10 to enable the expansion-bay | ||
1178 | * CD sound input, + 0x80 to enable the PC-card | ||
1179 | * sound input. The 0x100 enables the SCSI bus | ||
1180 | * terminator power. | ||
1181 | */ | ||
1182 | chip->latch_base = ioremap (0xf301a000, 0x1000); | ||
1183 | in_8(chip->latch_base + 0x190); | ||
1184 | } else if (chip->is_pbook_G3) { | ||
1185 | struct device_node* mio; | ||
1186 | for (mio = chip->node->parent; mio; mio = mio->parent) { | ||
1187 | if (strcmp(mio->name, "mac-io") == 0 | ||
1188 | && mio->n_addrs > 0) { | ||
1189 | chip->macio_base = ioremap(mio->addrs[0].address, 0x40); | ||
1190 | break; | ||
1191 | } | ||
1192 | } | ||
1193 | /* Enable CD sound input. */ | ||
1194 | /* The relevant bits for writing to this byte are 0x8f. | ||
1195 | * I haven't found out what the 0x80 bit does. | ||
1196 | * For the 0xf bits, writing 3 or 7 enables the CD | ||
1197 | * input, any other value disables it. Values | ||
1198 | * 1, 3, 5, 7 enable the microphone. Values 0, 2, | ||
1199 | * 4, 6, 8 - f enable the input from the modem. | ||
1200 | */ | ||
1201 | if (chip->macio_base) | ||
1202 | out_8(chip->macio_base + 0x37, 3); | ||
1203 | } | ||
1204 | |||
1205 | /* Reset dbdma channels */ | ||
1206 | snd_pmac_dbdma_reset(chip); | ||
1207 | |||
1208 | #if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) | ||
1209 | /* add sleep notifier */ | ||
1210 | if (! snd_pmac_register_sleep_notifier(chip)) | ||
1211 | snd_card_set_pm_callback(chip->card, snd_pmac_suspend, snd_pmac_resume, chip); | ||
1212 | #endif | ||
1213 | |||
1214 | if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) | ||
1215 | goto __error; | ||
1216 | |||
1217 | *chip_return = chip; | ||
1218 | return 0; | ||
1219 | |||
1220 | __error: | ||
1221 | snd_pmac_free(chip); | ||
1222 | return err; | ||
1223 | } | ||
1224 | |||
1225 | |||
1226 | /* | ||
1227 | * sleep notify for powerbook | ||
1228 | */ | ||
1229 | |||
1230 | #if defined(CONFIG_PM) && defined(CONFIG_PMAC_PBOOK) | ||
1231 | |||
1232 | /* | ||
1233 | * Save state when going to sleep, restore it afterwards. | ||
1234 | */ | ||
1235 | |||
1236 | static int snd_pmac_suspend(snd_card_t *card, pm_message_t state) | ||
1237 | { | ||
1238 | pmac_t *chip = card->pm_private_data; | ||
1239 | unsigned long flags; | ||
1240 | |||
1241 | if (chip->suspend) | ||
1242 | chip->suspend(chip); | ||
1243 | snd_pcm_suspend_all(chip->pcm); | ||
1244 | spin_lock_irqsave(&chip->reg_lock, flags); | ||
1245 | snd_pmac_beep_stop(chip); | ||
1246 | spin_unlock_irqrestore(&chip->reg_lock, flags); | ||
1247 | if (chip->irq >= 0) | ||
1248 | disable_irq(chip->irq); | ||
1249 | if (chip->tx_irq >= 0) | ||
1250 | disable_irq(chip->tx_irq); | ||
1251 | if (chip->rx_irq >= 0) | ||
1252 | disable_irq(chip->rx_irq); | ||
1253 | snd_pmac_sound_feature(chip, 0); | ||
1254 | return 0; | ||
1255 | } | ||
1256 | |||
1257 | static int snd_pmac_resume(snd_card_t *card) | ||
1258 | { | ||
1259 | pmac_t *chip = card->pm_private_data; | ||
1260 | |||
1261 | snd_pmac_sound_feature(chip, 1); | ||
1262 | if (chip->resume) | ||
1263 | chip->resume(chip); | ||
1264 | /* enable CD sound input */ | ||
1265 | if (chip->macio_base && chip->is_pbook_G3) { | ||
1266 | out_8(chip->macio_base + 0x37, 3); | ||
1267 | } else if (chip->is_pbook_3400) { | ||
1268 | in_8(chip->latch_base + 0x190); | ||
1269 | } | ||
1270 | |||
1271 | snd_pmac_pcm_set_format(chip); | ||
1272 | |||
1273 | if (chip->irq >= 0) | ||
1274 | enable_irq(chip->irq); | ||
1275 | if (chip->tx_irq >= 0) | ||
1276 | enable_irq(chip->tx_irq); | ||
1277 | if (chip->rx_irq >= 0) | ||
1278 | enable_irq(chip->rx_irq); | ||
1279 | |||
1280 | return 0; | ||
1281 | } | ||
1282 | |||
1283 | /* the chip is stored statically by snd_pmac_register_sleep_notifier | ||
1284 | * because we can't have any private data for notify callback. | ||
1285 | */ | ||
1286 | static pmac_t *sleeping_pmac = NULL; | ||
1287 | |||
1288 | static int snd_pmac_sleep_notify(struct pmu_sleep_notifier *self, int when) | ||
1289 | { | ||
1290 | pmac_t *chip; | ||
1291 | |||
1292 | chip = sleeping_pmac; | ||
1293 | snd_runtime_check(chip, return 0); | ||
1294 | |||
1295 | switch (when) { | ||
1296 | case PBOOK_SLEEP_NOW: | ||
1297 | snd_pmac_suspend(chip->card, PMSG_SUSPEND); | ||
1298 | break; | ||
1299 | case PBOOK_WAKE: | ||
1300 | snd_pmac_resume(chip->card); | ||
1301 | break; | ||
1302 | } | ||
1303 | return PBOOK_SLEEP_OK; | ||
1304 | } | ||
1305 | |||
1306 | static struct pmu_sleep_notifier snd_pmac_sleep_notifier = { | ||
1307 | snd_pmac_sleep_notify, SLEEP_LEVEL_SOUND, | ||
1308 | }; | ||
1309 | |||
1310 | static int __init snd_pmac_register_sleep_notifier(pmac_t *chip) | ||
1311 | { | ||
1312 | /* should be protected here.. */ | ||
1313 | snd_assert(! sleeping_pmac, return -EBUSY); | ||
1314 | sleeping_pmac = chip; | ||
1315 | pmu_register_sleep_notifier(&snd_pmac_sleep_notifier); | ||
1316 | return 0; | ||
1317 | } | ||
1318 | |||
1319 | static int snd_pmac_unregister_sleep_notifier(pmac_t *chip) | ||
1320 | { | ||
1321 | /* should be protected here.. */ | ||
1322 | snd_assert(sleeping_pmac == chip, return -ENODEV); | ||
1323 | pmu_unregister_sleep_notifier(&snd_pmac_sleep_notifier); | ||
1324 | sleeping_pmac = NULL; | ||
1325 | return 0; | ||
1326 | } | ||
1327 | |||
1328 | #endif /* CONFIG_PM && CONFIG_PMAC_PBOOK */ | ||
diff --git a/sound/ppc/pmac.h b/sound/ppc/pmac.h new file mode 100644 index 000000000000..a699b01210ee --- /dev/null +++ b/sound/ppc/pmac.h | |||
@@ -0,0 +1,214 @@ | |||
1 | /* | ||
2 | * Driver for PowerMac onboard soundchips | ||
3 | * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de> | ||
4 | * based on dmasound.c. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | |||
22 | #ifndef __PMAC_H | ||
23 | #define __PMAC_H | ||
24 | |||
25 | #include <linux/version.h> | ||
26 | #include <sound/control.h> | ||
27 | #include <sound/pcm.h> | ||
28 | #include "awacs.h" | ||
29 | |||
30 | #include <linux/adb.h> | ||
31 | #ifdef CONFIG_ADB_CUDA | ||
32 | #include <linux/cuda.h> | ||
33 | #endif | ||
34 | #ifdef CONFIG_ADB_PMU | ||
35 | #include <linux/pmu.h> | ||
36 | #endif | ||
37 | #include <linux/nvram.h> | ||
38 | #include <linux/tty.h> | ||
39 | #include <linux/vt_kern.h> | ||
40 | #include <asm/dbdma.h> | ||
41 | #include <asm/prom.h> | ||
42 | #include <asm/machdep.h> | ||
43 | |||
44 | /* maximum number of fragments */ | ||
45 | #define PMAC_MAX_FRAGS 32 | ||
46 | |||
47 | |||
48 | #define PMAC_SUPPORT_AUTOMUTE | ||
49 | |||
50 | /* | ||
51 | * typedefs | ||
52 | */ | ||
53 | typedef struct snd_pmac pmac_t; | ||
54 | typedef struct snd_pmac_stream pmac_stream_t; | ||
55 | typedef struct snd_pmac_beep pmac_beep_t; | ||
56 | typedef struct snd_pmac_dbdma pmac_dbdma_t; | ||
57 | |||
58 | |||
59 | /* | ||
60 | * DBDMA space | ||
61 | */ | ||
62 | struct snd_pmac_dbdma { | ||
63 | unsigned long addr; | ||
64 | struct dbdma_cmd __iomem *cmds; | ||
65 | void *space; | ||
66 | int size; | ||
67 | }; | ||
68 | |||
69 | /* | ||
70 | * playback/capture stream | ||
71 | */ | ||
72 | struct snd_pmac_stream { | ||
73 | int running; /* boolean */ | ||
74 | |||
75 | int stream; /* PLAYBACK/CAPTURE */ | ||
76 | |||
77 | int dma_size; /* in bytes */ | ||
78 | int period_size; /* in bytes */ | ||
79 | int buffer_size; /* in kbytes */ | ||
80 | int nperiods, cur_period; | ||
81 | |||
82 | pmac_dbdma_t cmd; | ||
83 | volatile struct dbdma_regs __iomem *dma; | ||
84 | |||
85 | snd_pcm_substream_t *substream; | ||
86 | |||
87 | unsigned int cur_freqs; /* currently available frequencies */ | ||
88 | unsigned int cur_formats; /* currently available formats */ | ||
89 | }; | ||
90 | |||
91 | |||
92 | /* | ||
93 | */ | ||
94 | |||
95 | enum snd_pmac_model { | ||
96 | PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER, PMAC_SNAPPER | ||
97 | }; | ||
98 | |||
99 | struct snd_pmac { | ||
100 | snd_card_t *card; | ||
101 | |||
102 | /* h/w info */ | ||
103 | struct device_node *node; | ||
104 | unsigned int revision; | ||
105 | unsigned int manufacturer; | ||
106 | unsigned int subframe; | ||
107 | unsigned int device_id; | ||
108 | enum snd_pmac_model model; | ||
109 | |||
110 | unsigned int has_iic : 1; | ||
111 | unsigned int is_pbook_3400 : 1; | ||
112 | unsigned int is_pbook_G3 : 1; | ||
113 | |||
114 | unsigned int can_byte_swap : 1; | ||
115 | unsigned int can_duplex : 1; | ||
116 | unsigned int can_capture : 1; | ||
117 | |||
118 | unsigned int auto_mute : 1; | ||
119 | unsigned int initialized : 1; | ||
120 | unsigned int feature_is_set : 1; | ||
121 | |||
122 | unsigned int of_requested; | ||
123 | |||
124 | int num_freqs; | ||
125 | int *freq_table; | ||
126 | unsigned int freqs_ok; /* bit flags */ | ||
127 | unsigned int formats_ok; /* pcm hwinfo */ | ||
128 | int active; | ||
129 | int rate_index; | ||
130 | int format; /* current format */ | ||
131 | |||
132 | spinlock_t reg_lock; | ||
133 | volatile struct awacs_regs __iomem *awacs; | ||
134 | int awacs_reg[8]; /* register cache */ | ||
135 | unsigned int hp_stat_mask; | ||
136 | |||
137 | unsigned char __iomem *latch_base; | ||
138 | unsigned char __iomem *macio_base; | ||
139 | |||
140 | pmac_stream_t playback; | ||
141 | pmac_stream_t capture; | ||
142 | |||
143 | pmac_dbdma_t extra_dma; | ||
144 | |||
145 | int irq, tx_irq, rx_irq; | ||
146 | |||
147 | snd_pcm_t *pcm; | ||
148 | |||
149 | pmac_beep_t *beep; | ||
150 | |||
151 | unsigned int control_mask; /* control mask */ | ||
152 | |||
153 | /* mixer stuffs */ | ||
154 | void *mixer_data; | ||
155 | void (*mixer_free)(pmac_t *); | ||
156 | snd_kcontrol_t *master_sw_ctl; | ||
157 | snd_kcontrol_t *speaker_sw_ctl; | ||
158 | snd_kcontrol_t *drc_sw_ctl; /* only used for tumbler -ReneR */ | ||
159 | snd_kcontrol_t *hp_detect_ctl; | ||
160 | |||
161 | /* lowlevel callbacks */ | ||
162 | void (*set_format)(pmac_t *chip); | ||
163 | void (*update_automute)(pmac_t *chip, int do_notify); | ||
164 | int (*detect_headphone)(pmac_t *chip); | ||
165 | #ifdef CONFIG_PMAC_PBOOK | ||
166 | void (*suspend)(pmac_t *chip); | ||
167 | void (*resume)(pmac_t *chip); | ||
168 | #endif | ||
169 | |||
170 | }; | ||
171 | |||
172 | |||
173 | /* exported functions */ | ||
174 | int snd_pmac_new(snd_card_t *card, pmac_t **chip_return); | ||
175 | int snd_pmac_pcm_new(pmac_t *chip); | ||
176 | int snd_pmac_attach_beep(pmac_t *chip); | ||
177 | void snd_pmac_detach_beep(pmac_t *chip); | ||
178 | void snd_pmac_beep_stop(pmac_t *chip); | ||
179 | unsigned int snd_pmac_rate_index(pmac_t *chip, pmac_stream_t *rec, unsigned int rate); | ||
180 | |||
181 | void snd_pmac_beep_dma_start(pmac_t *chip, int bytes, unsigned long addr, int speed); | ||
182 | void snd_pmac_beep_dma_stop(pmac_t *chip); | ||
183 | |||
184 | /* initialize mixer */ | ||
185 | int snd_pmac_awacs_init(pmac_t *chip); | ||
186 | int snd_pmac_burgundy_init(pmac_t *chip); | ||
187 | int snd_pmac_daca_init(pmac_t *chip); | ||
188 | int snd_pmac_tumbler_init(pmac_t *chip); | ||
189 | int snd_pmac_tumbler_post_init(void); | ||
190 | |||
191 | /* i2c functions */ | ||
192 | typedef struct snd_pmac_keywest { | ||
193 | int addr; | ||
194 | struct i2c_client *client; | ||
195 | int id; | ||
196 | int (*init_client)(struct snd_pmac_keywest *i2c); | ||
197 | char *name; | ||
198 | } pmac_keywest_t; | ||
199 | |||
200 | int snd_pmac_keywest_init(pmac_keywest_t *i2c); | ||
201 | void snd_pmac_keywest_cleanup(pmac_keywest_t *i2c); | ||
202 | |||
203 | /* misc */ | ||
204 | int snd_pmac_boolean_stereo_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo); | ||
205 | int snd_pmac_boolean_mono_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo); | ||
206 | |||
207 | int snd_pmac_add_automute(pmac_t *chip); | ||
208 | |||
209 | #define big_mdelay(msec) do {\ | ||
210 | set_current_state(TASK_UNINTERRUPTIBLE);\ | ||
211 | schedule_timeout(((msec) * HZ + 999) / 1000);\ | ||
212 | } while (0) | ||
213 | |||
214 | #endif /* __PMAC_H */ | ||
diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c new file mode 100644 index 000000000000..8f1953a8e290 --- /dev/null +++ b/sound/ppc/powermac.c | |||
@@ -0,0 +1,159 @@ | |||
1 | /* | ||
2 | * Driver for PowerMac AWACS | ||
3 | * Copyright (c) 2001 by Takashi Iwai <tiwai@suse.de> | ||
4 | * based on dmasound.c. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <sound/driver.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/moduleparam.h> | ||
24 | #include <sound/core.h> | ||
25 | #include <sound/initval.h> | ||
26 | #include "pmac.h" | ||
27 | #include "awacs.h" | ||
28 | #include "burgundy.h" | ||
29 | |||
30 | #define CHIP_NAME "PMac" | ||
31 | |||
32 | MODULE_DESCRIPTION("PowerMac"); | ||
33 | MODULE_SUPPORTED_DEVICE("{{Apple,PowerMac}}"); | ||
34 | MODULE_LICENSE("GPL"); | ||
35 | |||
36 | static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ | ||
37 | static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ | ||
38 | static int enable_beep = 1; | ||
39 | |||
40 | module_param(index, int, 0444); | ||
41 | MODULE_PARM_DESC(index, "Index value for " CHIP_NAME " soundchip."); | ||
42 | module_param(id, charp, 0444); | ||
43 | MODULE_PARM_DESC(id, "ID string for " CHIP_NAME " soundchip."); | ||
44 | module_param(enable_beep, bool, 0444); | ||
45 | MODULE_PARM_DESC(enable_beep, "Enable beep using PCM."); | ||
46 | |||
47 | |||
48 | /* | ||
49 | * card entry | ||
50 | */ | ||
51 | |||
52 | static snd_card_t *snd_pmac_card = NULL; | ||
53 | |||
54 | /* | ||
55 | */ | ||
56 | |||
57 | static int __init snd_pmac_probe(void) | ||
58 | { | ||
59 | snd_card_t *card; | ||
60 | pmac_t *chip; | ||
61 | char *name_ext; | ||
62 | int err; | ||
63 | |||
64 | card = snd_card_new(index, id, THIS_MODULE, 0); | ||
65 | if (card == NULL) | ||
66 | return -ENOMEM; | ||
67 | |||
68 | if ((err = snd_pmac_new(card, &chip)) < 0) | ||
69 | goto __error; | ||
70 | |||
71 | switch (chip->model) { | ||
72 | case PMAC_BURGUNDY: | ||
73 | strcpy(card->driver, "PMac Burgundy"); | ||
74 | strcpy(card->shortname, "PowerMac Burgundy"); | ||
75 | sprintf(card->longname, "%s (Dev %d) Sub-frame %d", | ||
76 | card->shortname, chip->device_id, chip->subframe); | ||
77 | if ((err = snd_pmac_burgundy_init(chip)) < 0) | ||
78 | goto __error; | ||
79 | break; | ||
80 | case PMAC_DACA: | ||
81 | strcpy(card->driver, "PMac DACA"); | ||
82 | strcpy(card->shortname, "PowerMac DACA"); | ||
83 | sprintf(card->longname, "%s (Dev %d) Sub-frame %d", | ||
84 | card->shortname, chip->device_id, chip->subframe); | ||
85 | if ((err = snd_pmac_daca_init(chip)) < 0) | ||
86 | goto __error; | ||
87 | break; | ||
88 | case PMAC_TUMBLER: | ||
89 | case PMAC_SNAPPER: | ||
90 | name_ext = chip->model == PMAC_TUMBLER ? "Tumbler" : "Snapper"; | ||
91 | sprintf(card->driver, "PMac %s", name_ext); | ||
92 | sprintf(card->shortname, "PowerMac %s", name_ext); | ||
93 | sprintf(card->longname, "%s (Dev %d) Sub-frame %d", | ||
94 | card->shortname, chip->device_id, chip->subframe); | ||
95 | if ( snd_pmac_tumbler_init(chip) < 0 || snd_pmac_tumbler_post_init() < 0) | ||
96 | goto __error; | ||
97 | break; | ||
98 | case PMAC_AWACS: | ||
99 | case PMAC_SCREAMER: | ||
100 | name_ext = chip->model == PMAC_SCREAMER ? "Screamer" : "AWACS"; | ||
101 | sprintf(card->driver, "PMac %s", name_ext); | ||
102 | sprintf(card->shortname, "PowerMac %s", name_ext); | ||
103 | if (chip->is_pbook_3400) | ||
104 | name_ext = " [PB3400]"; | ||
105 | else if (chip->is_pbook_G3) | ||
106 | name_ext = " [PBG3]"; | ||
107 | else | ||
108 | name_ext = ""; | ||
109 | sprintf(card->longname, "%s%s Rev %d", | ||
110 | card->shortname, name_ext, chip->revision); | ||
111 | if ((err = snd_pmac_awacs_init(chip)) < 0) | ||
112 | goto __error; | ||
113 | break; | ||
114 | default: | ||
115 | snd_printk("unsupported hardware %d\n", chip->model); | ||
116 | err = -EINVAL; | ||
117 | goto __error; | ||
118 | } | ||
119 | |||
120 | if ((err = snd_pmac_pcm_new(chip)) < 0) | ||
121 | goto __error; | ||
122 | |||
123 | chip->initialized = 1; | ||
124 | if (enable_beep) | ||
125 | snd_pmac_attach_beep(chip); | ||
126 | |||
127 | if ((err = snd_card_register(card)) < 0) | ||
128 | goto __error; | ||
129 | |||
130 | snd_pmac_card = card; | ||
131 | return 0; | ||
132 | |||
133 | __error: | ||
134 | snd_card_free(card); | ||
135 | return err; | ||
136 | } | ||
137 | |||
138 | |||
139 | /* | ||
140 | * MODULE stuff | ||
141 | */ | ||
142 | |||
143 | static int __init alsa_card_pmac_init(void) | ||
144 | { | ||
145 | int err; | ||
146 | if ((err = snd_pmac_probe()) < 0) | ||
147 | return err; | ||
148 | return 0; | ||
149 | |||
150 | } | ||
151 | |||
152 | static void __exit alsa_card_pmac_exit(void) | ||
153 | { | ||
154 | if (snd_pmac_card) | ||
155 | snd_card_free(snd_pmac_card); | ||
156 | } | ||
157 | |||
158 | module_init(alsa_card_pmac_init) | ||
159 | module_exit(alsa_card_pmac_exit) | ||
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c new file mode 100644 index 000000000000..7d10385f0a76 --- /dev/null +++ b/sound/ppc/tumbler.c | |||
@@ -0,0 +1,1175 @@ | |||
1 | /* | ||
2 | * PMac Tumbler/Snapper lowlevel functions | ||
3 | * | ||
4 | * Copyright (c) by Takashi Iwai <tiwai@suse.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | * | ||
20 | * Rene Rebe <rene.rebe@gmx.net>: | ||
21 | * * update from shadow registers on wakeup and headphone plug | ||
22 | * * automatically toggle DRC on headphone plug | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | |||
27 | #include <sound/driver.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/delay.h> | ||
30 | #include <linux/i2c.h> | ||
31 | #include <linux/i2c-dev.h> | ||
32 | #include <linux/kmod.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/interrupt.h> | ||
35 | #include <sound/core.h> | ||
36 | #include <asm/io.h> | ||
37 | #include <asm/irq.h> | ||
38 | #ifdef CONFIG_PPC_HAS_FEATURE_CALLS | ||
39 | #include <asm/pmac_feature.h> | ||
40 | #endif | ||
41 | #include "pmac.h" | ||
42 | #include "tumbler_volume.h" | ||
43 | |||
44 | /* i2c address for tumbler */ | ||
45 | #define TAS_I2C_ADDR 0x34 | ||
46 | |||
47 | /* registers */ | ||
48 | #define TAS_REG_MCS 0x01 /* main control */ | ||
49 | #define TAS_REG_DRC 0x02 | ||
50 | #define TAS_REG_VOL 0x04 | ||
51 | #define TAS_REG_TREBLE 0x05 | ||
52 | #define TAS_REG_BASS 0x06 | ||
53 | #define TAS_REG_INPUT1 0x07 | ||
54 | #define TAS_REG_INPUT2 0x08 | ||
55 | |||
56 | /* tas3001c */ | ||
57 | #define TAS_REG_PCM TAS_REG_INPUT1 | ||
58 | |||
59 | /* tas3004 */ | ||
60 | #define TAS_REG_LMIX TAS_REG_INPUT1 | ||
61 | #define TAS_REG_RMIX TAS_REG_INPUT2 | ||
62 | #define TAS_REG_MCS2 0x43 /* main control 2 */ | ||
63 | #define TAS_REG_ACS 0x40 /* analog control */ | ||
64 | |||
65 | /* mono volumes for tas3001c/tas3004 */ | ||
66 | enum { | ||
67 | VOL_IDX_PCM_MONO, /* tas3001c only */ | ||
68 | VOL_IDX_BASS, VOL_IDX_TREBLE, | ||
69 | VOL_IDX_LAST_MONO | ||
70 | }; | ||
71 | |||
72 | /* stereo volumes for tas3004 */ | ||
73 | enum { | ||
74 | VOL_IDX_PCM, VOL_IDX_PCM2, VOL_IDX_ADC, | ||
75 | VOL_IDX_LAST_MIX | ||
76 | }; | ||
77 | |||
78 | typedef struct pmac_gpio { | ||
79 | #ifdef CONFIG_PPC_HAS_FEATURE_CALLS | ||
80 | unsigned int addr; | ||
81 | #else | ||
82 | void __iomem *addr; | ||
83 | #endif | ||
84 | int active_state; | ||
85 | } pmac_gpio_t; | ||
86 | |||
87 | typedef struct pmac_tumbler_t { | ||
88 | pmac_keywest_t i2c; | ||
89 | pmac_gpio_t audio_reset; | ||
90 | pmac_gpio_t amp_mute; | ||
91 | pmac_gpio_t hp_mute; | ||
92 | pmac_gpio_t hp_detect; | ||
93 | int headphone_irq; | ||
94 | unsigned int master_vol[2]; | ||
95 | unsigned int master_switch[2]; | ||
96 | unsigned int mono_vol[VOL_IDX_LAST_MONO]; | ||
97 | unsigned int mix_vol[VOL_IDX_LAST_MIX][2]; /* stereo volumes for tas3004 */ | ||
98 | int drc_range; | ||
99 | int drc_enable; | ||
100 | int capture_source; | ||
101 | } pmac_tumbler_t; | ||
102 | |||
103 | |||
104 | /* | ||
105 | */ | ||
106 | |||
107 | static int send_init_client(pmac_keywest_t *i2c, unsigned int *regs) | ||
108 | { | ||
109 | while (*regs > 0) { | ||
110 | int err, count = 10; | ||
111 | do { | ||
112 | err = i2c_smbus_write_byte_data(i2c->client, | ||
113 | regs[0], regs[1]); | ||
114 | if (err >= 0) | ||
115 | break; | ||
116 | mdelay(10); | ||
117 | } while (count--); | ||
118 | if (err < 0) | ||
119 | return -ENXIO; | ||
120 | regs += 2; | ||
121 | } | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | |||
126 | static int tumbler_init_client(pmac_keywest_t *i2c) | ||
127 | { | ||
128 | static unsigned int regs[] = { | ||
129 | /* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */ | ||
130 | TAS_REG_MCS, (1<<6)|(2<<4)|(2<<2)|0, | ||
131 | 0, /* terminator */ | ||
132 | }; | ||
133 | return send_init_client(i2c, regs); | ||
134 | } | ||
135 | |||
136 | static int snapper_init_client(pmac_keywest_t *i2c) | ||
137 | { | ||
138 | static unsigned int regs[] = { | ||
139 | /* normal operation, SCLK=64fps, i2s output, 16bit width */ | ||
140 | TAS_REG_MCS, (1<<6)|(2<<4)|0, | ||
141 | /* normal operation, all-pass mode */ | ||
142 | TAS_REG_MCS2, (1<<1), | ||
143 | /* normal output, no deemphasis, A input, power-up, line-in */ | ||
144 | TAS_REG_ACS, 0, | ||
145 | 0, /* terminator */ | ||
146 | }; | ||
147 | return send_init_client(i2c, regs); | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * gpio access | ||
152 | */ | ||
153 | #ifdef CONFIG_PPC_HAS_FEATURE_CALLS | ||
154 | #define do_gpio_write(gp, val) \ | ||
155 | pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, (gp)->addr, val) | ||
156 | #define do_gpio_read(gp) \ | ||
157 | pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, (gp)->addr, 0) | ||
158 | #define tumbler_gpio_free(gp) /* NOP */ | ||
159 | #else | ||
160 | #define do_gpio_write(gp, val) writeb(val, (gp)->addr) | ||
161 | #define do_gpio_read(gp) readb((gp)->addr) | ||
162 | static inline void tumbler_gpio_free(pmac_gpio_t *gp) | ||
163 | { | ||
164 | if (gp->addr) { | ||
165 | iounmap(gp->addr); | ||
166 | gp->addr = NULL; | ||
167 | } | ||
168 | } | ||
169 | #endif /* CONFIG_PPC_HAS_FEATURE_CALLS */ | ||
170 | |||
171 | static void write_audio_gpio(pmac_gpio_t *gp, int active) | ||
172 | { | ||
173 | if (! gp->addr) | ||
174 | return; | ||
175 | active = active ? gp->active_state : !gp->active_state; | ||
176 | do_gpio_write(gp, active ? 0x05 : 0x04); | ||
177 | } | ||
178 | |||
179 | static int read_audio_gpio(pmac_gpio_t *gp) | ||
180 | { | ||
181 | int ret; | ||
182 | if (! gp->addr) | ||
183 | return 0; | ||
184 | ret = ((do_gpio_read(gp) & 0x02) !=0); | ||
185 | return ret == gp->active_state; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * update master volume | ||
190 | */ | ||
191 | static int tumbler_set_master_volume(pmac_tumbler_t *mix) | ||
192 | { | ||
193 | unsigned char block[6]; | ||
194 | unsigned int left_vol, right_vol; | ||
195 | |||
196 | if (! mix->i2c.client) | ||
197 | return -ENODEV; | ||
198 | |||
199 | if (! mix->master_switch[0]) | ||
200 | left_vol = 0; | ||
201 | else { | ||
202 | left_vol = mix->master_vol[0]; | ||
203 | if (left_vol >= ARRAY_SIZE(master_volume_table)) | ||
204 | left_vol = ARRAY_SIZE(master_volume_table) - 1; | ||
205 | left_vol = master_volume_table[left_vol]; | ||
206 | } | ||
207 | if (! mix->master_switch[1]) | ||
208 | right_vol = 0; | ||
209 | else { | ||
210 | right_vol = mix->master_vol[1]; | ||
211 | if (right_vol >= ARRAY_SIZE(master_volume_table)) | ||
212 | right_vol = ARRAY_SIZE(master_volume_table) - 1; | ||
213 | right_vol = master_volume_table[right_vol]; | ||
214 | } | ||
215 | |||
216 | block[0] = (left_vol >> 16) & 0xff; | ||
217 | block[1] = (left_vol >> 8) & 0xff; | ||
218 | block[2] = (left_vol >> 0) & 0xff; | ||
219 | |||
220 | block[3] = (right_vol >> 16) & 0xff; | ||
221 | block[4] = (right_vol >> 8) & 0xff; | ||
222 | block[5] = (right_vol >> 0) & 0xff; | ||
223 | |||
224 | if (i2c_smbus_write_block_data(mix->i2c.client, TAS_REG_VOL, | ||
225 | 6, block) < 0) { | ||
226 | snd_printk("failed to set volume \n"); | ||
227 | return -EINVAL; | ||
228 | } | ||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | |||
233 | /* output volume */ | ||
234 | static int tumbler_info_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
235 | { | ||
236 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
237 | uinfo->count = 2; | ||
238 | uinfo->value.integer.min = 0; | ||
239 | uinfo->value.integer.max = ARRAY_SIZE(master_volume_table) - 1; | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | static int tumbler_get_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
244 | { | ||
245 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
246 | pmac_tumbler_t *mix = chip->mixer_data; | ||
247 | snd_assert(mix, return -ENODEV); | ||
248 | ucontrol->value.integer.value[0] = mix->master_vol[0]; | ||
249 | ucontrol->value.integer.value[1] = mix->master_vol[1]; | ||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static int tumbler_put_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
254 | { | ||
255 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
256 | pmac_tumbler_t *mix = chip->mixer_data; | ||
257 | int change; | ||
258 | |||
259 | snd_assert(mix, return -ENODEV); | ||
260 | change = mix->master_vol[0] != ucontrol->value.integer.value[0] || | ||
261 | mix->master_vol[1] != ucontrol->value.integer.value[1]; | ||
262 | if (change) { | ||
263 | mix->master_vol[0] = ucontrol->value.integer.value[0]; | ||
264 | mix->master_vol[1] = ucontrol->value.integer.value[1]; | ||
265 | tumbler_set_master_volume(mix); | ||
266 | } | ||
267 | return change; | ||
268 | } | ||
269 | |||
270 | /* output switch */ | ||
271 | static int tumbler_get_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
272 | { | ||
273 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
274 | pmac_tumbler_t *mix = chip->mixer_data; | ||
275 | snd_assert(mix, return -ENODEV); | ||
276 | ucontrol->value.integer.value[0] = mix->master_switch[0]; | ||
277 | ucontrol->value.integer.value[1] = mix->master_switch[1]; | ||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | static int tumbler_put_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
282 | { | ||
283 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
284 | pmac_tumbler_t *mix = chip->mixer_data; | ||
285 | int change; | ||
286 | |||
287 | snd_assert(mix, return -ENODEV); | ||
288 | change = mix->master_switch[0] != ucontrol->value.integer.value[0] || | ||
289 | mix->master_switch[1] != ucontrol->value.integer.value[1]; | ||
290 | if (change) { | ||
291 | mix->master_switch[0] = !!ucontrol->value.integer.value[0]; | ||
292 | mix->master_switch[1] = !!ucontrol->value.integer.value[1]; | ||
293 | tumbler_set_master_volume(mix); | ||
294 | } | ||
295 | return change; | ||
296 | } | ||
297 | |||
298 | |||
299 | /* | ||
300 | * TAS3001c dynamic range compression | ||
301 | */ | ||
302 | |||
303 | #define TAS3001_DRC_MAX 0x5f | ||
304 | |||
305 | static int tumbler_set_drc(pmac_tumbler_t *mix) | ||
306 | { | ||
307 | unsigned char val[2]; | ||
308 | |||
309 | if (! mix->i2c.client) | ||
310 | return -ENODEV; | ||
311 | |||
312 | if (mix->drc_enable) { | ||
313 | val[0] = 0xc1; /* enable, 3:1 compression */ | ||
314 | if (mix->drc_range > TAS3001_DRC_MAX) | ||
315 | val[1] = 0xf0; | ||
316 | else if (mix->drc_range < 0) | ||
317 | val[1] = 0x91; | ||
318 | else | ||
319 | val[1] = mix->drc_range + 0x91; | ||
320 | } else { | ||
321 | val[0] = 0; | ||
322 | val[1] = 0; | ||
323 | } | ||
324 | |||
325 | if (i2c_smbus_write_block_data(mix->i2c.client, TAS_REG_DRC, | ||
326 | 2, val) < 0) { | ||
327 | snd_printk("failed to set DRC\n"); | ||
328 | return -EINVAL; | ||
329 | } | ||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | /* | ||
334 | * TAS3004 | ||
335 | */ | ||
336 | |||
337 | #define TAS3004_DRC_MAX 0xef | ||
338 | |||
339 | static int snapper_set_drc(pmac_tumbler_t *mix) | ||
340 | { | ||
341 | unsigned char val[6]; | ||
342 | |||
343 | if (! mix->i2c.client) | ||
344 | return -ENODEV; | ||
345 | |||
346 | if (mix->drc_enable) | ||
347 | val[0] = 0x50; /* 3:1 above threshold */ | ||
348 | else | ||
349 | val[0] = 0x51; /* disabled */ | ||
350 | val[1] = 0x02; /* 1:1 below threshold */ | ||
351 | if (mix->drc_range > 0xef) | ||
352 | val[2] = 0xef; | ||
353 | else if (mix->drc_range < 0) | ||
354 | val[2] = 0x00; | ||
355 | else | ||
356 | val[2] = mix->drc_range; | ||
357 | val[3] = 0xb0; | ||
358 | val[4] = 0x60; | ||
359 | val[5] = 0xa0; | ||
360 | |||
361 | if (i2c_smbus_write_block_data(mix->i2c.client, TAS_REG_DRC, | ||
362 | 6, val) < 0) { | ||
363 | snd_printk("failed to set DRC\n"); | ||
364 | return -EINVAL; | ||
365 | } | ||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | static int tumbler_info_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
370 | { | ||
371 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
372 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
373 | uinfo->count = 1; | ||
374 | uinfo->value.integer.min = 0; | ||
375 | uinfo->value.integer.max = | ||
376 | chip->model == PMAC_TUMBLER ? TAS3001_DRC_MAX : TAS3004_DRC_MAX; | ||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static int tumbler_get_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
381 | { | ||
382 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
383 | pmac_tumbler_t *mix; | ||
384 | if (! (mix = chip->mixer_data)) | ||
385 | return -ENODEV; | ||
386 | ucontrol->value.integer.value[0] = mix->drc_range; | ||
387 | return 0; | ||
388 | } | ||
389 | |||
390 | static int tumbler_put_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
391 | { | ||
392 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
393 | pmac_tumbler_t *mix; | ||
394 | int change; | ||
395 | |||
396 | if (! (mix = chip->mixer_data)) | ||
397 | return -ENODEV; | ||
398 | change = mix->drc_range != ucontrol->value.integer.value[0]; | ||
399 | if (change) { | ||
400 | mix->drc_range = ucontrol->value.integer.value[0]; | ||
401 | if (chip->model == PMAC_TUMBLER) | ||
402 | tumbler_set_drc(mix); | ||
403 | else | ||
404 | snapper_set_drc(mix); | ||
405 | } | ||
406 | return change; | ||
407 | } | ||
408 | |||
409 | static int tumbler_get_drc_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
410 | { | ||
411 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
412 | pmac_tumbler_t *mix; | ||
413 | if (! (mix = chip->mixer_data)) | ||
414 | return -ENODEV; | ||
415 | ucontrol->value.integer.value[0] = mix->drc_enable; | ||
416 | return 0; | ||
417 | } | ||
418 | |||
419 | static int tumbler_put_drc_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
420 | { | ||
421 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
422 | pmac_tumbler_t *mix; | ||
423 | int change; | ||
424 | |||
425 | if (! (mix = chip->mixer_data)) | ||
426 | return -ENODEV; | ||
427 | change = mix->drc_enable != ucontrol->value.integer.value[0]; | ||
428 | if (change) { | ||
429 | mix->drc_enable = !!ucontrol->value.integer.value[0]; | ||
430 | if (chip->model == PMAC_TUMBLER) | ||
431 | tumbler_set_drc(mix); | ||
432 | else | ||
433 | snapper_set_drc(mix); | ||
434 | } | ||
435 | return change; | ||
436 | } | ||
437 | |||
438 | |||
439 | /* | ||
440 | * mono volumes | ||
441 | */ | ||
442 | |||
443 | struct tumbler_mono_vol { | ||
444 | int index; | ||
445 | int reg; | ||
446 | int bytes; | ||
447 | unsigned int max; | ||
448 | unsigned int *table; | ||
449 | }; | ||
450 | |||
451 | static int tumbler_set_mono_volume(pmac_tumbler_t *mix, struct tumbler_mono_vol *info) | ||
452 | { | ||
453 | unsigned char block[4]; | ||
454 | unsigned int vol; | ||
455 | int i; | ||
456 | |||
457 | if (! mix->i2c.client) | ||
458 | return -ENODEV; | ||
459 | |||
460 | vol = mix->mono_vol[info->index]; | ||
461 | if (vol >= info->max) | ||
462 | vol = info->max - 1; | ||
463 | vol = info->table[vol]; | ||
464 | for (i = 0; i < info->bytes; i++) | ||
465 | block[i] = (vol >> ((info->bytes - i - 1) * 8)) & 0xff; | ||
466 | if (i2c_smbus_write_block_data(mix->i2c.client, info->reg, | ||
467 | info->bytes, block) < 0) { | ||
468 | snd_printk("failed to set mono volume %d\n", info->index); | ||
469 | return -EINVAL; | ||
470 | } | ||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static int tumbler_info_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
475 | { | ||
476 | struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; | ||
477 | |||
478 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
479 | uinfo->count = 1; | ||
480 | uinfo->value.integer.min = 0; | ||
481 | uinfo->value.integer.max = info->max - 1; | ||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | static int tumbler_get_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
486 | { | ||
487 | struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; | ||
488 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
489 | pmac_tumbler_t *mix; | ||
490 | if (! (mix = chip->mixer_data)) | ||
491 | return -ENODEV; | ||
492 | ucontrol->value.integer.value[0] = mix->mono_vol[info->index]; | ||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | static int tumbler_put_mono(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
497 | { | ||
498 | struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value; | ||
499 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
500 | pmac_tumbler_t *mix; | ||
501 | int change; | ||
502 | |||
503 | if (! (mix = chip->mixer_data)) | ||
504 | return -ENODEV; | ||
505 | change = mix->mono_vol[info->index] != ucontrol->value.integer.value[0]; | ||
506 | if (change) { | ||
507 | mix->mono_vol[info->index] = ucontrol->value.integer.value[0]; | ||
508 | tumbler_set_mono_volume(mix, info); | ||
509 | } | ||
510 | return change; | ||
511 | } | ||
512 | |||
513 | /* TAS3001c mono volumes */ | ||
514 | static struct tumbler_mono_vol tumbler_pcm_vol_info = { | ||
515 | .index = VOL_IDX_PCM_MONO, | ||
516 | .reg = TAS_REG_PCM, | ||
517 | .bytes = 3, | ||
518 | .max = ARRAY_SIZE(mixer_volume_table), | ||
519 | .table = mixer_volume_table, | ||
520 | }; | ||
521 | |||
522 | static struct tumbler_mono_vol tumbler_bass_vol_info = { | ||
523 | .index = VOL_IDX_BASS, | ||
524 | .reg = TAS_REG_BASS, | ||
525 | .bytes = 1, | ||
526 | .max = ARRAY_SIZE(bass_volume_table), | ||
527 | .table = bass_volume_table, | ||
528 | }; | ||
529 | |||
530 | static struct tumbler_mono_vol tumbler_treble_vol_info = { | ||
531 | .index = VOL_IDX_TREBLE, | ||
532 | .reg = TAS_REG_TREBLE, | ||
533 | .bytes = 1, | ||
534 | .max = ARRAY_SIZE(treble_volume_table), | ||
535 | .table = treble_volume_table, | ||
536 | }; | ||
537 | |||
538 | /* TAS3004 mono volumes */ | ||
539 | static struct tumbler_mono_vol snapper_bass_vol_info = { | ||
540 | .index = VOL_IDX_BASS, | ||
541 | .reg = TAS_REG_BASS, | ||
542 | .bytes = 1, | ||
543 | .max = ARRAY_SIZE(snapper_bass_volume_table), | ||
544 | .table = snapper_bass_volume_table, | ||
545 | }; | ||
546 | |||
547 | static struct tumbler_mono_vol snapper_treble_vol_info = { | ||
548 | .index = VOL_IDX_TREBLE, | ||
549 | .reg = TAS_REG_TREBLE, | ||
550 | .bytes = 1, | ||
551 | .max = ARRAY_SIZE(snapper_treble_volume_table), | ||
552 | .table = snapper_treble_volume_table, | ||
553 | }; | ||
554 | |||
555 | |||
556 | #define DEFINE_MONO(xname,type) { \ | ||
557 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ | ||
558 | .name = xname, \ | ||
559 | .info = tumbler_info_mono, \ | ||
560 | .get = tumbler_get_mono, \ | ||
561 | .put = tumbler_put_mono, \ | ||
562 | .private_value = (unsigned long)(&tumbler_##type##_vol_info), \ | ||
563 | } | ||
564 | |||
565 | #define DEFINE_SNAPPER_MONO(xname,type) { \ | ||
566 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ | ||
567 | .name = xname, \ | ||
568 | .info = tumbler_info_mono, \ | ||
569 | .get = tumbler_get_mono, \ | ||
570 | .put = tumbler_put_mono, \ | ||
571 | .private_value = (unsigned long)(&snapper_##type##_vol_info), \ | ||
572 | } | ||
573 | |||
574 | |||
575 | /* | ||
576 | * snapper mixer volumes | ||
577 | */ | ||
578 | |||
579 | static int snapper_set_mix_vol1(pmac_tumbler_t *mix, int idx, int ch, int reg) | ||
580 | { | ||
581 | int i, j, vol; | ||
582 | unsigned char block[9]; | ||
583 | |||
584 | vol = mix->mix_vol[idx][ch]; | ||
585 | if (vol >= ARRAY_SIZE(mixer_volume_table)) { | ||
586 | vol = ARRAY_SIZE(mixer_volume_table) - 1; | ||
587 | mix->mix_vol[idx][ch] = vol; | ||
588 | } | ||
589 | |||
590 | for (i = 0; i < 3; i++) { | ||
591 | vol = mix->mix_vol[i][ch]; | ||
592 | vol = mixer_volume_table[vol]; | ||
593 | for (j = 0; j < 3; j++) | ||
594 | block[i * 3 + j] = (vol >> ((2 - j) * 8)) & 0xff; | ||
595 | } | ||
596 | if (i2c_smbus_write_block_data(mix->i2c.client, reg, 9, block) < 0) { | ||
597 | snd_printk("failed to set mono volume %d\n", reg); | ||
598 | return -EINVAL; | ||
599 | } | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | static int snapper_set_mix_vol(pmac_tumbler_t *mix, int idx) | ||
604 | { | ||
605 | if (! mix->i2c.client) | ||
606 | return -ENODEV; | ||
607 | if (snapper_set_mix_vol1(mix, idx, 0, TAS_REG_LMIX) < 0 || | ||
608 | snapper_set_mix_vol1(mix, idx, 1, TAS_REG_RMIX) < 0) | ||
609 | return -EINVAL; | ||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | static int snapper_info_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
614 | { | ||
615 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
616 | uinfo->count = 2; | ||
617 | uinfo->value.integer.min = 0; | ||
618 | uinfo->value.integer.max = ARRAY_SIZE(mixer_volume_table) - 1; | ||
619 | return 0; | ||
620 | } | ||
621 | |||
622 | static int snapper_get_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
623 | { | ||
624 | int idx = (int)kcontrol->private_value; | ||
625 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
626 | pmac_tumbler_t *mix; | ||
627 | if (! (mix = chip->mixer_data)) | ||
628 | return -ENODEV; | ||
629 | ucontrol->value.integer.value[0] = mix->mix_vol[idx][0]; | ||
630 | ucontrol->value.integer.value[1] = mix->mix_vol[idx][1]; | ||
631 | return 0; | ||
632 | } | ||
633 | |||
634 | static int snapper_put_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
635 | { | ||
636 | int idx = (int)kcontrol->private_value; | ||
637 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
638 | pmac_tumbler_t *mix; | ||
639 | int change; | ||
640 | |||
641 | if (! (mix = chip->mixer_data)) | ||
642 | return -ENODEV; | ||
643 | change = mix->mix_vol[idx][0] != ucontrol->value.integer.value[0] || | ||
644 | mix->mix_vol[idx][1] != ucontrol->value.integer.value[1]; | ||
645 | if (change) { | ||
646 | mix->mix_vol[idx][0] = ucontrol->value.integer.value[0]; | ||
647 | mix->mix_vol[idx][1] = ucontrol->value.integer.value[1]; | ||
648 | snapper_set_mix_vol(mix, idx); | ||
649 | } | ||
650 | return change; | ||
651 | } | ||
652 | |||
653 | |||
654 | /* | ||
655 | * mute switches | ||
656 | */ | ||
657 | |||
658 | enum { TUMBLER_MUTE_HP, TUMBLER_MUTE_AMP }; | ||
659 | |||
660 | static int tumbler_get_mute_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
661 | { | ||
662 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
663 | pmac_tumbler_t *mix; | ||
664 | pmac_gpio_t *gp; | ||
665 | if (! (mix = chip->mixer_data)) | ||
666 | return -ENODEV; | ||
667 | gp = (kcontrol->private_value == TUMBLER_MUTE_HP) ? &mix->hp_mute : &mix->amp_mute; | ||
668 | ucontrol->value.integer.value[0] = ! read_audio_gpio(gp); | ||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | static int tumbler_put_mute_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
673 | { | ||
674 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
675 | pmac_tumbler_t *mix; | ||
676 | pmac_gpio_t *gp; | ||
677 | int val; | ||
678 | #ifdef PMAC_SUPPORT_AUTOMUTE | ||
679 | if (chip->update_automute && chip->auto_mute) | ||
680 | return 0; /* don't touch in the auto-mute mode */ | ||
681 | #endif | ||
682 | if (! (mix = chip->mixer_data)) | ||
683 | return -ENODEV; | ||
684 | gp = (kcontrol->private_value == TUMBLER_MUTE_HP) ? &mix->hp_mute : &mix->amp_mute; | ||
685 | val = ! read_audio_gpio(gp); | ||
686 | if (val != ucontrol->value.integer.value[0]) { | ||
687 | write_audio_gpio(gp, ! ucontrol->value.integer.value[0]); | ||
688 | return 1; | ||
689 | } | ||
690 | return 0; | ||
691 | } | ||
692 | |||
693 | static int snapper_set_capture_source(pmac_tumbler_t *mix) | ||
694 | { | ||
695 | if (! mix->i2c.client) | ||
696 | return -ENODEV; | ||
697 | return i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, | ||
698 | mix->capture_source ? 2 : 0); | ||
699 | } | ||
700 | |||
701 | static int snapper_info_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo) | ||
702 | { | ||
703 | static char *texts[2] = { | ||
704 | "Line", "Mic" | ||
705 | }; | ||
706 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | ||
707 | uinfo->count = 1; | ||
708 | uinfo->value.enumerated.items = 2; | ||
709 | if (uinfo->value.enumerated.item > 1) | ||
710 | uinfo->value.enumerated.item = 1; | ||
711 | strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); | ||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | static int snapper_get_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
716 | { | ||
717 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
718 | pmac_tumbler_t *mix = chip->mixer_data; | ||
719 | |||
720 | snd_assert(mix, return -ENODEV); | ||
721 | ucontrol->value.integer.value[0] = mix->capture_source; | ||
722 | return 0; | ||
723 | } | ||
724 | |||
725 | static int snapper_put_capture_source(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol) | ||
726 | { | ||
727 | pmac_t *chip = snd_kcontrol_chip(kcontrol); | ||
728 | pmac_tumbler_t *mix = chip->mixer_data; | ||
729 | int change; | ||
730 | |||
731 | snd_assert(mix, return -ENODEV); | ||
732 | change = ucontrol->value.integer.value[0] != mix->capture_source; | ||
733 | if (change) { | ||
734 | mix->capture_source = !!ucontrol->value.integer.value[0]; | ||
735 | snapper_set_capture_source(mix); | ||
736 | } | ||
737 | return change; | ||
738 | } | ||
739 | |||
740 | #define DEFINE_SNAPPER_MIX(xname,idx,ofs) { \ | ||
741 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\ | ||
742 | .name = xname, \ | ||
743 | .info = snapper_info_mix, \ | ||
744 | .get = snapper_get_mix, \ | ||
745 | .put = snapper_put_mix, \ | ||
746 | .index = idx,\ | ||
747 | .private_value = ofs, \ | ||
748 | } | ||
749 | |||
750 | |||
751 | /* | ||
752 | */ | ||
753 | static snd_kcontrol_new_t tumbler_mixers[] __initdata = { | ||
754 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
755 | .name = "Master Playback Volume", | ||
756 | .info = tumbler_info_master_volume, | ||
757 | .get = tumbler_get_master_volume, | ||
758 | .put = tumbler_put_master_volume | ||
759 | }, | ||
760 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
761 | .name = "Master Playback Switch", | ||
762 | .info = snd_pmac_boolean_stereo_info, | ||
763 | .get = tumbler_get_master_switch, | ||
764 | .put = tumbler_put_master_switch | ||
765 | }, | ||
766 | DEFINE_MONO("Tone Control - Bass", bass), | ||
767 | DEFINE_MONO("Tone Control - Treble", treble), | ||
768 | DEFINE_MONO("PCM Playback Volume", pcm), | ||
769 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
770 | .name = "DRC Range", | ||
771 | .info = tumbler_info_drc_value, | ||
772 | .get = tumbler_get_drc_value, | ||
773 | .put = tumbler_put_drc_value | ||
774 | }, | ||
775 | }; | ||
776 | |||
777 | static snd_kcontrol_new_t snapper_mixers[] __initdata = { | ||
778 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
779 | .name = "Master Playback Volume", | ||
780 | .info = tumbler_info_master_volume, | ||
781 | .get = tumbler_get_master_volume, | ||
782 | .put = tumbler_put_master_volume | ||
783 | }, | ||
784 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
785 | .name = "Master Playback Switch", | ||
786 | .info = snd_pmac_boolean_stereo_info, | ||
787 | .get = tumbler_get_master_switch, | ||
788 | .put = tumbler_put_master_switch | ||
789 | }, | ||
790 | DEFINE_SNAPPER_MIX("PCM Playback Volume", 0, VOL_IDX_PCM), | ||
791 | DEFINE_SNAPPER_MIX("PCM Playback Volume", 1, VOL_IDX_PCM2), | ||
792 | DEFINE_SNAPPER_MIX("Monitor Mix Volume", 0, VOL_IDX_ADC), | ||
793 | DEFINE_SNAPPER_MONO("Tone Control - Bass", bass), | ||
794 | DEFINE_SNAPPER_MONO("Tone Control - Treble", treble), | ||
795 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
796 | .name = "DRC Range", | ||
797 | .info = tumbler_info_drc_value, | ||
798 | .get = tumbler_get_drc_value, | ||
799 | .put = tumbler_put_drc_value | ||
800 | }, | ||
801 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
802 | .name = "Input Source", /* FIXME: "Capture Source" doesn't work properly */ | ||
803 | .info = snapper_info_capture_source, | ||
804 | .get = snapper_get_capture_source, | ||
805 | .put = snapper_put_capture_source | ||
806 | }, | ||
807 | }; | ||
808 | |||
809 | static snd_kcontrol_new_t tumbler_hp_sw __initdata = { | ||
810 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
811 | .name = "Headphone Playback Switch", | ||
812 | .info = snd_pmac_boolean_mono_info, | ||
813 | .get = tumbler_get_mute_switch, | ||
814 | .put = tumbler_put_mute_switch, | ||
815 | .private_value = TUMBLER_MUTE_HP, | ||
816 | }; | ||
817 | static snd_kcontrol_new_t tumbler_speaker_sw __initdata = { | ||
818 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
819 | .name = "PC Speaker Playback Switch", | ||
820 | .info = snd_pmac_boolean_mono_info, | ||
821 | .get = tumbler_get_mute_switch, | ||
822 | .put = tumbler_put_mute_switch, | ||
823 | .private_value = TUMBLER_MUTE_AMP, | ||
824 | }; | ||
825 | static snd_kcontrol_new_t tumbler_drc_sw __initdata = { | ||
826 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
827 | .name = "DRC Switch", | ||
828 | .info = snd_pmac_boolean_mono_info, | ||
829 | .get = tumbler_get_drc_switch, | ||
830 | .put = tumbler_put_drc_switch | ||
831 | }; | ||
832 | |||
833 | |||
834 | #ifdef PMAC_SUPPORT_AUTOMUTE | ||
835 | /* | ||
836 | * auto-mute stuffs | ||
837 | */ | ||
838 | static int tumbler_detect_headphone(pmac_t *chip) | ||
839 | { | ||
840 | pmac_tumbler_t *mix = chip->mixer_data; | ||
841 | return read_audio_gpio(&mix->hp_detect); | ||
842 | } | ||
843 | |||
844 | static void check_mute(pmac_t *chip, pmac_gpio_t *gp, int val, int do_notify, snd_kcontrol_t *sw) | ||
845 | { | ||
846 | //pmac_tumbler_t *mix = chip->mixer_data; | ||
847 | if (val != read_audio_gpio(gp)) { | ||
848 | write_audio_gpio(gp, val); | ||
849 | if (do_notify) | ||
850 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, &sw->id); | ||
851 | } | ||
852 | } | ||
853 | |||
854 | static struct work_struct device_change; | ||
855 | |||
856 | static void | ||
857 | device_change_handler(void *self) | ||
858 | { | ||
859 | pmac_t *chip = (pmac_t*) self; | ||
860 | pmac_tumbler_t *mix; | ||
861 | |||
862 | if (!chip) | ||
863 | return; | ||
864 | |||
865 | mix = chip->mixer_data; | ||
866 | |||
867 | /* first set the DRC so the speaker do not explode -ReneR */ | ||
868 | if (chip->model == PMAC_TUMBLER) | ||
869 | tumbler_set_drc(mix); | ||
870 | else | ||
871 | snapper_set_drc(mix); | ||
872 | |||
873 | /* reset the master volume so the correct amplification is applied */ | ||
874 | tumbler_set_master_volume(mix); | ||
875 | } | ||
876 | |||
877 | static void tumbler_update_automute(pmac_t *chip, int do_notify) | ||
878 | { | ||
879 | if (chip->auto_mute) { | ||
880 | pmac_tumbler_t *mix = chip->mixer_data; | ||
881 | snd_assert(mix, return); | ||
882 | if (tumbler_detect_headphone(chip)) { | ||
883 | /* mute speaker */ | ||
884 | check_mute(chip, &mix->amp_mute, 1, do_notify, chip->speaker_sw_ctl); | ||
885 | check_mute(chip, &mix->hp_mute, 0, do_notify, chip->master_sw_ctl); | ||
886 | mix->drc_enable = 0; | ||
887 | |||
888 | } else { | ||
889 | /* unmute speaker */ | ||
890 | check_mute(chip, &mix->amp_mute, 0, do_notify, chip->speaker_sw_ctl); | ||
891 | check_mute(chip, &mix->hp_mute, 1, do_notify, chip->master_sw_ctl); | ||
892 | mix->drc_enable = 1; | ||
893 | } | ||
894 | if (do_notify) { | ||
895 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
896 | &chip->hp_detect_ctl->id); | ||
897 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
898 | &chip->drc_sw_ctl->id); | ||
899 | } | ||
900 | |||
901 | /* finally we need to schedule an update of the mixer values | ||
902 | (master and DRC are enough for now) -ReneR */ | ||
903 | schedule_work(&device_change); | ||
904 | |||
905 | } | ||
906 | } | ||
907 | #endif /* PMAC_SUPPORT_AUTOMUTE */ | ||
908 | |||
909 | |||
910 | /* interrupt - headphone plug changed */ | ||
911 | static irqreturn_t headphone_intr(int irq, void *devid, struct pt_regs *regs) | ||
912 | { | ||
913 | pmac_t *chip = devid; | ||
914 | if (chip->update_automute && chip->initialized) { | ||
915 | chip->update_automute(chip, 1); | ||
916 | return IRQ_HANDLED; | ||
917 | } | ||
918 | return IRQ_NONE; | ||
919 | } | ||
920 | |||
921 | /* look for audio-gpio device */ | ||
922 | static struct device_node *find_audio_device(const char *name) | ||
923 | { | ||
924 | struct device_node *np; | ||
925 | |||
926 | if (! (np = find_devices("gpio"))) | ||
927 | return NULL; | ||
928 | |||
929 | for (np = np->child; np; np = np->sibling) { | ||
930 | char *property = get_property(np, "audio-gpio", NULL); | ||
931 | if (property && strcmp(property, name) == 0) | ||
932 | return np; | ||
933 | } | ||
934 | return NULL; | ||
935 | } | ||
936 | |||
937 | /* look for audio-gpio device */ | ||
938 | static struct device_node *find_compatible_audio_device(const char *name) | ||
939 | { | ||
940 | struct device_node *np; | ||
941 | |||
942 | if (! (np = find_devices("gpio"))) | ||
943 | return NULL; | ||
944 | |||
945 | for (np = np->child; np; np = np->sibling) { | ||
946 | if (device_is_compatible(np, name)) | ||
947 | return np; | ||
948 | } | ||
949 | return NULL; | ||
950 | } | ||
951 | |||
952 | /* find an audio device and get its address */ | ||
953 | static unsigned long tumbler_find_device(const char *device, pmac_gpio_t *gp, int is_compatible) | ||
954 | { | ||
955 | struct device_node *node; | ||
956 | u32 *base; | ||
957 | |||
958 | if (is_compatible) | ||
959 | node = find_compatible_audio_device(device); | ||
960 | else | ||
961 | node = find_audio_device(device); | ||
962 | if (! node) { | ||
963 | snd_printdd("cannot find device %s\n", device); | ||
964 | return -ENODEV; | ||
965 | } | ||
966 | |||
967 | base = (u32 *)get_property(node, "AAPL,address", NULL); | ||
968 | if (! base) { | ||
969 | snd_printd("cannot find address for device %s\n", device); | ||
970 | return -ENODEV; | ||
971 | } | ||
972 | |||
973 | #ifdef CONFIG_PPC_HAS_FEATURE_CALLS | ||
974 | gp->addr = (*base) & 0x0000ffff; | ||
975 | #else | ||
976 | gp->addr = ioremap((unsigned long)(*base), 1); | ||
977 | #endif | ||
978 | base = (u32 *)get_property(node, "audio-gpio-active-state", NULL); | ||
979 | if (base) | ||
980 | gp->active_state = *base; | ||
981 | else | ||
982 | gp->active_state = 1; | ||
983 | |||
984 | |||
985 | return (node->n_intrs > 0) ? node->intrs[0].line : 0; | ||
986 | } | ||
987 | |||
988 | /* reset audio */ | ||
989 | static void tumbler_reset_audio(pmac_t *chip) | ||
990 | { | ||
991 | pmac_tumbler_t *mix = chip->mixer_data; | ||
992 | |||
993 | write_audio_gpio(&mix->audio_reset, 0); | ||
994 | big_mdelay(200); | ||
995 | write_audio_gpio(&mix->audio_reset, 1); | ||
996 | big_mdelay(100); | ||
997 | write_audio_gpio(&mix->audio_reset, 0); | ||
998 | big_mdelay(100); | ||
999 | } | ||
1000 | |||
1001 | #ifdef CONFIG_PMAC_PBOOK | ||
1002 | /* resume mixer */ | ||
1003 | static void tumbler_resume(pmac_t *chip) | ||
1004 | { | ||
1005 | pmac_tumbler_t *mix = chip->mixer_data; | ||
1006 | |||
1007 | snd_assert(mix, return); | ||
1008 | |||
1009 | tumbler_reset_audio(chip); | ||
1010 | if (mix->i2c.client && mix->i2c.init_client) { | ||
1011 | if (mix->i2c.init_client(&mix->i2c) < 0) | ||
1012 | printk(KERN_ERR "tumbler_init_client error\n"); | ||
1013 | } else | ||
1014 | printk(KERN_ERR "tumbler: i2c is not initialized\n"); | ||
1015 | if (chip->model == PMAC_TUMBLER) { | ||
1016 | tumbler_set_mono_volume(mix, &tumbler_pcm_vol_info); | ||
1017 | tumbler_set_mono_volume(mix, &tumbler_bass_vol_info); | ||
1018 | tumbler_set_mono_volume(mix, &tumbler_treble_vol_info); | ||
1019 | tumbler_set_drc(mix); | ||
1020 | } else { | ||
1021 | snapper_set_mix_vol(mix, VOL_IDX_PCM); | ||
1022 | snapper_set_mix_vol(mix, VOL_IDX_PCM2); | ||
1023 | snapper_set_mix_vol(mix, VOL_IDX_ADC); | ||
1024 | tumbler_set_mono_volume(mix, &snapper_bass_vol_info); | ||
1025 | tumbler_set_mono_volume(mix, &snapper_treble_vol_info); | ||
1026 | snapper_set_drc(mix); | ||
1027 | snapper_set_capture_source(mix); | ||
1028 | } | ||
1029 | tumbler_set_master_volume(mix); | ||
1030 | if (chip->update_automute) | ||
1031 | chip->update_automute(chip, 0); | ||
1032 | } | ||
1033 | #endif | ||
1034 | |||
1035 | /* initialize tumbler */ | ||
1036 | static int __init tumbler_init(pmac_t *chip) | ||
1037 | { | ||
1038 | int irq, err; | ||
1039 | pmac_tumbler_t *mix = chip->mixer_data; | ||
1040 | snd_assert(mix, return -EINVAL); | ||
1041 | |||
1042 | tumbler_find_device("audio-hw-reset", &mix->audio_reset, 0); | ||
1043 | tumbler_find_device("amp-mute", &mix->amp_mute, 0); | ||
1044 | tumbler_find_device("headphone-mute", &mix->hp_mute, 0); | ||
1045 | irq = tumbler_find_device("headphone-detect", &mix->hp_detect, 0); | ||
1046 | if (irq < 0) | ||
1047 | irq = tumbler_find_device("keywest-gpio15", &mix->hp_detect, 1); | ||
1048 | |||
1049 | tumbler_reset_audio(chip); | ||
1050 | |||
1051 | /* activate headphone status interrupts */ | ||
1052 | if (irq >= 0) { | ||
1053 | unsigned char val; | ||
1054 | if ((err = request_irq(irq, headphone_intr, 0, | ||
1055 | "Tumbler Headphone Detection", chip)) < 0) | ||
1056 | return err; | ||
1057 | /* activate headphone status interrupts */ | ||
1058 | val = do_gpio_read(&mix->hp_detect); | ||
1059 | do_gpio_write(&mix->hp_detect, val | 0x80); | ||
1060 | } | ||
1061 | mix->headphone_irq = irq; | ||
1062 | |||
1063 | return 0; | ||
1064 | } | ||
1065 | |||
1066 | static void tumbler_cleanup(pmac_t *chip) | ||
1067 | { | ||
1068 | pmac_tumbler_t *mix = chip->mixer_data; | ||
1069 | if (! mix) | ||
1070 | return; | ||
1071 | |||
1072 | if (mix->headphone_irq >= 0) | ||
1073 | free_irq(mix->headphone_irq, chip); | ||
1074 | tumbler_gpio_free(&mix->audio_reset); | ||
1075 | tumbler_gpio_free(&mix->amp_mute); | ||
1076 | tumbler_gpio_free(&mix->hp_mute); | ||
1077 | tumbler_gpio_free(&mix->hp_detect); | ||
1078 | snd_pmac_keywest_cleanup(&mix->i2c); | ||
1079 | kfree(mix); | ||
1080 | chip->mixer_data = NULL; | ||
1081 | } | ||
1082 | |||
1083 | /* exported */ | ||
1084 | int __init snd_pmac_tumbler_init(pmac_t *chip) | ||
1085 | { | ||
1086 | int i, err; | ||
1087 | pmac_tumbler_t *mix; | ||
1088 | u32 *paddr; | ||
1089 | struct device_node *tas_node; | ||
1090 | char *chipname; | ||
1091 | |||
1092 | #ifdef CONFIG_KMOD | ||
1093 | if (current->fs->root) | ||
1094 | request_module("i2c-keywest"); | ||
1095 | #endif /* CONFIG_KMOD */ | ||
1096 | |||
1097 | mix = kmalloc(sizeof(*mix), GFP_KERNEL); | ||
1098 | if (! mix) | ||
1099 | return -ENOMEM; | ||
1100 | memset(mix, 0, sizeof(*mix)); | ||
1101 | mix->headphone_irq = -1; | ||
1102 | |||
1103 | chip->mixer_data = mix; | ||
1104 | chip->mixer_free = tumbler_cleanup; | ||
1105 | |||
1106 | if ((err = tumbler_init(chip)) < 0) | ||
1107 | return err; | ||
1108 | |||
1109 | /* set up TAS */ | ||
1110 | tas_node = find_devices("deq"); | ||
1111 | if (tas_node == NULL) | ||
1112 | return -ENODEV; | ||
1113 | |||
1114 | paddr = (u32 *)get_property(tas_node, "i2c-address", NULL); | ||
1115 | if (paddr) | ||
1116 | mix->i2c.addr = (*paddr) >> 1; | ||
1117 | else | ||
1118 | mix->i2c.addr = TAS_I2C_ADDR; | ||
1119 | |||
1120 | if (chip->model == PMAC_TUMBLER) { | ||
1121 | mix->i2c.init_client = tumbler_init_client; | ||
1122 | mix->i2c.name = "TAS3001c"; | ||
1123 | chipname = "Tumbler"; | ||
1124 | } else { | ||
1125 | mix->i2c.init_client = snapper_init_client; | ||
1126 | mix->i2c.name = "TAS3004"; | ||
1127 | chipname = "Snapper"; | ||
1128 | } | ||
1129 | |||
1130 | if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0) | ||
1131 | return err; | ||
1132 | |||
1133 | /* | ||
1134 | * build mixers | ||
1135 | */ | ||
1136 | sprintf(chip->card->mixername, "PowerMac %s", chipname); | ||
1137 | |||
1138 | if (chip->model == PMAC_TUMBLER) { | ||
1139 | for (i = 0; i < ARRAY_SIZE(tumbler_mixers); i++) { | ||
1140 | if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0) | ||
1141 | return err; | ||
1142 | } | ||
1143 | } else { | ||
1144 | for (i = 0; i < ARRAY_SIZE(snapper_mixers); i++) { | ||
1145 | if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snapper_mixers[i], chip))) < 0) | ||
1146 | return err; | ||
1147 | } | ||
1148 | } | ||
1149 | chip->master_sw_ctl = snd_ctl_new1(&tumbler_hp_sw, chip); | ||
1150 | if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0) | ||
1151 | return err; | ||
1152 | chip->speaker_sw_ctl = snd_ctl_new1(&tumbler_speaker_sw, chip); | ||
1153 | if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0) | ||
1154 | return err; | ||
1155 | chip->drc_sw_ctl = snd_ctl_new1(&tumbler_drc_sw, chip); | ||
1156 | if ((err = snd_ctl_add(chip->card, chip->drc_sw_ctl)) < 0) | ||
1157 | return err; | ||
1158 | |||
1159 | |||
1160 | #ifdef CONFIG_PMAC_PBOOK | ||
1161 | chip->resume = tumbler_resume; | ||
1162 | #endif | ||
1163 | |||
1164 | INIT_WORK(&device_change, device_change_handler, (void *)chip); | ||
1165 | |||
1166 | #ifdef PMAC_SUPPORT_AUTOMUTE | ||
1167 | if (mix->headphone_irq >=0 && (err = snd_pmac_add_automute(chip)) < 0) | ||
1168 | return err; | ||
1169 | chip->detect_headphone = tumbler_detect_headphone; | ||
1170 | chip->update_automute = tumbler_update_automute; | ||
1171 | tumbler_update_automute(chip, 0); /* update the status only */ | ||
1172 | #endif | ||
1173 | |||
1174 | return 0; | ||
1175 | } | ||
diff --git a/sound/ppc/tumbler_volume.h b/sound/ppc/tumbler_volume.h new file mode 100644 index 000000000000..ef8d85d58b02 --- /dev/null +++ b/sound/ppc/tumbler_volume.h | |||
@@ -0,0 +1,250 @@ | |||
1 | /* volume tables, taken from TAS3001c data manual */ | ||
2 | /* volume gain values */ | ||
3 | /* 0 = -70 dB, 175 = 18.0 dB in 0.5 dB step */ | ||
4 | static unsigned int master_volume_table[] = { | ||
5 | 0x00000015, 0x00000016, 0x00000017, | ||
6 | 0x00000019, 0x0000001a, 0x0000001c, | ||
7 | 0x0000001d, 0x0000001f, 0x00000021, | ||
8 | 0x00000023, 0x00000025, 0x00000027, | ||
9 | 0x00000029, 0x0000002c, 0x0000002e, | ||
10 | 0x00000031, 0x00000034, 0x00000037, | ||
11 | 0x0000003a, 0x0000003e, 0x00000042, | ||
12 | 0x00000045, 0x0000004a, 0x0000004e, | ||
13 | 0x00000053, 0x00000057, 0x0000005d, | ||
14 | 0x00000062, 0x00000068, 0x0000006e, | ||
15 | 0x00000075, 0x0000007b, 0x00000083, | ||
16 | 0x0000008b, 0x00000093, 0x0000009b, | ||
17 | 0x000000a5, 0x000000ae, 0x000000b9, | ||
18 | 0x000000c4, 0x000000cf, 0x000000dc, | ||
19 | 0x000000e9, 0x000000f6, 0x00000105, | ||
20 | 0x00000114, 0x00000125, 0x00000136, | ||
21 | 0x00000148, 0x0000015c, 0x00000171, | ||
22 | 0x00000186, 0x0000019e, 0x000001b6, | ||
23 | 0x000001d0, 0x000001eb, 0x00000209, | ||
24 | 0x00000227, 0x00000248, 0x0000026b, | ||
25 | 0x0000028f, 0x000002b6, 0x000002df, | ||
26 | 0x0000030b, 0x00000339, 0x0000036a, | ||
27 | 0x0000039e, 0x000003d5, 0x0000040f, | ||
28 | 0x0000044c, 0x0000048d, 0x000004d2, | ||
29 | 0x0000051c, 0x00000569, 0x000005bb, | ||
30 | 0x00000612, 0x0000066e, 0x000006d0, | ||
31 | 0x00000737, 0x000007a5, 0x00000818, | ||
32 | 0x00000893, 0x00000915, 0x0000099f, | ||
33 | 0x00000a31, 0x00000acc, 0x00000b6f, | ||
34 | 0x00000c1d, 0x00000cd5, 0x00000d97, | ||
35 | 0x00000e65, 0x00000f40, 0x00001027, | ||
36 | 0x0000111c, 0x00001220, 0x00001333, | ||
37 | 0x00001456, 0x0000158a, 0x000016d1, | ||
38 | 0x0000182b, 0x0000199a, 0x00001b1e, | ||
39 | 0x00001cb9, 0x00001e6d, 0x0000203a, | ||
40 | 0x00002223, 0x00002429, 0x0000264e, | ||
41 | 0x00002893, 0x00002afa, 0x00002d86, | ||
42 | 0x00003039, 0x00003314, 0x0000361b, | ||
43 | 0x00003950, 0x00003cb5, 0x0000404e, | ||
44 | 0x0000441d, 0x00004827, 0x00004c6d, | ||
45 | 0x000050f4, 0x000055c0, 0x00005ad5, | ||
46 | 0x00006037, 0x000065ea, 0x00006bf4, | ||
47 | 0x0000725a, 0x00007920, 0x0000804e, | ||
48 | 0x000087e8, 0x00008ff6, 0x0000987d, | ||
49 | 0x0000a186, 0x0000ab19, 0x0000b53c, | ||
50 | 0x0000bff9, 0x0000cb59, 0x0000d766, | ||
51 | 0x0000e429, 0x0000f1ae, 0x00010000, | ||
52 | 0x00010f2b, 0x00011f3d, 0x00013042, | ||
53 | 0x00014249, 0x00015562, 0x0001699c, | ||
54 | 0x00017f09, 0x000195bc, 0x0001adc6, | ||
55 | 0x0001c73d, 0x0001e237, 0x0001feca, | ||
56 | 0x00021d0e, 0x00023d1d, 0x00025f12, | ||
57 | 0x0002830b, 0x0002a925, 0x0002d182, | ||
58 | 0x0002fc42, 0x0003298b, 0x00035983, | ||
59 | 0x00038c53, 0x0003c225, 0x0003fb28, | ||
60 | 0x0004378b, 0x00047783, 0x0004bb44, | ||
61 | 0x0005030a, 0x00054f10, 0x00059f98, | ||
62 | 0x0005f4e5, 0x00064f40, 0x0006aef6, | ||
63 | 0x00071457, 0x00077fbb, 0x0007f17b, | ||
64 | }; | ||
65 | |||
66 | /* treble table for TAS3001c */ | ||
67 | /* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ | ||
68 | static unsigned int treble_volume_table[] = { | ||
69 | 0x96, 0x95, 0x94, | ||
70 | 0x93, 0x92, 0x91, | ||
71 | 0x90, 0x8f, 0x8e, | ||
72 | 0x8d, 0x8c, 0x8b, | ||
73 | 0x8a, 0x89, 0x88, | ||
74 | 0x87, 0x86, 0x85, | ||
75 | 0x84, 0x83, 0x82, | ||
76 | 0x81, 0x80, 0x7f, | ||
77 | 0x7e, 0x7d, 0x7c, | ||
78 | 0x7b, 0x7a, 0x79, | ||
79 | 0x78, 0x77, 0x76, | ||
80 | 0x75, 0x74, 0x73, | ||
81 | 0x72, 0x71, 0x70, | ||
82 | 0x6e, 0x6d, 0x6c, | ||
83 | 0x6b, 0x69, 0x68, | ||
84 | 0x66, 0x65, 0x63, | ||
85 | 0x62, 0x60, 0x5e, | ||
86 | 0x5c, 0x5a, 0x57, | ||
87 | 0x55, 0x52, 0x4f, | ||
88 | 0x4c, 0x49, 0x45, | ||
89 | 0x42, 0x3e, 0x3a, | ||
90 | 0x36, 0x32, 0x2d, | ||
91 | 0x28, 0x22, 0x1c, | ||
92 | 0x16, 0x10, 0x09, | ||
93 | 0x01, | ||
94 | }; | ||
95 | |||
96 | /* bass table for TAS3001c */ | ||
97 | /* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ | ||
98 | static unsigned int bass_volume_table[] = { | ||
99 | 0x86, 0x82, 0x7f, | ||
100 | 0x7d, 0x7a, 0x78, | ||
101 | 0x76, 0x74, 0x72, | ||
102 | 0x70, 0x6e, 0x6d, | ||
103 | 0x6b, 0x69, 0x66, | ||
104 | 0x64, 0x61, 0x5f, | ||
105 | 0x5d, 0x5c, 0x5a, | ||
106 | 0x59, 0x58, 0x56, | ||
107 | 0x55, 0x54, 0x53, | ||
108 | 0x51, 0x4f, 0x4d, | ||
109 | 0x4b, 0x49, 0x46, | ||
110 | 0x44, 0x42, 0x40, | ||
111 | 0x3e, 0x3c, 0x3b, | ||
112 | 0x39, 0x38, 0x36, | ||
113 | 0x35, 0x33, 0x31, | ||
114 | 0x30, 0x2e, 0x2c, | ||
115 | 0x2b, 0x29, 0x28, | ||
116 | 0x26, 0x25, 0x23, | ||
117 | 0x21, 0x1f, 0x1c, | ||
118 | 0x19, 0x18, 0x17, | ||
119 | 0x16, 0x14, 0x13, | ||
120 | 0x12, 0x10, 0x0f, | ||
121 | 0x0d, 0x0b, 0x0a, | ||
122 | 0x08, 0x06, 0x03, | ||
123 | 0x01, | ||
124 | }; | ||
125 | |||
126 | /* mixer (pcm) volume table */ | ||
127 | /* 0 = -70 dB, 175 = 18.0 dB in 0.5 dB step */ | ||
128 | static unsigned int mixer_volume_table[] = { | ||
129 | 0x00014b, 0x00015f, 0x000174, | ||
130 | 0x00018a, 0x0001a1, 0x0001ba, | ||
131 | 0x0001d4, 0x0001f0, 0x00020d, | ||
132 | 0x00022c, 0x00024d, 0x000270, | ||
133 | 0x000295, 0x0002bc, 0x0002e6, | ||
134 | 0x000312, 0x000340, 0x000372, | ||
135 | 0x0003a6, 0x0003dd, 0x000418, | ||
136 | 0x000456, 0x000498, 0x0004de, | ||
137 | 0x000528, 0x000576, 0x0005c9, | ||
138 | 0x000620, 0x00067d, 0x0006e0, | ||
139 | 0x000748, 0x0007b7, 0x00082c, | ||
140 | 0x0008a8, 0x00092b, 0x0009b6, | ||
141 | 0x000a49, 0x000ae5, 0x000b8b, | ||
142 | 0x000c3a, 0x000cf3, 0x000db8, | ||
143 | 0x000e88, 0x000f64, 0x00104e, | ||
144 | 0x001145, 0x00124b, 0x001361, | ||
145 | 0x001487, 0x0015be, 0x001708, | ||
146 | 0x001865, 0x0019d8, 0x001b60, | ||
147 | 0x001cff, 0x001eb7, 0x002089, | ||
148 | 0x002276, 0x002481, 0x0026ab, | ||
149 | 0x0028f5, 0x002b63, 0x002df5, | ||
150 | 0x0030ae, 0x003390, 0x00369e, | ||
151 | 0x0039db, 0x003d49, 0x0040ea, | ||
152 | 0x0044c3, 0x0048d6, 0x004d27, | ||
153 | 0x0051b9, 0x005691, 0x005bb2, | ||
154 | 0x006121, 0x0066e3, 0x006cfb, | ||
155 | 0x007370, 0x007a48, 0x008186, | ||
156 | 0x008933, 0x009154, 0x0099f1, | ||
157 | 0x00a310, 0x00acba, 0x00b6f6, | ||
158 | 0x00c1cd, 0x00cd49, 0x00d973, | ||
159 | 0x00e655, 0x00f3fb, 0x010270, | ||
160 | 0x0111c0, 0x0121f9, 0x013328, | ||
161 | 0x01455b, 0x0158a2, 0x016d0e, | ||
162 | 0x0182af, 0x019999, 0x01b1de, | ||
163 | 0x01cb94, 0x01e6cf, 0x0203a7, | ||
164 | 0x022235, 0x024293, 0x0264db, | ||
165 | 0x02892c, 0x02afa3, 0x02d862, | ||
166 | 0x03038a, 0x033142, 0x0361af, | ||
167 | 0x0394fa, 0x03cb50, 0x0404de, | ||
168 | 0x0441d5, 0x048268, 0x04c6d0, | ||
169 | 0x050f44, 0x055c04, 0x05ad50, | ||
170 | 0x06036e, 0x065ea5, 0x06bf44, | ||
171 | 0x07259d, 0x079207, 0x0804dc, | ||
172 | 0x087e80, 0x08ff59, 0x0987d5, | ||
173 | 0x0a1866, 0x0ab189, 0x0b53be, | ||
174 | 0x0bff91, 0x0cb591, 0x0d765a, | ||
175 | 0x0e4290, 0x0f1adf, 0x100000, | ||
176 | 0x10f2b4, 0x11f3c9, 0x13041a, | ||
177 | 0x14248e, 0x15561a, 0x1699c0, | ||
178 | 0x17f094, 0x195bb8, 0x1adc61, | ||
179 | 0x1c73d5, 0x1e236d, 0x1fec98, | ||
180 | 0x21d0d9, 0x23d1cd, 0x25f125, | ||
181 | 0x2830af, 0x2a9254, 0x2d1818, | ||
182 | 0x2fc420, 0x3298b0, 0x35982f, | ||
183 | 0x38c528, 0x3c224c, 0x3fb278, | ||
184 | 0x437880, 0x477828, 0x4bb446, | ||
185 | 0x5030a1, 0x54f106, 0x59f980, | ||
186 | 0x5f4e52, 0x64f403, 0x6aef5d, | ||
187 | 0x714575, 0x77fbaa, 0x7f17af, | ||
188 | }; | ||
189 | |||
190 | |||
191 | /* treble table for TAS3004 */ | ||
192 | /* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ | ||
193 | static unsigned int snapper_treble_volume_table[] = { | ||
194 | 0x96, 0x95, 0x94, | ||
195 | 0x93, 0x92, 0x91, | ||
196 | 0x90, 0x8f, 0x8e, | ||
197 | 0x8d, 0x8c, 0x8b, | ||
198 | 0x8a, 0x89, 0x88, | ||
199 | 0x87, 0x86, 0x85, | ||
200 | 0x84, 0x83, 0x82, | ||
201 | 0x81, 0x80, 0x7f, | ||
202 | 0x7e, 0x7d, 0x7c, | ||
203 | 0x7b, 0x7a, 0x79, | ||
204 | 0x78, 0x77, 0x76, | ||
205 | 0x75, 0x74, 0x73, | ||
206 | 0x72, 0x71, 0x70, | ||
207 | 0x6f, 0x6d, 0x6c, | ||
208 | 0x6b, 0x69, 0x68, | ||
209 | 0x67, 0x65, 0x63, | ||
210 | 0x62, 0x60, 0x5d, | ||
211 | 0x5b, 0x59, 0x56, | ||
212 | 0x53, 0x51, 0x4d, | ||
213 | 0x4a, 0x47, 0x43, | ||
214 | 0x3f, 0x3b, 0x36, | ||
215 | 0x31, 0x2c, 0x26, | ||
216 | 0x20, 0x1a, 0x13, | ||
217 | 0x08, 0x04, 0x01, | ||
218 | 0x01, | ||
219 | }; | ||
220 | |||
221 | /* bass table for TAS3004 */ | ||
222 | /* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */ | ||
223 | static unsigned int snapper_bass_volume_table[] = { | ||
224 | 0x96, 0x95, 0x94, | ||
225 | 0x93, 0x92, 0x91, | ||
226 | 0x90, 0x8f, 0x8e, | ||
227 | 0x8d, 0x8c, 0x8b, | ||
228 | 0x8a, 0x89, 0x88, | ||
229 | 0x87, 0x86, 0x85, | ||
230 | 0x84, 0x83, 0x82, | ||
231 | 0x81, 0x80, 0x7f, | ||
232 | 0x7e, 0x7d, 0x7c, | ||
233 | 0x7b, 0x7a, 0x79, | ||
234 | 0x78, 0x77, 0x76, | ||
235 | 0x75, 0x74, 0x73, | ||
236 | 0x72, 0x71, 0x6f, | ||
237 | 0x6e, 0x6d, 0x6b, | ||
238 | 0x6a, 0x69, 0x67, | ||
239 | 0x66, 0x65, 0x63, | ||
240 | 0x62, 0x61, 0x5f, | ||
241 | 0x5d, 0x5b, 0x58, | ||
242 | 0x55, 0x52, 0x4f, | ||
243 | 0x4c, 0x49, 0x46, | ||
244 | 0x43, 0x3f, 0x3b, | ||
245 | 0x37, 0x33, 0x2e, | ||
246 | 0x29, 0x24, 0x1e, | ||
247 | 0x18, 0x11, 0x0a, | ||
248 | 0x01, | ||
249 | }; | ||
250 | |||