Blame view

sound/pci/cs5535audio/cs5535audio_olpc.c 4.73 KB
b5ccc57b0   Andres Salomon   ALSA: cs5535audio...
1
2
3
4
5
6
7
8
9
10
11
  /*
   * OLPC XO-1 additional sound features
   *
   * Copyright © 2006  Jaya Kumar <jayakumar.lkml@gmail.com>
   * Copyright © 2007-2008  Andres Salomon <dilinger@debian.org>
   *
   * 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.
   */
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
12
13
14
15
  #include <sound/core.h>
  #include <sound/info.h>
  #include <sound/control.h>
  #include <sound/ac97_codec.h>
3c5549467   Andres Salomon   ALSA: cs5535audio...
16
  #include <linux/gpio.h>
c8974be54   Jordan Crouse   ALSA: cs5535audio...
17
18
  
  #include <asm/olpc.h>
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
19
  #include "cs5535audio.h"
3c5549467   Andres Salomon   ALSA: cs5535audio...
20
  #define DRV_NAME "cs5535audio-olpc"
d6276b780   Andres Salomon   ALSA: cs5535audio...
21
22
23
24
25
26
27
28
  /*
   * OLPC has an additional feature on top of the regular AD1888 codec features.
   * It has an Analog Input mode that is switched into (after disabling the
   * High Pass Filter) via GPIO.  It is supported on B2 and later models.
   */
  void olpc_analog_input(struct snd_ac97 *ac97, int on)
  {
  	int err;
0fb497f5b   Andres Salomon   ALSA: cs5535audio...
29
30
  	if (!machine_is_olpc())
  		return;
d6276b780   Andres Salomon   ALSA: cs5535audio...
31
32
33
34
35
36
37
38
39
40
  	/* update the High Pass Filter (via AC97_AD_TEST2) */
  	err = snd_ac97_update_bits(ac97, AC97_AD_TEST2,
  			1 << AC97_AD_HPFD_SHIFT, on << AC97_AD_HPFD_SHIFT);
  	if (err < 0) {
  		snd_printk(KERN_ERR "setting High Pass Filter - %d
  ", err);
  		return;
  	}
  
  	/* set Analog Input through GPIO */
3c5549467   Andres Salomon   ALSA: cs5535audio...
41
  	gpio_set_value(OLPC_GPIO_MIC_AC, on);
d6276b780   Andres Salomon   ALSA: cs5535audio...
42
  }
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
43

bf1e52783   Andres Salomon   ALSA: cs5535audio...
44
45
46
47
48
49
  /*
   * OLPC XO-1's V_REFOUT is a mic bias enable.
   */
  void olpc_mic_bias(struct snd_ac97 *ac97, int on)
  {
  	int err;
0fb497f5b   Andres Salomon   ALSA: cs5535audio...
50
51
  	if (!machine_is_olpc())
  		return;
bf1e52783   Andres Salomon   ALSA: cs5535audio...
52
53
54
55
56
57
58
  	on = on ? 0 : 1;
  	err = snd_ac97_update_bits(ac97, AC97_AD_MISC,
  			1 << AC97_AD_VREFD_SHIFT, on << AC97_AD_VREFD_SHIFT);
  	if (err < 0)
  		snd_printk(KERN_ERR "setting MIC Bias - %d
  ", err);
  }
466ae3055   Andres Salomon   ALSA: cs5535audio...
59
60
  static int olpc_dc_info(struct snd_kcontrol *kctl,
  		struct snd_ctl_elem_info *uinfo)
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
61
62
63
64
65
66
67
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 1;
  	return 0;
  }
466ae3055   Andres Salomon   ALSA: cs5535audio...
68
  static int olpc_dc_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
69
  {
3c5549467   Andres Salomon   ALSA: cs5535audio...
70
  	v->value.integer.value[0] = gpio_get_value(OLPC_GPIO_MIC_AC);
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
71
72
  	return 0;
  }
466ae3055   Andres Salomon   ALSA: cs5535audio...
73
  static int olpc_dc_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
74
  {
466ae3055   Andres Salomon   ALSA: cs5535audio...
75
  	struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
76

466ae3055   Andres Salomon   ALSA: cs5535audio...
77
  	olpc_analog_input(cs5535au->ac97, v->value.integer.value[0]);
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
78
79
  	return 1;
  }
bf1e52783   Andres Salomon   ALSA: cs5535audio...
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  static int olpc_mic_info(struct snd_kcontrol *kctl,
  		struct snd_ctl_elem_info *uinfo)
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 1;
  	return 0;
  }
  
  static int olpc_mic_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
  {
  	struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
  	struct snd_ac97 *ac97 = cs5535au->ac97;
  	int i;
  
  	i = (snd_ac97_read(ac97, AC97_AD_MISC) >> AC97_AD_VREFD_SHIFT) & 0x1;
  	v->value.integer.value[0] = i ? 0 : 1;
  	return 0;
  }
  
  static int olpc_mic_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *v)
  {
  	struct cs5535audio *cs5535au = snd_kcontrol_chip(kctl);
  
  	olpc_mic_bias(cs5535au->ac97, v->value.integer.value[0]);
  	return 1;
  }
  
  static struct snd_kcontrol_new olpc_cs5535audio_ctls[] __devinitdata = {
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
110
111
  {
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
466ae3055   Andres Salomon   ALSA: cs5535audio...
112
113
114
115
  	.name = "DC Mode Enable",
  	.info = olpc_dc_info,
  	.get = olpc_dc_get,
  	.put = olpc_dc_put,
b5ccc57b0   Andres Salomon   ALSA: cs5535audio...
116
  	.private_value = 0,
bf1e52783   Andres Salomon   ALSA: cs5535audio...
117
118
119
120
121
122
123
124
125
  },
  {
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	.name = "MIC Bias Enable",
  	.info = olpc_mic_info,
  	.get = olpc_mic_get,
  	.put = olpc_mic_put,
  	.private_value = 0,
  },
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
126
  };
