Blame view

sound/ppc/tumbler.c 38.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  /*
   * PMac Tumbler/Snapper lowlevel functions
   *
   * Copyright (c) by Takashi Iwai <tiwai@suse.de>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   *   Rene Rebe <rene.rebe@gmx.net>:
   *     * update from shadow registers on wakeup and headphone plug
   *     * automatically toggle DRC on headphone plug
   *	
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
26
27
  #include <linux/init.h>
  #include <linux/delay.h>
  #include <linux/i2c.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
  #include <linux/kmod.h>
  #include <linux/slab.h>
  #include <linux/interrupt.h>
819ef70b1   Risto Suominen   ALSA: powermac - ...
31
  #include <linux/string.h>
5af507300   Rob Herring   drivers: clean-up...
32
  #include <linux/of_irq.h>
6cbbfe1c8   Takashi Iwai   ALSA: Include lin...
33
  #include <linux/io.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  #include <sound/core.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
  #include <asm/irq.h>
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
36
  #include <asm/machdep.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
  #include <asm/pmac_feature.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
  #include "pmac.h"
  #include "tumbler_volume.h"
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
40
41
42
  #undef DEBUG
  
  #ifdef DEBUG
6da671138   Takashi Iwai   ALSA: powermac - ...
43
  #define DBG(fmt...) printk(KERN_DEBUG fmt)
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
44
45
46
  #else
  #define DBG(fmt...)
  #endif
68c7ccb8f   Takashi Iwai   ALSA: powermac - ...
47
  #define IS_G4DA (of_machine_is_compatible("PowerMac3,4"))
819ef70b1   Risto Suominen   ALSA: powermac - ...
48

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
  /* i2c address for tumbler */
  #define TAS_I2C_ADDR	0x34
  
  /* registers */
  #define TAS_REG_MCS	0x01	/* main control */
  #define TAS_REG_DRC	0x02
  #define TAS_REG_VOL	0x04
  #define TAS_REG_TREBLE	0x05
  #define TAS_REG_BASS	0x06
  #define TAS_REG_INPUT1	0x07
  #define TAS_REG_INPUT2	0x08
  
  /* tas3001c */
  #define TAS_REG_PCM	TAS_REG_INPUT1
   
  /* tas3004 */
  #define TAS_REG_LMIX	TAS_REG_INPUT1
  #define TAS_REG_RMIX	TAS_REG_INPUT2
  #define TAS_REG_MCS2	0x43		/* main control 2 */
  #define TAS_REG_ACS	0x40		/* analog control */
  
  /* mono volumes for tas3001c/tas3004 */
  enum {
  	VOL_IDX_PCM_MONO, /* tas3001c only */
  	VOL_IDX_BASS, VOL_IDX_TREBLE,
  	VOL_IDX_LAST_MONO
  };
  
  /* stereo volumes for tas3004 */
  enum {
  	VOL_IDX_PCM, VOL_IDX_PCM2, VOL_IDX_ADC,
  	VOL_IDX_LAST_MIX
  };
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
82
  struct pmac_gpio {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83
  	unsigned int addr;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
84
85
86
  	u8 active_val;
  	u8 inactive_val;
  	u8 active_state;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
87
88
89
90
91
92
93
94
95
96
  };
  
  struct pmac_tumbler {
  	struct pmac_keywest i2c;
  	struct pmac_gpio audio_reset;
  	struct pmac_gpio amp_mute;
  	struct pmac_gpio line_mute;
  	struct pmac_gpio line_detect;
  	struct pmac_gpio hp_mute;
  	struct pmac_gpio hp_detect;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
  	int headphone_irq;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
98
  	int lineout_irq;
085e6fc96   Colin Leroy   [PATCH] pmac: sav...
99
  	unsigned int save_master_vol[2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
  	unsigned int master_vol[2];
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
101
  	unsigned int save_master_switch[2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
105
106
107
  	unsigned int master_switch[2];
  	unsigned int mono_vol[VOL_IDX_LAST_MONO];
  	unsigned int mix_vol[VOL_IDX_LAST_MIX][2]; /* stereo volumes for tas3004 */
  	int drc_range;
  	int drc_enable;
  	int capture_source;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
108
109
110
111
  	int anded_reset;
  	int auto_mute_notify;
  	int reset_on_sleep;
  	u8  acs;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
112
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
114
115
116
  
  
  /*
   */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
117
  static int send_init_client(struct pmac_keywest *i2c, unsigned int *regs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
122
123
124
125
  {
  	while (*regs > 0) {
  		int err, count = 10;
  		do {
  			err = i2c_smbus_write_byte_data(i2c->client,
  							regs[0], regs[1]);
  			if (err >= 0)
  				break;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
126
127
  			DBG("(W) i2c error %d
  ", err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
131
132
133
134
135
  			mdelay(10);
  		} while (count--);
  		if (err < 0)
  			return -ENXIO;
  		regs += 2;
  	}
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
136
  static int tumbler_init_client(struct pmac_keywest *i2c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
140
141
142
  {
  	static unsigned int regs[] = {
  		/* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */
  		TAS_REG_MCS, (1<<6)|(2<<4)|(2<<2)|0,
  		0, /* terminator */
  	};
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
143
144
  	DBG("(I) tumbler init client
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
  	return send_init_client(i2c, regs);
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
147
  static int snapper_init_client(struct pmac_keywest *i2c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
149
150
151
152
153
154
155
156
157
  {
  	static unsigned int regs[] = {
  		/* normal operation, SCLK=64fps, i2s output, 16bit width */
  		TAS_REG_MCS, (1<<6)|(2<<4)|0,
  		/* normal operation, all-pass mode */
  		TAS_REG_MCS2, (1<<1),
  		/* normal output, no deemphasis, A input, power-up, line-in */
  		TAS_REG_ACS, 0,
  		0, /* terminator */
  	};
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
158
159
  	DBG("(I) snapper init client
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
162
163
164
165
  	return send_init_client(i2c, regs);
  }
  	
  /*
   * gpio access
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
167
168
169
170
  #define do_gpio_write(gp, val) \
  	pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, (gp)->addr, val)
  #define do_gpio_read(gp) \
  	pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, (gp)->addr, 0)
  #define tumbler_gpio_free(gp) /* NOP */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171

65b29f503   Takashi Iwai   [ALSA] Remove xxx...
172
  static void write_audio_gpio(struct pmac_gpio *gp, int active)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
173
174
175
  {
  	if (! gp->addr)
  		return;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
176
  	active = active ? gp->active_val : gp->inactive_val;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
177
178
179
  	do_gpio_write(gp, active);
  	DBG("(I) gpio %x write %d
  ", gp->addr, active);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
181
  static int check_audio_gpio(struct pmac_gpio *gp)
4be8dc7ff   Benjamin Herrenschmidt   [PATCH] ppc64: im...
182
183
184
185
186
187
188
  {
  	int ret;
  
  	if (! gp->addr)
  		return 0;
  
  	ret = do_gpio_read(gp);
2fd53a7e9   Andreas Schwab   [ALSA] [PPC,SOUND...
189
  	return (ret & 0x1) == (gp->active_val & 0x1);
4be8dc7ff   Benjamin Herrenschmidt   [PATCH] ppc64: im...
190
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
191
  static int read_audio_gpio(struct pmac_gpio *gp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
194
195
  {
  	int ret;
  	if (! gp->addr)
  		return 0;
2fd53a7e9   Andreas Schwab   [ALSA] [PPC,SOUND...
196
197
  	ret = do_gpio_read(gp);
  	ret = (ret & 0x02) !=0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
202
203
  	return ret == gp->active_state;
  }
  
  /*
   * update master volume
   */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
204
  static int tumbler_set_master_volume(struct pmac_tumbler *mix)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  {
  	unsigned char block[6];
  	unsigned int left_vol, right_vol;
    
  	if (! mix->i2c.client)
  		return -ENODEV;
    
  	if (! mix->master_switch[0])
  		left_vol = 0;
  	else {
  		left_vol = mix->master_vol[0];
  		if (left_vol >= ARRAY_SIZE(master_volume_table))
  			left_vol = ARRAY_SIZE(master_volume_table) - 1;
  		left_vol = master_volume_table[left_vol];
  	}
  	if (! mix->master_switch[1])
  		right_vol = 0;
  	else {
  		right_vol = mix->master_vol[1];
  		if (right_vol >= ARRAY_SIZE(master_volume_table))
  			right_vol = ARRAY_SIZE(master_volume_table) - 1;
  		right_vol = master_volume_table[right_vol];
  	}
  
  	block[0] = (left_vol >> 16) & 0xff;
  	block[1] = (left_vol >> 8)  & 0xff;
  	block[2] = (left_vol >> 0)  & 0xff;
  
  	block[3] = (right_vol >> 16) & 0xff;
  	block[4] = (right_vol >> 8)  & 0xff;
  	block[5] = (right_vol >> 0)  & 0xff;
    
367636e8a   Benjamin Herrenschmidt   [PATCH] powerpc: ...
237
238
  	if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_VOL, 6,
  					   block) < 0) {
6da671138   Takashi Iwai   ALSA: powermac - ...
239
240
  		snd_printk(KERN_ERR "failed to set volume 
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
241
242
  		return -EINVAL;
  	}
f1b1f75e2   Risto Suominen   ALSA: powermac - ...
243
244
  	DBG("(I) succeeded to set volume (%u, %u)
  ", left_vol, right_vol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
248
249
  	return 0;
  }
  
  
  /* output volume */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
250
251
  static int tumbler_info_master_volume(struct snd_kcontrol *kcontrol,
  				      struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
257
258
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 2;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = ARRAY_SIZE(master_volume_table) - 1;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
259
260
  static int tumbler_get_master_volume(struct snd_kcontrol *kcontrol,
  				     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
262
263
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
264

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
  	ucontrol->value.integer.value[0] = mix->master_vol[0];
  	ucontrol->value.integer.value[1] = mix->master_vol[1];
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
269
270
  static int tumbler_put_master_volume(struct snd_kcontrol *kcontrol,
  				     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
272
273
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
274
  	unsigned int vol[2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
  	int change;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
276
277
278
279
280
281
282
  	vol[0] = ucontrol->value.integer.value[0];
  	vol[1] = ucontrol->value.integer.value[1];
  	if (vol[0] >= ARRAY_SIZE(master_volume_table) ||
  	    vol[1] >= ARRAY_SIZE(master_volume_table))
  		return -EINVAL;
  	change = mix->master_vol[0] != vol[0] ||
  		mix->master_vol[1] != vol[1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
  	if (change) {
d4079ac49   Takashi Iwai   [ALSA] powermac -...
284
285
  		mix->master_vol[0] = vol[0];
  		mix->master_vol[1] = vol[1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
286
287
288
289
290
291
  		tumbler_set_master_volume(mix);
  	}
  	return change;
  }
  
  /* output switch */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
292
293
  static int tumbler_get_master_switch(struct snd_kcontrol *kcontrol,
  				     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
295
296
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
297

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
  	ucontrol->value.integer.value[0] = mix->master_switch[0];
  	ucontrol->value.integer.value[1] = mix->master_switch[1];
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
302
303
  static int tumbler_put_master_switch(struct snd_kcontrol *kcontrol,
  				     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
305
306
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
307
  	int change;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  	change = mix->master_switch[0] != ucontrol->value.integer.value[0] ||
  		mix->master_switch[1] != ucontrol->value.integer.value[1];
  	if (change) {
  		mix->master_switch[0] = !!ucontrol->value.integer.value[0];
  		mix->master_switch[1] = !!ucontrol->value.integer.value[1];
  		tumbler_set_master_volume(mix);
  	}
  	return change;
  }
  
  
  /*
   * TAS3001c dynamic range compression
   */
  
  #define TAS3001_DRC_MAX		0x5f
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
324
  static int tumbler_set_drc(struct pmac_tumbler *mix)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
  {
  	unsigned char val[2];
  
  	if (! mix->i2c.client)
  		return -ENODEV;
    
  	if (mix->drc_enable) {
  		val[0] = 0xc1; /* enable, 3:1 compression */
  		if (mix->drc_range > TAS3001_DRC_MAX)
  			val[1] = 0xf0;
  		else if (mix->drc_range < 0)
  			val[1] = 0x91;
  		else
  			val[1] = mix->drc_range + 0x91;
  	} else {
  		val[0] = 0;
  		val[1] = 0;
  	}
367636e8a   Benjamin Herrenschmidt   [PATCH] powerpc: ...
343
344
  	if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_DRC,
  					   2, val) < 0) {
6da671138   Takashi Iwai   ALSA: powermac - ...
345
346
  		snd_printk(KERN_ERR "failed to set DRC
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
  		return -EINVAL;
  	}
f1b1f75e2   Risto Suominen   ALSA: powermac - ...
349
350
  	DBG("(I) succeeded to set DRC (%u, %u)
  ", val[0], val[1]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
351
352
353
354
355
356
357
358
  	return 0;
  }
  
  /*
   * TAS3004
   */
  
  #define TAS3004_DRC_MAX		0xef
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
359
  static int snapper_set_drc(struct pmac_tumbler *mix)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  {
  	unsigned char val[6];
  
  	if (! mix->i2c.client)
  		return -ENODEV;
    
  	if (mix->drc_enable)
  		val[0] = 0x50; /* 3:1 above threshold */
  	else
  		val[0] = 0x51; /* disabled */
  	val[1] = 0x02; /* 1:1 below threshold */
  	if (mix->drc_range > 0xef)
  		val[2] = 0xef;
  	else if (mix->drc_range < 0)
  		val[2] = 0x00;
  	else
  		val[2] = mix->drc_range;
  	val[3] = 0xb0;
  	val[4] = 0x60;
  	val[5] = 0xa0;
367636e8a   Benjamin Herrenschmidt   [PATCH] powerpc: ...
380
381
  	if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_DRC,
  					   6, val) < 0) {
6da671138   Takashi Iwai   ALSA: powermac - ...
382
383
  		snd_printk(KERN_ERR "failed to set DRC
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
  		return -EINVAL;
  	}
f1b1f75e2   Risto Suominen   ALSA: powermac - ...
386
387
  	DBG("(I) succeeded to set DRC (%u, %u)
  ", val[0], val[1]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388
389
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
390
391
  static int tumbler_info_drc_value(struct snd_kcontrol *kcontrol,
  				  struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
393
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
394
395
396
397
398
399
400
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max =
  		chip->model == PMAC_TUMBLER ? TAS3001_DRC_MAX : TAS3004_DRC_MAX;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
401
402
  static int tumbler_get_drc_value(struct snd_kcontrol *kcontrol,
  				 struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
404
405
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
406
407
408
409
410
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
  	ucontrol->value.integer.value[0] = mix->drc_range;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
411
412
  static int tumbler_put_drc_value(struct snd_kcontrol *kcontrol,
  				 struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
414
415
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
416
  	unsigned int val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
417
418
419
420
  	int change;
  
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
421
422
423
424
425
426
427
428
429
  	val = ucontrol->value.integer.value[0];
  	if (chip->model == PMAC_TUMBLER) {
  		if (val > TAS3001_DRC_MAX)
  			return -EINVAL;
  	} else {
  		if (val > TAS3004_DRC_MAX)
  			return -EINVAL;
  	}
  	change = mix->drc_range != val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
  	if (change) {
d4079ac49   Takashi Iwai   [ALSA] powermac -...
431
  		mix->drc_range = val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
433
434
435
436
437
438
  		if (chip->model == PMAC_TUMBLER)
  			tumbler_set_drc(mix);
  		else
  			snapper_set_drc(mix);
  	}
  	return change;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
439
440
  static int tumbler_get_drc_switch(struct snd_kcontrol *kcontrol,
  				  struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
442
443
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
445
446
447
448
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
  	ucontrol->value.integer.value[0] = mix->drc_enable;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
449
450
  static int tumbler_put_drc_switch(struct snd_kcontrol *kcontrol,
  				  struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
452
453
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
  	int change;
  
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
  	change = mix->drc_enable != ucontrol->value.integer.value[0];
  	if (change) {
  		mix->drc_enable = !!ucontrol->value.integer.value[0];
  		if (chip->model == PMAC_TUMBLER)
  			tumbler_set_drc(mix);
  		else
  			snapper_set_drc(mix);
  	}
  	return change;
  }
  
  
  /*
   * mono volumes
   */
  
  struct tumbler_mono_vol {
  	int index;
  	int reg;
  	int bytes;
  	unsigned int max;
  	unsigned int *table;
  };
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
481
482
  static int tumbler_set_mono_volume(struct pmac_tumbler *mix,
  				   struct tumbler_mono_vol *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
483
484
485
486
487
488
489
490
491
492
493
494
495
496
  {
  	unsigned char block[4];
  	unsigned int vol;
  	int i;
    
  	if (! mix->i2c.client)
  		return -ENODEV;
    
  	vol = mix->mono_vol[info->index];
  	if (vol >= info->max)
  		vol = info->max - 1;
  	vol = info->table[vol];
  	for (i = 0; i < info->bytes; i++)
  		block[i] = (vol >> ((info->bytes - i - 1) * 8)) & 0xff;
367636e8a   Benjamin Herrenschmidt   [PATCH] powerpc: ...
497
498
  	if (i2c_smbus_write_i2c_block_data(mix->i2c.client, info->reg,
  					   info->bytes, block) < 0) {
6da671138   Takashi Iwai   ALSA: powermac - ...
499
500
501
  		snd_printk(KERN_ERR "failed to set mono volume %d
  ",
  			   info->index);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
504
505
  		return -EINVAL;
  	}
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
506
507
  static int tumbler_info_mono(struct snd_kcontrol *kcontrol,
  			     struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
509
510
511
512
513
514
515
516
  {
  	struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value;
  
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = info->max - 1;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
517
518
  static int tumbler_get_mono(struct snd_kcontrol *kcontrol,
  			    struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
519
520
  {
  	struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
521
522
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
523
524
525
526
527
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
  	ucontrol->value.integer.value[0] = mix->mono_vol[info->index];
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
528
529
  static int tumbler_put_mono(struct snd_kcontrol *kcontrol,
  			    struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
531
  {
  	struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
532
533
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
534
  	unsigned int vol;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
537
538
  	int change;
  
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
539
540
541
542
  	vol = ucontrol->value.integer.value[0];
  	if (vol >= info->max)
  		return -EINVAL;
  	change = mix->mono_vol[info->index] != vol;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
543
  	if (change) {
d4079ac49   Takashi Iwai   [ALSA] powermac -...
544
  		mix->mono_vol[info->index] = vol;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
  		tumbler_set_mono_volume(mix, info);
  	}
  	return change;
  }
  
  /* TAS3001c mono volumes */
  static struct tumbler_mono_vol tumbler_pcm_vol_info = {
  	.index = VOL_IDX_PCM_MONO,
  	.reg = TAS_REG_PCM,
  	.bytes = 3,
  	.max = ARRAY_SIZE(mixer_volume_table),
  	.table = mixer_volume_table,
  };
  
  static struct tumbler_mono_vol tumbler_bass_vol_info = {
  	.index = VOL_IDX_BASS,
  	.reg = TAS_REG_BASS,
  	.bytes = 1,
  	.max = ARRAY_SIZE(bass_volume_table),
  	.table = bass_volume_table,
  };
  
  static struct tumbler_mono_vol tumbler_treble_vol_info = {
  	.index = VOL_IDX_TREBLE,
  	.reg = TAS_REG_TREBLE,
  	.bytes = 1,
  	.max = ARRAY_SIZE(treble_volume_table),
  	.table = treble_volume_table,
  };
  
  /* TAS3004 mono volumes */
  static struct tumbler_mono_vol snapper_bass_vol_info = {
  	.index = VOL_IDX_BASS,
  	.reg = TAS_REG_BASS,
  	.bytes = 1,
  	.max = ARRAY_SIZE(snapper_bass_volume_table),
  	.table = snapper_bass_volume_table,
  };
  
  static struct tumbler_mono_vol snapper_treble_vol_info = {
  	.index = VOL_IDX_TREBLE,
  	.reg = TAS_REG_TREBLE,
  	.bytes = 1,
  	.max = ARRAY_SIZE(snapper_treble_volume_table),
  	.table = snapper_treble_volume_table,
  };
  
  
  #define DEFINE_MONO(xname,type) { \
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
  	.name = xname, \
  	.info = tumbler_info_mono, \
  	.get = tumbler_get_mono, \
  	.put = tumbler_put_mono, \
  	.private_value = (unsigned long)(&tumbler_##type##_vol_info), \
  }
  
  #define DEFINE_SNAPPER_MONO(xname,type) { \
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
  	.name = xname, \
  	.info = tumbler_info_mono, \
  	.get = tumbler_get_mono, \
  	.put = tumbler_put_mono, \
  	.private_value = (unsigned long)(&snapper_##type##_vol_info), \
  }
  
  
  /*
   * snapper mixer volumes
   */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
615
  static int snapper_set_mix_vol1(struct pmac_tumbler *mix, int idx, int ch, int reg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
  {
  	int i, j, vol;
  	unsigned char block[9];
  
  	vol = mix->mix_vol[idx][ch];
  	if (vol >= ARRAY_SIZE(mixer_volume_table)) {
  		vol = ARRAY_SIZE(mixer_volume_table) - 1;
  		mix->mix_vol[idx][ch] = vol;
  	}
  
  	for (i = 0; i < 3; i++) {
  		vol = mix->mix_vol[i][ch];
  		vol = mixer_volume_table[vol];
  		for (j = 0; j < 3; j++)
  			block[i * 3 + j] = (vol >> ((2 - j) * 8)) & 0xff;
  	}
367636e8a   Benjamin Herrenschmidt   [PATCH] powerpc: ...
632
633
  	if (i2c_smbus_write_i2c_block_data(mix->i2c.client, reg,
  					   9, block) < 0) {
6da671138   Takashi Iwai   ALSA: powermac - ...
634
635
  		snd_printk(KERN_ERR "failed to set mono volume %d
  ", reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
639
  		return -EINVAL;
  	}
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
640
  static int snapper_set_mix_vol(struct pmac_tumbler *mix, int idx)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
644
645
646
647
648
  {
  	if (! mix->i2c.client)
  		return -ENODEV;
  	if (snapper_set_mix_vol1(mix, idx, 0, TAS_REG_LMIX) < 0 ||
  	    snapper_set_mix_vol1(mix, idx, 1, TAS_REG_RMIX) < 0)
  		return -EINVAL;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
649
650
  static int snapper_info_mix(struct snd_kcontrol *kcontrol,
  			    struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
654
655
656
657
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 2;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = ARRAY_SIZE(mixer_volume_table) - 1;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
658
659
  static int snapper_get_mix(struct snd_kcontrol *kcontrol,
  			   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
660
661
  {
  	int idx = (int)kcontrol->private_value;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
662
663
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
665
666
667
668
669
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
  	ucontrol->value.integer.value[0] = mix->mix_vol[idx][0];
  	ucontrol->value.integer.value[1] = mix->mix_vol[idx][1];
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
670
671
  static int snapper_put_mix(struct snd_kcontrol *kcontrol,
  			   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
672
673
  {
  	int idx = (int)kcontrol->private_value;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
674
675
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
676
  	unsigned int vol[2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
677
678
679
680
  	int change;
  
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
681
682
683
684
685
686
687
  	vol[0] = ucontrol->value.integer.value[0];
  	vol[1] = ucontrol->value.integer.value[1];
  	if (vol[0] >= ARRAY_SIZE(mixer_volume_table) ||
  	    vol[1] >= ARRAY_SIZE(mixer_volume_table))
  		return -EINVAL;
  	change = mix->mix_vol[idx][0] != vol[0] ||
  		mix->mix_vol[idx][1] != vol[1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
  	if (change) {
d4079ac49   Takashi Iwai   [ALSA] powermac -...
689
690
  		mix->mix_vol[idx][0] = vol[0];
  		mix->mix_vol[idx][1] = vol[1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
692
693
694
695
696
697
  		snapper_set_mix_vol(mix, idx);
  	}
  	return change;
  }
  
  
  /*
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
698
699
   * mute switches. FIXME: Turn that into software mute when both outputs are muted
   * to avoid codec reset on ibook M7
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700
   */
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
701
  enum { TUMBLER_MUTE_HP, TUMBLER_MUTE_AMP, TUMBLER_MUTE_LINE };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702

65b29f503   Takashi Iwai   [ALSA] Remove xxx...
703
704
  static int tumbler_get_mute_switch(struct snd_kcontrol *kcontrol,
  				   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
705
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
706
707
708
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
  	struct pmac_gpio *gp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
709
710
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
711
712
713
714
715
716
717
718
719
720
721
722
  	switch(kcontrol->private_value) {
  	case TUMBLER_MUTE_HP:
  		gp = &mix->hp_mute;	break;
  	case TUMBLER_MUTE_AMP:
  		gp = &mix->amp_mute;	break;
  	case TUMBLER_MUTE_LINE:
  		gp = &mix->line_mute;	break;
  	default:
  		gp = NULL;
  	}
  	if (gp == NULL)
  		return -EINVAL;
4be8dc7ff   Benjamin Herrenschmidt   [PATCH] ppc64: im...
723
  	ucontrol->value.integer.value[0] = !check_audio_gpio(gp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
724
725
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
726
727
  static int tumbler_put_mute_switch(struct snd_kcontrol *kcontrol,
  				   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
728
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
729
730
731
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
  	struct pmac_gpio *gp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
733
734
735
736
737
738
  	int val;
  #ifdef PMAC_SUPPORT_AUTOMUTE
  	if (chip->update_automute && chip->auto_mute)
  		return 0; /* don't touch in the auto-mute mode */
  #endif	
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
739
740
741
742
743
744
745
746
747
748
749
750
  	switch(kcontrol->private_value) {
  	case TUMBLER_MUTE_HP:
  		gp = &mix->hp_mute;	break;
  	case TUMBLER_MUTE_AMP:
  		gp = &mix->amp_mute;	break;
  	case TUMBLER_MUTE_LINE:
  		gp = &mix->line_mute;	break;
  	default:
  		gp = NULL;
  	}
  	if (gp == NULL)
  		return -EINVAL;
4be8dc7ff   Benjamin Herrenschmidt   [PATCH] ppc64: im...
751
  	val = ! check_audio_gpio(gp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
752
753
754
755
756
757
  	if (val != ucontrol->value.integer.value[0]) {
  		write_audio_gpio(gp, ! ucontrol->value.integer.value[0]);
  		return 1;
  	}
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
758
  static int snapper_set_capture_source(struct pmac_tumbler *mix)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759
760
761
  {
  	if (! mix->i2c.client)
  		return -ENODEV;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
762
  	if (mix->capture_source)
7b6c3a34e   Andreas Schwab   ALSA: sound/ppc/p...
763
  		mix->acs |= 2;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
764
765
766
  	else
  		mix->acs &= ~2;
  	return i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, mix->acs);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
767
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
768
769
  static int snapper_info_capture_source(struct snd_kcontrol *kcontrol,
  				       struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
770
  {
950227216   Takashi Iwai   ALSA: ppc: Use sn...
771
  	static const char * const texts[2] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
772
773
  		"Line", "Mic"
  	};
950227216   Takashi Iwai   ALSA: ppc: Use sn...
774
775
  
  	return snd_ctl_enum_info(uinfo, 1, 2, texts);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
776
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
777
778
  static int snapper_get_capture_source(struct snd_kcontrol *kcontrol,
  				      struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
780
781
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
782

dec44dbe0   Kamalesh Babulal   [ALSA] powermac -...
783
  	ucontrol->value.enumerated.item[0] = mix->capture_source;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
785
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
786
787
  static int snapper_put_capture_source(struct snd_kcontrol *kcontrol,
  				      struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
789
790
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
  	int change;
dec44dbe0   Kamalesh Babulal   [ALSA] powermac -...
792
  	change = ucontrol->value.enumerated.item[0] != mix->capture_source;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
  	if (change) {
d4079ac49   Takashi Iwai   [ALSA] powermac -...
794
  		mix->capture_source = !!ucontrol->value.enumerated.item[0];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
  		snapper_set_capture_source(mix);
  	}
  	return change;
  }
  
  #define DEFINE_SNAPPER_MIX(xname,idx,ofs) { \
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
  	.name = xname, \
  	.info = snapper_info_mix, \
  	.get = snapper_get_mix, \
  	.put = snapper_put_mix, \
  	.index = idx,\
  	.private_value = ofs, \
  }
  
  
  /*
   */
15afafc25   Bill Pemberton   ALSA: ppc: remove...
813
  static struct snd_kcontrol_new tumbler_mixers[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
  	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	  .name = "Master Playback Volume",
  	  .info = tumbler_info_master_volume,
  	  .get = tumbler_get_master_volume,
  	  .put = tumbler_put_master_volume
  	},
  	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	  .name = "Master Playback Switch",
  	  .info = snd_pmac_boolean_stereo_info,
  	  .get = tumbler_get_master_switch,
  	  .put = tumbler_put_master_switch
  	},
  	DEFINE_MONO("Tone Control - Bass", bass),
  	DEFINE_MONO("Tone Control - Treble", treble),
  	DEFINE_MONO("PCM Playback Volume", pcm),
  	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	  .name = "DRC Range",
  	  .info = tumbler_info_drc_value,
  	  .get = tumbler_get_drc_value,
  	  .put = tumbler_put_drc_value
  	},
  };
15afafc25   Bill Pemberton   ALSA: ppc: remove...
836
  static struct snd_kcontrol_new snapper_mixers[] = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
838
839
840
841
842
843
844
845
846
847
848
849
  	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	  .name = "Master Playback Volume",
  	  .info = tumbler_info_master_volume,
  	  .get = tumbler_get_master_volume,
  	  .put = tumbler_put_master_volume
  	},
  	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	  .name = "Master Playback Switch",
  	  .info = snd_pmac_boolean_stereo_info,
  	  .get = tumbler_get_master_switch,
  	  .put = tumbler_put_master_switch
  	},
  	DEFINE_SNAPPER_MIX("PCM Playback Volume", 0, VOL_IDX_PCM),
11843ee4d   Takashi Iwai   ALSA: powermac - ...
850
851
  	/* Alternative PCM is assigned to Mic analog loopback on iBook G4 */
  	DEFINE_SNAPPER_MIX("Mic Playback Volume", 0, VOL_IDX_PCM2),
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
  	DEFINE_SNAPPER_MIX("Monitor Mix Volume", 0, VOL_IDX_ADC),
  	DEFINE_SNAPPER_MONO("Tone Control - Bass", bass),
  	DEFINE_SNAPPER_MONO("Tone Control - Treble", treble),
  	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	  .name = "DRC Range",
  	  .info = tumbler_info_drc_value,
  	  .get = tumbler_get_drc_value,
  	  .put = tumbler_put_drc_value
  	},
  	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	  .name = "Input Source", /* FIXME: "Capture Source" doesn't work properly */
  	  .info = snapper_info_capture_source,
  	  .get = snapper_get_capture_source,
  	  .put = snapper_put_capture_source
  	},
  };
15afafc25   Bill Pemberton   ALSA: ppc: remove...
868
  static struct snd_kcontrol_new tumbler_hp_sw = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
869
870
871
872
873
874
875
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	.name = "Headphone Playback Switch",
  	.info = snd_pmac_boolean_mono_info,
  	.get = tumbler_get_mute_switch,
  	.put = tumbler_put_mute_switch,
  	.private_value = TUMBLER_MUTE_HP,
  };
15afafc25   Bill Pemberton   ALSA: ppc: remove...
876
  static struct snd_kcontrol_new tumbler_speaker_sw = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
877
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
ad1cd7450   Jaroslav Kysela   ALSA: rename "PC ...
878
  	.name = "Speaker Playback Switch",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
879
880
881
882
883
  	.info = snd_pmac_boolean_mono_info,
  	.get = tumbler_get_mute_switch,
  	.put = tumbler_put_mute_switch,
  	.private_value = TUMBLER_MUTE_AMP,
  };
15afafc25   Bill Pemberton   ALSA: ppc: remove...
884
  static struct snd_kcontrol_new tumbler_lineout_sw = {
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
885
886
887
888
889
890
891
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	.name = "Line Out Playback Switch",
  	.info = snd_pmac_boolean_mono_info,
  	.get = tumbler_get_mute_switch,
  	.put = tumbler_put_mute_switch,
  	.private_value = TUMBLER_MUTE_LINE,
  };
15afafc25   Bill Pemberton   ALSA: ppc: remove...
892
  static struct snd_kcontrol_new tumbler_drc_sw = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
896
897
898
899
900
901
902
903
904
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	.name = "DRC Switch",
  	.info = snd_pmac_boolean_mono_info,
  	.get = tumbler_get_drc_switch,
  	.put = tumbler_put_drc_switch
  };
  
  
  #ifdef PMAC_SUPPORT_AUTOMUTE
  /*
   * auto-mute stuffs
   */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
905
  static int tumbler_detect_headphone(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
906
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
907
  	struct pmac_tumbler *mix = chip->mixer_data;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
908
909
910
911
912
913
  	int detect = 0;
  
  	if (mix->hp_detect.addr)
  		detect |= read_audio_gpio(&mix->hp_detect);
  	return detect;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
914
  static int tumbler_detect_lineout(struct snd_pmac *chip)
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
915
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
916
  	struct pmac_tumbler *mix = chip->mixer_data;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
917
918
919
920
921
  	int detect = 0;
  
  	if (mix->line_detect.addr)
  		detect |= read_audio_gpio(&mix->line_detect);
  	return detect;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
922
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
923
924
  static void check_mute(struct snd_pmac *chip, struct pmac_gpio *gp, int val, int do_notify,
  		       struct snd_kcontrol *sw)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
  {
4be8dc7ff   Benjamin Herrenschmidt   [PATCH] ppc64: im...
926
  	if (check_audio_gpio(gp) != val) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
927
928
  		write_audio_gpio(gp, val);
  		if (do_notify)
4be8dc7ff   Benjamin Herrenschmidt   [PATCH] ppc64: im...
929
930
  			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
  				       &sw->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
931
932
933
934
  	}
  }
  
  static struct work_struct device_change;
c4028958b   David Howells   WorkStruct: make ...
935
  static struct snd_pmac *device_change_chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
936

c4028958b   David Howells   WorkStruct: make ...
937
  static void device_change_handler(struct work_struct *work)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
938
  {
c4028958b   David Howells   WorkStruct: make ...
939
  	struct snd_pmac *chip = device_change_chip;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
940
  	struct pmac_tumbler *mix;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
941
  	int headphone, lineout;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
943
944
945
946
  
  	if (!chip)
  		return;
  
  	mix = chip->mixer_data;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
947
948
  	if (snd_BUG_ON(!mix))
  		return;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
949

7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
950
951
952
953
954
955
956
957
958
959
960
961
962
963
  	headphone = tumbler_detect_headphone(chip);
  	lineout = tumbler_detect_lineout(chip);
  
  	DBG("headphone: %d, lineout: %d
  ", headphone, lineout);
  
  	if (headphone || lineout) {
  		/* unmute headphone/lineout & mute speaker */
  		if (headphone)
  			check_mute(chip, &mix->hp_mute, 0, mix->auto_mute_notify,
  				   chip->master_sw_ctl);
  		if (lineout && mix->line_mute.addr != 0)
  			check_mute(chip, &mix->line_mute, 0, mix->auto_mute_notify,
  				   chip->lineout_sw_ctl);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
964
  		if (mix->anded_reset)
989a0b248   Nishanth Aravamudan   [ALSA] Fix-up sle...
965
  			msleep(10);
30282f96d   Risto Suominen   ALSA: powermac - ...
966
  		check_mute(chip, &mix->amp_mute, !IS_G4DA, mix->auto_mute_notify,
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
967
  			   chip->speaker_sw_ctl);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
968
  	} else {
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
969
  		/* unmute speaker, mute others */
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
970
971
972
  		check_mute(chip, &mix->amp_mute, 0, mix->auto_mute_notify,
  			   chip->speaker_sw_ctl);
  		if (mix->anded_reset)
989a0b248   Nishanth Aravamudan   [ALSA] Fix-up sle...
973
  			msleep(10);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
974
975
  		check_mute(chip, &mix->hp_mute, 1, mix->auto_mute_notify,
  			   chip->master_sw_ctl);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
976
977
978
  		if (mix->line_mute.addr != 0)
  			check_mute(chip, &mix->line_mute, 1, mix->auto_mute_notify,
  				   chip->lineout_sw_ctl);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
979
  	}
f32838530   Takashi Iwai   [ALSA] powermac -...
980
  	if (mix->auto_mute_notify)
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
981
982
  		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
  				       &chip->hp_detect_ctl->id);
f32838530   Takashi Iwai   [ALSA] powermac -...
983
984
985
986
  
  #ifdef CONFIG_SND_POWERMAC_AUTO_DRC
  	mix->drc_enable = ! (headphone || lineout);
  	if (mix->auto_mute_notify)
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
987
988
  		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
  			       &chip->drc_sw_ctl->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
989
990
991
992
  	if (chip->model == PMAC_TUMBLER)
  		tumbler_set_drc(mix);
  	else
  		snapper_set_drc(mix);
f32838530   Takashi Iwai   [ALSA] powermac -...
993
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
994
995
996
997
  
  	/* reset the master volume so the correct amplification is applied */
  	tumbler_set_master_volume(mix);
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
998
  static void tumbler_update_automute(struct snd_pmac *chip, int do_notify)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
999
1000
  {
  	if (chip->auto_mute) {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1001
  		struct pmac_tumbler *mix;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1002
  		mix = chip->mixer_data;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
1003
1004
  		if (snd_BUG_ON(!mix))
  			return;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1005
  		mix->auto_mute_notify = do_notify;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1006
  		schedule_work(&device_change);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1007
1008
1009
1010
1011
1012
  	}
  }
  #endif /* PMAC_SUPPORT_AUTOMUTE */
  
  
  /* interrupt - headphone plug changed */
7d12e780e   David Howells   IRQ: Maintain reg...
1013
  static irqreturn_t headphone_intr(int irq, void *devid)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1014
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1015
  	struct snd_pmac *chip = devid;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
  	if (chip->update_automute && chip->initialized) {
  		chip->update_automute(chip, 1);
  		return IRQ_HANDLED;
  	}
  	return IRQ_NONE;
  }
  
  /* look for audio-gpio device */
  static struct device_node *find_audio_device(const char *name)
  {
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1026
  	struct device_node *gpiop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1027
1028
  	struct device_node *np;
    
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1029
1030
  	gpiop = of_find_node_by_name(NULL, "gpio");
  	if (! gpiop)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1031
1032
  		return NULL;
    
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1033
1034
  	for (np = of_get_next_child(gpiop, NULL); np;
  			np = of_get_next_child(gpiop, np)) {
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1035
  		const char *property = of_get_property(np, "audio-gpio", NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1036
  		if (property && strcmp(property, name) == 0)
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1037
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1038
  	}  
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1039
1040
  	of_node_put(gpiop);
  	return np;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1041
1042
1043
1044
1045
  }
  
  /* look for audio-gpio device */
  static struct device_node *find_compatible_audio_device(const char *name)
  {
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1046
  	struct device_node *gpiop;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1047
1048
  	struct device_node *np;
    
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1049
1050
  	gpiop = of_find_node_by_name(NULL, "gpio");
  	if (!gpiop)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1051
1052
  		return NULL;
    
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1053
1054
  	for (np = of_get_next_child(gpiop, NULL); np;
  			np = of_get_next_child(gpiop, np)) {
55b61fec2   Stephen Rothwell   [POWERPC] Rename ...
1055
  		if (of_device_is_compatible(np, name))
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1056
  			break;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1057
  	}  
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1058
1059
  	of_node_put(gpiop);
  	return np;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1060
1061
1062
  }
  
  /* find an audio device and get its address */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1063
1064
  static long tumbler_find_device(const char *device, const char *platform,
  				struct pmac_gpio *gp, int is_compatible)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1065
1066
  {
  	struct device_node *node;
abddd185a   Jeremy Kerr   [POWERPC] sound: ...
1067
1068
  	const u32 *base;
  	u32 addr;
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1069
  	long ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
1071
1072
1073
1074
1075
  
  	if (is_compatible)
  		node = find_compatible_audio_device(device);
  	else
  		node = find_audio_device(device);
  	if (! node) {
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1076
1077
  		DBG("(W) cannot find audio device %s !
  ", device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
1079
1080
1081
  		snd_printdd("cannot find device %s
  ", device);
  		return -ENODEV;
  	}
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1082
  	base = of_get_property(node, "AAPL,address", NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1083
  	if (! base) {
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1084
  		base = of_get_property(node, "reg", NULL);
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1085
  		if (!base) {
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1086
1087
  			DBG("(E) cannot find address for device %s !
  ", device);
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1088
1089
  			snd_printd("cannot find address for device %s
  ", device);
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1090
  			of_node_put(node);
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1091
1092
  			return -ENODEV;
  		}
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1093
1094
1095
1096
1097
  		addr = *base;
  		if (addr < 0x50)
  			addr += 0x50;
  	} else
  		addr = *base;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1098

b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1099
  	gp->addr = addr & 0x0000ffff;
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1100
  	/* Try to find the active state, default to 0 ! */
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1101
  	base = of_get_property(node, "audio-gpio-active-state", NULL);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1102
  	if (base) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1103
  		gp->active_state = *base;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1104
1105
1106
  		gp->active_val = (*base) ? 0x5 : 0x4;
  		gp->inactive_val = (*base) ? 0x4 : 0x5;
  	} else {
abddd185a   Jeremy Kerr   [POWERPC] sound: ...
1107
  		const u32 *prop = NULL;
b6d733500   Risto Suominen   ALSA: powermac - ...
1108
1109
  		gp->active_state = IS_G4DA
  				&& !strncmp(device, "keywest-gpio1", 13);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1110
1111
1112
1113
1114
1115
1116
  		gp->active_val = 0x4;
  		gp->inactive_val = 0x5;
  		/* Here are some crude hacks to extract the GPIO polarity and
  		 * open collector informations out of the do-platform script
  		 * as we don't yet have an interpreter for these things
  		 */
  		if (platform)
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1117
  			prop = of_get_property(node, platform, NULL);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
  		if (prop) {
  			if (prop[3] == 0x9 && prop[4] == 0x9) {
  				gp->active_val = 0xd;
  				gp->inactive_val = 0xc;
  			}
  			if (prop[3] == 0x1 && prop[4] == 0x1) {
  				gp->active_val = 0x5;
  				gp->inactive_val = 0x4;
  			}
  		}
  	}
  
  	DBG("(I) GPIO device %s found, offset: %x, active state: %d !
  ",
  	    device, gp->addr, gp->active_state);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1133

30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1134
1135
1136
  	ret = irq_of_parse_and_map(node, 0);
  	of_node_put(node);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1137
1138
1139
  }
  
  /* reset audio */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1140
  static void tumbler_reset_audio(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1141
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1142
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1143

b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1144
  	if (mix->anded_reset) {
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1145
1146
  		DBG("(I) codec anded reset !
  ");
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1147
1148
  		write_audio_gpio(&mix->hp_mute, 0);
  		write_audio_gpio(&mix->amp_mute, 0);
989a0b248   Nishanth Aravamudan   [ALSA] Fix-up sle...
1149
  		msleep(200);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1150
1151
  		write_audio_gpio(&mix->hp_mute, 1);
  		write_audio_gpio(&mix->amp_mute, 1);
989a0b248   Nishanth Aravamudan   [ALSA] Fix-up sle...
1152
  		msleep(100);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1153
1154
  		write_audio_gpio(&mix->hp_mute, 0);
  		write_audio_gpio(&mix->amp_mute, 0);
989a0b248   Nishanth Aravamudan   [ALSA] Fix-up sle...
1155
  		msleep(100);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1156
  	} else {
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1157
1158
  		DBG("(I) codec normal reset !
  ");
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1159
  		write_audio_gpio(&mix->audio_reset, 0);
989a0b248   Nishanth Aravamudan   [ALSA] Fix-up sle...
1160
  		msleep(200);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1161
  		write_audio_gpio(&mix->audio_reset, 1);
989a0b248   Nishanth Aravamudan   [ALSA] Fix-up sle...
1162
  		msleep(100);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1163
  		write_audio_gpio(&mix->audio_reset, 0);
989a0b248   Nishanth Aravamudan   [ALSA] Fix-up sle...
1164
  		msleep(100);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1165
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1166
  }
8c8709334   Benjamin Herrenschmidt   [PATCH] ppc32: Re...
1167
  #ifdef CONFIG_PM
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1168
  /* suspend mixer */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1169
  static void tumbler_suspend(struct snd_pmac *chip)
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1170
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1171
  	struct pmac_tumbler *mix = chip->mixer_data;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1172
1173
1174
  
  	if (mix->headphone_irq >= 0)
  		disable_irq(mix->headphone_irq);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1175
1176
  	if (mix->lineout_irq >= 0)
  		disable_irq(mix->lineout_irq);
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1177
1178
  	mix->save_master_switch[0] = mix->master_switch[0];
  	mix->save_master_switch[1] = mix->master_switch[1];
085e6fc96   Colin Leroy   [PATCH] pmac: sav...
1179
1180
  	mix->save_master_vol[0] = mix->master_vol[0];
  	mix->save_master_vol[1] = mix->master_vol[1];
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
  	mix->master_switch[0] = mix->master_switch[1] = 0;
  	tumbler_set_master_volume(mix);
  	if (!mix->anded_reset) {
  		write_audio_gpio(&mix->amp_mute, 1);
  		write_audio_gpio(&mix->hp_mute, 1);
  	}
  	if (chip->model == PMAC_SNAPPER) {
  		mix->acs |= 1;
  		i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, mix->acs);
  	}
  	if (mix->anded_reset) {
  		write_audio_gpio(&mix->amp_mute, 1);
  		write_audio_gpio(&mix->hp_mute, 1);
  	} else
  		write_audio_gpio(&mix->audio_reset, 1);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1197
  /* resume mixer */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1198
  static void tumbler_resume(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1199
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1200
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1201

b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1202
1203
1204
  	mix->acs &= ~1;
  	mix->master_switch[0] = mix->save_master_switch[0];
  	mix->master_switch[1] = mix->save_master_switch[1];
085e6fc96   Colin Leroy   [PATCH] pmac: sav...
1205
1206
  	mix->master_vol[0] = mix->save_master_vol[0];
  	mix->master_vol[1] = mix->save_master_vol[1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
  	tumbler_reset_audio(chip);
  	if (mix->i2c.client && mix->i2c.init_client) {
  		if (mix->i2c.init_client(&mix->i2c) < 0)
  			printk(KERN_ERR "tumbler_init_client error
  ");
  	} else
  		printk(KERN_ERR "tumbler: i2c is not initialized
  ");
  	if (chip->model == PMAC_TUMBLER) {
  		tumbler_set_mono_volume(mix, &tumbler_pcm_vol_info);
  		tumbler_set_mono_volume(mix, &tumbler_bass_vol_info);
  		tumbler_set_mono_volume(mix, &tumbler_treble_vol_info);
  		tumbler_set_drc(mix);
  	} else {
  		snapper_set_mix_vol(mix, VOL_IDX_PCM);
  		snapper_set_mix_vol(mix, VOL_IDX_PCM2);
  		snapper_set_mix_vol(mix, VOL_IDX_ADC);
  		tumbler_set_mono_volume(mix, &snapper_bass_vol_info);
  		tumbler_set_mono_volume(mix, &snapper_treble_vol_info);
  		snapper_set_drc(mix);
  		snapper_set_capture_source(mix);
  	}
  	tumbler_set_master_volume(mix);
  	if (chip->update_automute)
  		chip->update_automute(chip, 0);
c0ce5c522   Guido Guenther   [PATCH] PowerBook...
1232
1233
  	if (mix->headphone_irq >= 0) {
  		unsigned char val;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1234
  		enable_irq(mix->headphone_irq);
c0ce5c522   Guido Guenther   [PATCH] PowerBook...
1235
1236
1237
1238
  		/* activate headphone status interrupts */
  		val = do_gpio_read(&mix->hp_detect);
  		do_gpio_write(&mix->hp_detect, val | 0x80);
  	}
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1239
1240
  	if (mix->lineout_irq >= 0)
  		enable_irq(mix->lineout_irq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1241
1242
1243
1244
  }
  #endif
  
  /* initialize tumbler */
15afafc25   Bill Pemberton   ALSA: ppc: remove...
1245
  static int tumbler_init(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1246
  {
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1247
  	int irq;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1248
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1249

7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
  	if (tumbler_find_device("audio-hw-reset",
  				"platform-do-hw-reset",
  				&mix->audio_reset, 0) < 0)
  		tumbler_find_device("hw-reset",
  				    "platform-do-hw-reset",
  				    &mix->audio_reset, 1);
  	if (tumbler_find_device("amp-mute",
  				"platform-do-amp-mute",
  				&mix->amp_mute, 0) < 0)
  		tumbler_find_device("amp-mute",
  				    "platform-do-amp-mute",
  				    &mix->amp_mute, 1);
  	if (tumbler_find_device("headphone-mute",
  				"platform-do-headphone-mute",
  				&mix->hp_mute, 0) < 0)
  		tumbler_find_device("headphone-mute",
  				    "platform-do-headphone-mute",
  				    &mix->hp_mute, 1);
  	if (tumbler_find_device("line-output-mute",
  				"platform-do-lineout-mute",
  				&mix->line_mute, 0) < 0)
  		tumbler_find_device("line-output-mute",
  				   "platform-do-lineout-mute",
  				    &mix->line_mute, 1);
  	irq = tumbler_find_device("headphone-detect",
  				  NULL, &mix->hp_detect, 0);
ef24ba709   Michael Ellerman   powerpc: Remove a...
1276
  	if (irq <= 0)
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1277
1278
  		irq = tumbler_find_device("headphone-detect",
  					  NULL, &mix->hp_detect, 1);
ef24ba709   Michael Ellerman   powerpc: Remove a...
1279
  	if (irq <= 0)
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1280
1281
1282
1283
1284
  		irq = tumbler_find_device("keywest-gpio15",
  					  NULL, &mix->hp_detect, 1);
  	mix->headphone_irq = irq;
   	irq = tumbler_find_device("line-output-detect",
  				  NULL, &mix->line_detect, 0);
ef24ba709   Michael Ellerman   powerpc: Remove a...
1285
  	if (irq <= 0)
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1286
1287
  		irq = tumbler_find_device("line-output-detect",
  					  NULL, &mix->line_detect, 1);
ef24ba709   Michael Ellerman   powerpc: Remove a...
1288
  	if (IS_G4DA && irq <= 0)
b6d733500   Risto Suominen   ALSA: powermac - ...
1289
1290
  		irq = tumbler_find_device("keywest-gpio16",
  					  NULL, &mix->line_detect, 1);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1291
  	mix->lineout_irq = irq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1292
1293
  
  	tumbler_reset_audio(chip);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1294
1295
1296
    
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1297
  static void tumbler_cleanup(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1298
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1299
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1300
1301
1302
1303
1304
  	if (! mix)
  		return;
  
  	if (mix->headphone_irq >= 0)
  		free_irq(mix->headphone_irq, chip);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1305
1306
  	if (mix->lineout_irq >= 0)
  		free_irq(mix->lineout_irq, chip);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
  	tumbler_gpio_free(&mix->audio_reset);
  	tumbler_gpio_free(&mix->amp_mute);
  	tumbler_gpio_free(&mix->hp_mute);
  	tumbler_gpio_free(&mix->hp_detect);
  	snd_pmac_keywest_cleanup(&mix->i2c);
  	kfree(mix);
  	chip->mixer_data = NULL;
  }
  
  /* exported */
15afafc25   Bill Pemberton   ALSA: ppc: remove...
1317
  int snd_pmac_tumbler_init(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1318
1319
  {
  	int i, err;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1320
  	struct pmac_tumbler *mix;
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1321
  	const u32 *paddr;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1322
  	struct device_node *tas_node, *np;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1323
  	char *chipname;
0d63e4f9e   Jan Blunck   Dont touch fs_str...
1324
  	request_module("i2c-powermac");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1325

59feddb25   Panagiotis Issaris   [ALSA] Conversion...
1326
  	mix = kzalloc(sizeof(*mix), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1327
1328
  	if (! mix)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1329
1330
1331
1332
  	mix->headphone_irq = -1;
  
  	chip->mixer_data = mix;
  	chip->mixer_free = tumbler_cleanup;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1333
1334
1335
1336
1337
  	mix->anded_reset = 0;
  	mix->reset_on_sleep = 1;
  
  	for (np = chip->node->child; np; np = np->sibling) {
  		if (!strcmp(np->name, "sound")) {
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1338
  			if (of_get_property(np, "has-anded-reset", NULL))
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1339
  				mix->anded_reset = 1;
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1340
  			if (of_get_property(np, "layout-id", NULL))
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1341
1342
1343
1344
  				mix->reset_on_sleep = 0;
  			break;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1345
1346
1347
1348
  	if ((err = tumbler_init(chip)) < 0)
  		return err;
  
  	/* set up TAS */
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1349
  	tas_node = of_find_node_by_name(NULL, "deq");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1350
  	if (tas_node == NULL)
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1351
  		tas_node = of_find_node_by_name(NULL, "codec");
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1352
  	if (tas_node == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1353
  		return -ENODEV;
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1354
  	paddr = of_get_property(tas_node, "i2c-address", NULL);
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1355
  	if (paddr == NULL)
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1356
  		paddr = of_get_property(tas_node, "reg", NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1357
1358
1359
1360
  	if (paddr)
  		mix->i2c.addr = (*paddr) >> 1;
  	else
  		mix->i2c.addr = TAS_I2C_ADDR;
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1361
  	of_node_put(tas_node);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1362

7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1363
1364
  	DBG("(I) TAS i2c address is: %x
  ", mix->i2c.addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
  	if (chip->model == PMAC_TUMBLER) {
  		mix->i2c.init_client = tumbler_init_client;
  		mix->i2c.name = "TAS3001c";
  		chipname = "Tumbler";
  	} else {
  		mix->i2c.init_client = snapper_init_client;
  		mix->i2c.name = "TAS3004";
  		chipname = "Snapper";
  	}
  
  	if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0)
  		return err;
  
  	/*
  	 * build mixers
  	 */
  	sprintf(chip->card->mixername, "PowerMac %s", chipname);
  
  	if (chip->model == PMAC_TUMBLER) {
  		for (i = 0; i < ARRAY_SIZE(tumbler_mixers); i++) {
  			if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0)
  				return err;
  		}
  	} else {
  		for (i = 0; i < ARRAY_SIZE(snapper_mixers); i++) {
  			if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snapper_mixers[i], chip))) < 0)
  				return err;
  		}
  	}
  	chip->master_sw_ctl = snd_ctl_new1(&tumbler_hp_sw, chip);
  	if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0)
  		return err;
  	chip->speaker_sw_ctl = snd_ctl_new1(&tumbler_speaker_sw, chip);
  	if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0)
  		return err;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1400
1401
1402
1403
1404
  	if (mix->line_mute.addr != 0) {
  		chip->lineout_sw_ctl = snd_ctl_new1(&tumbler_lineout_sw, chip);
  		if ((err = snd_ctl_add(chip->card, chip->lineout_sw_ctl)) < 0)
  			return err;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1405
1406
1407
  	chip->drc_sw_ctl = snd_ctl_new1(&tumbler_drc_sw, chip);
  	if ((err = snd_ctl_add(chip->card, chip->drc_sw_ctl)) < 0)
  		return err;
f32838530   Takashi Iwai   [ALSA] powermac -...
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
  	/* set initial DRC range to 60% */
  	if (chip->model == PMAC_TUMBLER)
  		mix->drc_range = (TAS3001_DRC_MAX * 6) / 10;
  	else
  		mix->drc_range = (TAS3004_DRC_MAX * 6) / 10;
  	mix->drc_enable = 1; /* will be changed later if AUTO_DRC is set */
  	if (chip->model == PMAC_TUMBLER)
  		tumbler_set_drc(mix);
  	else
  		snapper_set_drc(mix);
8c8709334   Benjamin Herrenschmidt   [PATCH] ppc32: Re...
1418
  #ifdef CONFIG_PM
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1419
  	chip->suspend = tumbler_suspend;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1420
1421
  	chip->resume = tumbler_resume;
  #endif
c4028958b   David Howells   WorkStruct: make ...
1422
1423
  	INIT_WORK(&device_change, device_change_handler);
  	device_change_chip = chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1424
1425
  
  #ifdef PMAC_SUPPORT_AUTOMUTE
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1426
1427
  	if ((mix->headphone_irq >=0 || mix->lineout_irq >= 0)
  	    && (err = snd_pmac_add_automute(chip)) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1428
1429
1430
1431
  		return err;
  	chip->detect_headphone = tumbler_detect_headphone;
  	chip->update_automute = tumbler_update_automute;
  	tumbler_update_automute(chip, 0); /* update the status only */
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
  
  	/* activate headphone status interrupts */
    	if (mix->headphone_irq >= 0) {
  		unsigned char val;
  		if ((err = request_irq(mix->headphone_irq, headphone_intr, 0,
  				       "Sound Headphone Detection", chip)) < 0)
  			return 0;
  		/* activate headphone status interrupts */
  		val = do_gpio_read(&mix->hp_detect);
  		do_gpio_write(&mix->hp_detect, val | 0x80);
  	}
    	if (mix->lineout_irq >= 0) {
  		unsigned char val;
  		if ((err = request_irq(mix->lineout_irq, headphone_intr, 0,
  				       "Sound Lineout Detection", chip)) < 0)
  			return 0;
  		/* activate headphone status interrupts */
  		val = do_gpio_read(&mix->line_detect);
  		do_gpio_write(&mix->line_detect, val | 0x80);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1452
1453
1454
1455
  #endif
  
  	return 0;
  }