Blame view

sound/ppc/tumbler.c 38.2 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
31
32
33
  #include <linux/kmod.h>
  #include <linux/slab.h>
  #include <linux/interrupt.h>
  #include <sound/core.h>
  #include <asm/io.h>
  #include <asm/irq.h>
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
34
  #include <asm/machdep.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
  #include <asm/pmac_feature.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
37
  #include "pmac.h"
  #include "tumbler_volume.h"
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
38
39
40
  #undef DEBUG
  
  #ifdef DEBUG
6da671138   Takashi Iwai   ALSA: powermac - ...
41
  #define DBG(fmt...) printk(KERN_DEBUG fmt)
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
42
43
44
  #else
  #define DBG(fmt...)
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
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
  /* 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...
78
  struct pmac_gpio {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79
  	unsigned int addr;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
80
81
82
  	u8 active_val;
  	u8 inactive_val;
  	u8 active_state;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
83
84
85
86
87
88
89
90
91
92
  };
  
  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
93
  	int headphone_irq;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
94
  	int lineout_irq;
085e6fc96   Colin Leroy   [PATCH] pmac: sav...
95
  	unsigned int save_master_vol[2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
  	unsigned int master_vol[2];
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
97
  	unsigned int save_master_switch[2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
101
102
103
  	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...
104
105
106
107
  	int anded_reset;
  	int auto_mute_notify;
  	int reset_on_sleep;
  	u8  acs;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
108
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109
110
111
112
  
  
  /*
   */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