3556d1846   Andres Salomon   ALSA: cs5535audio...
127
128
129
130
131
132
133
134
135
136
  void __devinit olpc_prequirks(struct snd_card *card,
  		struct snd_ac97_template *ac97)
  {
  	if (!machine_is_olpc())
  		return;
  
  	/* invert EAPD if on an OLPC B3 or higher */
  	if (olpc_board_at_least(olpc_board_pre(0xb3)))
  		ac97->scaps |= AC97_SCAP_INV_EAPD;
  }
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
137
138
  int __devinit olpc_quirks(struct snd_card *card, struct snd_ac97 *ac97)
  {
466ae3055   Andres Salomon   ALSA: cs5535audio...
139
  	struct snd_ctl_elem_id elem;
bf1e52783   Andres Salomon   ALSA: cs5535audio...
140
  	int i, err;
466ae3055   Andres Salomon   ALSA: cs5535audio...
141

c8974be54   Jordan Crouse   ALSA: cs5535audio...
142
143
  	if (!machine_is_olpc())
  		return 0;
3c5549467   Andres Salomon   ALSA: cs5535audio...
144
145
146
147
148
149
  	if (gpio_request(OLPC_GPIO_MIC_AC, DRV_NAME)) {
  		printk(KERN_ERR DRV_NAME ": unable to allocate MIC GPIO
  ");
  		return -EIO;
  	}
  	gpio_direction_output(OLPC_GPIO_MIC_AC, 0);
466ae3055   Andres Salomon   ALSA: cs5535audio...
150
151
152
153
154
  	/* drop the original AD1888 HPF control */
  	memset(&elem, 0, sizeof(elem));
  	elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
  	strncpy(elem.name, "High Pass Filter Enable", sizeof(elem.name));
  	snd_ctl_remove_id(card, &elem);
bf1e52783   Andres Salomon   ALSA: cs5535audio...
155
156
157
158
159
160
161
162
163
164
  	/* drop the original V_REFOUT control */
  	memset(&elem, 0, sizeof(elem));
  	elem.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
  	strncpy(elem.name, "V_REFOUT Enable", sizeof(elem.name));
  	snd_ctl_remove_id(card, &elem);
  
  	/* add the OLPC-specific controls */
  	for (i = 0; i < ARRAY_SIZE(olpc_cs5535audio_ctls); i++) {
  		err = snd_ctl_add(card, snd_ctl_new1(&olpc_cs5535audio_ctls[i],
  				ac97->private_data));
3c5549467   Andres Salomon   ALSA: cs5535audio...
165
166
  		if (err < 0) {
  			gpio_free(OLPC_GPIO_MIC_AC);
bf1e52783   Andres Salomon   ALSA: cs5535audio...
167
  			return err;
3c5549467   Andres Salomon   ALSA: cs5535audio...
168
  		}
bf1e52783   Andres Salomon   ALSA: cs5535audio...
169
  	}
c8f0eeebc   Andres Salomon   ALSA: cs5535audio...
170
171
  	/* turn off the mic by default */
  	olpc_mic_bias(ac97, 0);
bf1e52783   Andres Salomon   ALSA: cs5535audio...
172
  	return 0;
57d4bf6d8   Jaya Kumar   ALSA: cs5535audio...
173
  }
3c5549467   Andres Salomon   ALSA: cs5535audio...
174
175
176
177
178
  
  void __devexit olpc_quirks_cleanup(void)
  {
  	gpio_free(OLPC_GPIO_MIC_AC);
  }