113
  static int send_init_client(struct pmac_keywest *i2c, unsigned int *regs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
116
117
118
119
120
121
  {
  	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...
122
123
  			DBG("(W) i2c error %d
  ", err);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
127
128
129
130
131
  			mdelay(10);
  		} while (count--);
  		if (err < 0)
  			return -ENXIO;
  		regs += 2;
  	}
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
132
  static int tumbler_init_client(struct pmac_keywest *i2c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
134
135
136
137
138
  {
  	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...
139
140
  	DBG("(I) tumbler init client
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
  	return send_init_client(i2c, regs);
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
143
  static int snapper_init_client(struct pmac_keywest *i2c)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
144
145
146
147
148
149
150
151
152
153
  {
  	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...
154
155
  	DBG("(I) snapper init client
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
160
161
  	return send_init_client(i2c, regs);
  }
  	
  /*
   * gpio access
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
166
  #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
167

65b29f503   Takashi Iwai   [ALSA] Remove xxx...
168
  static void write_audio_gpio(struct pmac_gpio *gp, int active)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
169
170
171
  {
  	if (! gp->addr)
  		return;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
172
  	active = active ? gp->active_val : gp->inactive_val;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
173
174
175
  	do_gpio_write(gp, active);
  	DBG("(I) gpio %x write %d
  ", gp->addr, active);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
176
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
177
  static int check_audio_gpio(struct pmac_gpio *gp)
4be8dc7ff   Benjamin Herrenschmidt   [PATCH] ppc64: im...
178
179
180
181
182
183
184
  {
  	int ret;
  
  	if (! gp->addr)
  		return 0;
  
  	ret = do_gpio_read(gp);
2fd53a7e9   Andreas Schwab   [ALSA] [PPC,SOUND...
185
  	return (ret & 0x1) == (gp->active_val & 0x1);
4be8dc7ff   Benjamin Herrenschmidt   [PATCH] ppc64: im...
186
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
187
  static int read_audio_gpio(struct pmac_gpio *gp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
189
190
191
  {
  	int ret;
  	if (! gp->addr)
  		return 0;
2fd53a7e9   Andreas Schwab   [ALSA] [PPC,SOUND...
192
193
  	ret = do_gpio_read(gp);
  	ret = (ret & 0x02) !=0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
197
198
199
  	return ret == gp->active_state;
  }
  
  /*
   * update master volume
   */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
200
  static int tumbler_set_master_volume(struct pmac_tumbler *mix)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
202
203
204
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
  {
  	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: ...
233
234
  	if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_VOL, 6,
  					   block) < 0) {
6da671138   Takashi Iwai   ALSA: powermac - ...
235
236
  		snd_printk(KERN_ERR "failed to set volume 
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
240
241
242
243
  		return -EINVAL;
  	}
  	return 0;
  }
  
  
  /* output volume */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
244
245
  static int tumbler_info_master_volume(struct snd_kcontrol *kcontrol,
  				      struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
250
251
252
  {
  	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...
253
254
  static int tumbler_get_master_volume(struct snd_kcontrol *kcontrol,
  				     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
256
257
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
258

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
260
261
262
  	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...
263
264
  static int tumbler_put_master_volume(struct snd_kcontrol *kcontrol,
  				     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
266
267
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
268
  	unsigned int vol[2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
  	int change;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
270
271
272
273
274
275
276
  	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
277
  	if (change) {
d4079ac49   Takashi Iwai   [ALSA] powermac -...
278
279
  		mix->master_vol[0] = vol[0];
  		mix->master_vol[1] = vol[1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
280
281
282
283
284
285
  		tumbler_set_master_volume(mix);
  	}
  	return change;
  }
  
  /* output switch */
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
286
287
  static int tumbler_get_master_switch(struct snd_kcontrol *kcontrol,
  				     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
288
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
289
290
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
291

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
292
293
294
295
  	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...
296
297
  static int tumbler_put_master_switch(struct snd_kcontrol *kcontrol,
  				     struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
299
300
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
301
  	int change;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
  	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...
318
  static int tumbler_set_drc(struct pmac_tumbler *mix)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
  {
  	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: ...
337
338
  	if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_DRC,
  					   2, val) < 0) {
6da671138   Takashi Iwai   ALSA: powermac - ...
339
340
  		snd_printk(KERN_ERR "failed to set DRC
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
341
342
343
344
345
346
347
348
349
350
  		return -EINVAL;
  	}
  	return 0;
  }
  
  /*
   * TAS3004
   */
  
  #define TAS3004_DRC_MAX		0xef
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
351
  static int snapper_set_drc(struct pmac_tumbler *mix)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
  {
  	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: ...
372
373
  	if (i2c_smbus_write_i2c_block_data(mix->i2c.client, TAS_REG_DRC,
  					   6, val) < 0) {
6da671138   Takashi Iwai   ALSA: powermac - ...
374
375
  		snd_printk(KERN_ERR "failed to set DRC
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
376
377
378
379
  		return -EINVAL;
  	}
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
380
381
  static int tumbler_info_drc_value(struct snd_kcontrol *kcontrol,
  				  struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
382
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
383
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
384
385
386
387
388
389
390
  	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...
391
392
  static int tumbler_get_drc_value(struct snd_kcontrol *kcontrol,
  				 struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
393
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
394
395
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
397
398
399
400
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
  	ucontrol->value.integer.value[0] = mix->drc_range;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
401
402
  static int tumbler_put_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;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
406
  	unsigned int val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
410
  	int change;
  
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
411
412
413
414
415
416
417
418
419
  	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
420
  	if (change) {
d4079ac49   Takashi Iwai   [ALSA] powermac -...
421
  		mix->drc_range = val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
424
425
426
427
428
  		if (chip->model == PMAC_TUMBLER)
  			tumbler_set_drc(mix);
  		else
  			snapper_set_drc(mix);
  	}
  	return change;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
429
430
  static int tumbler_get_drc_switch(struct snd_kcontrol *kcontrol,
  				  struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
431
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
432
433
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
434
435
436
437
438
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
  	ucontrol->value.integer.value[0] = mix->drc_enable;
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
439
440
  static int tumbler_put_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
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
  	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...
471
472
  static int tumbler_set_mono_volume(struct pmac_tumbler *mix,
  				   struct tumbler_mono_vol *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
476
477
478
479
480
481
482
483
484
485
486
  {
  	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: ...
487
488
  	if (i2c_smbus_write_i2c_block_data(mix->i2c.client, info->reg,
  					   info->bytes, block) < 0) {
6da671138   Takashi Iwai   ALSA: powermac - ...
489
490
491
  		snd_printk(KERN_ERR "failed to set mono volume %d
  ",
  			   info->index);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
492
493
494
495
  		return -EINVAL;
  	}
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
496
497
  static int tumbler_info_mono(struct snd_kcontrol *kcontrol,
  			     struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
498
499
500
501
502
503
504
505
506
  {
  	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...
507
508
  static int tumbler_get_mono(struct snd_kcontrol *kcontrol,
  			    struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
510
  {
  	struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
511
512
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
515
516
517
  	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...
518
519
  static int tumbler_put_mono(struct snd_kcontrol *kcontrol,
  			    struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
520
521
  {
  	struct tumbler_mono_vol *info = (struct tumbler_mono_vol *)kcontrol->private_value;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
522
523
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
524
  	unsigned int vol;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
525
526
527
528
  	int change;
  
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
529
530
531
532
  	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
533
  	if (change) {
d4079ac49   Takashi Iwai   [ALSA] powermac -...
534
  		mix->mono_vol[info->index] = vol;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
535
536
537
538
539
540
541
542
543
544
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
  		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...
605
  static int snapper_set_mix_vol1(struct pmac_tumbler *mix, int idx, int ch, int reg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
  {
  	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: ...
622
623
  	if (i2c_smbus_write_i2c_block_data(mix->i2c.client, reg,
  					   9, block) < 0) {
6da671138   Takashi Iwai   ALSA: powermac - ...
624
625
  		snd_printk(KERN_ERR "failed to set mono volume %d
  ", reg);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
626
627
628
629
  		return -EINVAL;
  	}
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
630
  static int snapper_set_mix_vol(struct pmac_tumbler *mix, int idx)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
631
632
633
634
635
636
637
638
  {
  	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...
639
640
  static int snapper_info_mix(struct snd_kcontrol *kcontrol,
  			    struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
641
642
643
644
645
646
647
  {
  	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...
648
649
  static int snapper_get_mix(struct snd_kcontrol *kcontrol,
  			   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
650
651
  {
  	int idx = (int)kcontrol->private_value;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
652
653
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
654
655
656
657
658
659
  	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...
660
661
  static int snapper_put_mix(struct snd_kcontrol *kcontrol,
  			   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
662
663
  {
  	int idx = (int)kcontrol->private_value;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
664
665
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
666
  	unsigned int vol[2];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
667
668
669
670
  	int change;
  
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
d4079ac49   Takashi Iwai   [ALSA] powermac -...
671
672
673
674
675
676
677
  	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
678
  	if (change) {
d4079ac49   Takashi Iwai   [ALSA] powermac -...
679
680
  		mix->mix_vol[idx][0] = vol[0];
  		mix->mix_vol[idx][1] = vol[1];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
684
685
686
687
  		snapper_set_mix_vol(mix, idx);
  	}
  	return change;
  }
  
  
  /*
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
688
689
   * 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
690
   */
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
691
  enum { TUMBLER_MUTE_HP, TUMBLER_MUTE_AMP, TUMBLER_MUTE_LINE };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692

65b29f503   Takashi Iwai   [ALSA] Remove xxx...
693
694
  static int tumbler_get_mute_switch(struct snd_kcontrol *kcontrol,
  				   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
695
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
696
697
698
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
  	struct pmac_gpio *gp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
699
700
  	if (! (mix = chip->mixer_data))
  		return -ENODEV;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
701
702
703
704
705
706
707
708
709
710
711
712
  	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...
713
  	ucontrol->value.integer.value[0] = !check_audio_gpio(gp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
715
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
716
717
  static int tumbler_put_mute_switch(struct snd_kcontrol *kcontrol,
  				   struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
718
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
719
720
721
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix;
  	struct pmac_gpio *gp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
722
723
724
725
726
727
728
  	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...
729
730
731
732
733
734
735
736
737
738
739
740
  	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...
741
  	val = ! check_audio_gpio(gp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
742
743
744
745
746
747
  	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...
748
  static int snapper_set_capture_source(struct pmac_tumbler *mix)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
749
750
751
  {
  	if (! mix->i2c.client)
  		return -ENODEV;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
752
753
754
755
756
  	if (mix->capture_source)
  		mix->acs = mix->acs |= 2;
  	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
757
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
758
759
  static int snapper_info_capture_source(struct snd_kcontrol *kcontrol,
  				       struct snd_ctl_elem_info *uinfo)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
760
761
762
763
764
765
766
767
768
769
770
771
  {
  	static char *texts[2] = {
  		"Line", "Mic"
  	};
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
  	uinfo->count = 1;
  	uinfo->value.enumerated.items = 2;
  	if (uinfo->value.enumerated.item > 1)
  		uinfo->value.enumerated.item = 1;
  	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
772
773
  static int snapper_get_capture_source(struct snd_kcontrol *kcontrol,
  				      struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
774
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
775
776
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
777

dec44dbe0   Kamalesh Babulal   [ALSA] powermac -...
778
  	ucontrol->value.enumerated.item[0] = mix->capture_source;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779
780
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
781
782
  static int snapper_put_capture_source(struct snd_kcontrol *kcontrol,
  				      struct snd_ctl_elem_value *ucontrol)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
783
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
784
785
  	struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
786
  	int change;
dec44dbe0   Kamalesh Babulal   [ALSA] powermac -...
787
  	change = ucontrol->value.enumerated.item[0] != mix->capture_source;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
788
  	if (change) {
d4079ac49   Takashi Iwai   [ALSA] powermac -...
789
  		mix->capture_source = !!ucontrol->value.enumerated.item[0];
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
  		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, \
  }
  
  
  /*
   */
3e1e0a5dd   Takashi Iwai   ALSA: powermac - ...
808
  static struct snd_kcontrol_new tumbler_mixers[] __devinitdata = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
  	{ .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
  	},
  };
3e1e0a5dd   Takashi Iwai   ALSA: powermac - ...
831
  static struct snd_kcontrol_new snapper_mixers[] __devinitdata = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832
833
834
835
836
837
838
839
840
841
842
843
844
  	{ .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 - ...
845
846
  	/* 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
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
  	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
  	},
  };
3e1e0a5dd   Takashi Iwai   ALSA: powermac - ...
863
  static struct snd_kcontrol_new tumbler_hp_sw __devinitdata = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
864
865
866
867
868
869
870
  	.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,
  };
3e1e0a5dd   Takashi Iwai   ALSA: powermac - ...
871
  static struct snd_kcontrol_new tumbler_speaker_sw __devinitdata = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
ad1cd7450   Jaroslav Kysela   ALSA: rename "PC ...
873
  	.name = "Speaker Playback Switch",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
875
876
877
878
  	.info = snd_pmac_boolean_mono_info,
  	.get = tumbler_get_mute_switch,
  	.put = tumbler_put_mute_switch,
  	.private_value = TUMBLER_MUTE_AMP,
  };
3e1e0a5dd   Takashi Iwai   ALSA: powermac - ...
879
  static struct snd_kcontrol_new tumbler_lineout_sw __devinitdata = {
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
880
881
882
883
884
885
886
  	.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,
  };
3e1e0a5dd   Takashi Iwai   ALSA: powermac - ...
887
  static struct snd_kcontrol_new tumbler_drc_sw __devinitdata = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
889
890
891
892
893
894
895
896
897
898
899
  	.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...
900
  static int tumbler_detect_headphone(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
902
  	struct pmac_tumbler *mix = chip->mixer_data;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
903
904
905
906
907
908
  	int detect = 0;
  
  	if (mix->hp_detect.addr)
  		detect |= read_audio_gpio(&mix->hp_detect);
  	return detect;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
909
  static int tumbler_detect_lineout(struct snd_pmac *chip)
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
910
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
911
  	struct pmac_tumbler *mix = chip->mixer_data;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
912
913
914
915
916
  	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
917
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
918
919
  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
920
  {
4be8dc7ff   Benjamin Herrenschmidt   [PATCH] ppc64: im...
921
  	if (check_audio_gpio(gp) != val) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
922
923
  		write_audio_gpio(gp, val);
  		if (do_notify)
4be8dc7ff   Benjamin Herrenschmidt   [PATCH] ppc64: im...
924
925
  			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
  				       &sw->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
926
927
928
929
  	}
  }
  
  static struct work_struct device_change;
c4028958b   David Howells   WorkStruct: make ...
930
  static struct snd_pmac *device_change_chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
931

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

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

b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1094
  	gp->addr = addr & 0x0000ffff;
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1095
  	/* Try to find the active state, default to 0 ! */
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1096
  	base = of_get_property(node, "audio-gpio-active-state", NULL);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1097
  	if (base) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1098
  		gp->active_state = *base;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1099
1100
1101
  		gp->active_val = (*base) ? 0x5 : 0x4;
  		gp->inactive_val = (*base) ? 0x4 : 0x5;
  	} else {
abddd185a   Jeremy Kerr   [POWERPC] sound: ...
1102
  		const u32 *prop = NULL;
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1103
  		gp->active_state = 0;
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1104
1105
1106
1107
1108
1109
1110
  		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 ...
1111
  			prop = of_get_property(node, platform, NULL);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
  		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
1127

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

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

b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1196
1197
1198
  	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...
1199
1200
  	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
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
  	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...
1226
1227
  	if (mix->headphone_irq >= 0) {
  		unsigned char val;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1228
  		enable_irq(mix->headphone_irq);
c0ce5c522   Guido Guenther   [PATCH] PowerBook...
1229
1230
1231
1232
  		/* 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...
1233
1234
  	if (mix->lineout_irq >= 0)
  		enable_irq(mix->lineout_irq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1235
1236
1237
1238
  }
  #endif
  
  /* initialize tumbler */
5c9b6e9e6   Stephen Rothwell   ALSA: sound/ppc: ...
1239
  static int __devinit tumbler_init(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1240
  {
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1241
  	int irq;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1242
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1243

7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
  	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);
0ebfff149   Benjamin Herrenschmidt   [POWERPC] Add new...
1270
  	if (irq <= NO_IRQ)
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1271
1272
  		irq = tumbler_find_device("headphone-detect",
  					  NULL, &mix->hp_detect, 1);
0ebfff149   Benjamin Herrenschmidt   [POWERPC] Add new...
1273
  	if (irq <= NO_IRQ)
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1274
1275
1276
1277
1278
  		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);
0ebfff149   Benjamin Herrenschmidt   [POWERPC] Add new...
1279
   	if (irq <= NO_IRQ)
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1280
1281
1282
  		irq = tumbler_find_device("line-output-detect",
  					  NULL, &mix->line_detect, 1);
  	mix->lineout_irq = irq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1283
1284
  
  	tumbler_reset_audio(chip);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1285
1286
1287
    
  	return 0;
  }
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1288
  static void tumbler_cleanup(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1289
  {
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1290
  	struct pmac_tumbler *mix = chip->mixer_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1291
1292
1293
1294
1295
  	if (! mix)
  		return;
  
  	if (mix->headphone_irq >= 0)
  		free_irq(mix->headphone_irq, chip);
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1296
1297
  	if (mix->lineout_irq >= 0)
  		free_irq(mix->lineout_irq, chip);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
  	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 */
5c9b6e9e6   Stephen Rothwell   ALSA: sound/ppc: ...
1308
  int __devinit snd_pmac_tumbler_init(struct snd_pmac *chip)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1309
1310
  {
  	int i, err;
65b29f503   Takashi Iwai   [ALSA] Remove xxx...
1311
  	struct pmac_tumbler *mix;
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1312
  	const u32 *paddr;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1313
  	struct device_node *tas_node, *np;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1314
  	char *chipname;
0d63e4f9e   Jan Blunck   Dont touch fs_str...
1315
  	request_module("i2c-powermac");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1316

59feddb25   Panagiotis Issaris   [ALSA] Conversion...
1317
  	mix = kzalloc(sizeof(*mix), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1318
1319
  	if (! mix)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1320
1321
1322
1323
  	mix->headphone_irq = -1;
  
  	chip->mixer_data = mix;
  	chip->mixer_free = tumbler_cleanup;
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1324
1325
1326
1327
1328
  	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 ...
1329
  			if (of_get_property(np, "has-anded-reset", NULL))
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1330
  				mix->anded_reset = 1;
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1331
  			if (of_get_property(np, "layout-id", NULL))
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1332
1333
1334
1335
  				mix->reset_on_sleep = 0;
  			break;
  		}
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1336
1337
1338
1339
  	if ((err = tumbler_init(chip)) < 0)
  		return err;
  
  	/* set up TAS */
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1340
  	tas_node = of_find_node_by_name(NULL, "deq");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1341
  	if (tas_node == NULL)
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1342
  		tas_node = of_find_node_by_name(NULL, "codec");
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1343
  	if (tas_node == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1344
  		return -ENODEV;
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1345
  	paddr = of_get_property(tas_node, "i2c-address", NULL);
b75550e1b   Benjamin Herrenschmidt   [PATCH] pmac: sou...
1346
  	if (paddr == NULL)
c4f55b394   Stephen Rothwell   [POWERPC] Rename ...
1347
  		paddr = of_get_property(tas_node, "reg", NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1348
1349
1350
1351
  	if (paddr)
  		mix->i2c.addr = (*paddr) >> 1;
  	else
  		mix->i2c.addr = TAS_I2C_ADDR;
30686ba6d   Stephen Rothwell   [POWERPC] Remove ...
1352
  	of_node_put(tas_node);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1353

7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1354
1355
  	DBG("(I) TAS i2c address is: %x
  ", mix->i2c.addr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1356
1357
1358
1359
1360
1361
1362
1363
1364
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
  	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...
1391
1392
1393
1394
1395
  	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
1396
1397
1398
  	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 -...
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
  	/* 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...
1409
  #ifdef CONFIG_PM
b20af5f59   Benjamin Herrenschmidt   [PATCH] pmac: Imp...
1410
  	chip->suspend = tumbler_suspend;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1411
1412
  	chip->resume = tumbler_resume;
  #endif
c4028958b   David Howells   WorkStruct: make ...
1413
1414
  	INIT_WORK(&device_change, device_change_handler);
  	device_change_chip = chip;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1415
1416
  
  #ifdef PMAC_SUPPORT_AUTOMUTE
7bbd82775   Benjamin Herrenschmidt   [PATCH] ppc64: ve...
1417
1418
  	if ((mix->headphone_irq >=0 || mix->lineout_irq >= 0)
  	    && (err = snd_pmac_add_automute(chip)) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1419
1420
1421
1422
  		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...
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
  
  	/* 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
1443
1444
1445
1446
  #endif
  
  	return 0;
  }