diff options
author | Daniel Mack <daniel@caiaq.de> | 2008-04-30 10:20:52 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2008-05-19 07:19:14 -0400 |
commit | 54e7e6167d29a4a98207884b2fbd28b0b3fe91f6 (patch) | |
tree | 84ada6a443d563fbb3d0a58a18768aeefe24f33e | |
parent | 4f9c16ccfa26691dbb9a5d9e7d5098eb934ccdbe (diff) |
[ALSA] soc - tlv320aic3x - add GPIO support
This patch adds support for AIC3x GPIO lines. They can be configured for
many possible functions as well as be driven manually. I also introduced
i2c read functionality since the GPIO state register has to be read from
hardware every time and can not be served from cache.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r-- | sound/soc/codecs/tlv320aic3x.c | 53 | ||||
-rw-r--r-- | sound/soc/codecs/tlv320aic3x.h | 49 |
2 files changed, 101 insertions, 1 deletions
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 738b3b634d74..957996e0eba2 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c | |||
@@ -138,6 +138,20 @@ static int aic3x_write(struct snd_soc_codec *codec, unsigned int reg, | |||
138 | return -EIO; | 138 | return -EIO; |
139 | } | 139 | } |
140 | 140 | ||
141 | /* | ||
142 | * read from the aic3x register space | ||
143 | */ | ||
144 | static int aic3x_read(struct snd_soc_codec *codec, unsigned int reg, | ||
145 | u8 *value) | ||
146 | { | ||
147 | *value = reg & 0xff; | ||
148 | if (codec->hw_read(codec->control_data, value, 1) != 1) | ||
149 | return -EIO; | ||
150 | |||
151 | aic3x_write_reg_cache(codec, reg, *value); | ||
152 | return 0; | ||
153 | } | ||
154 | |||
141 | #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ | 155 | #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \ |
142 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ | 156 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ |
143 | .info = snd_soc_info_volsw, \ | 157 | .info = snd_soc_info_volsw, \ |
@@ -911,6 +925,33 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event) | |||
911 | return 0; | 925 | return 0; |
912 | } | 926 | } |
913 | 927 | ||
928 | void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state) | ||
929 | { | ||
930 | u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG; | ||
931 | u8 bit = gpio ? 3: 0; | ||
932 | u8 val = aic3x_read_reg_cache(codec, reg) & ~(1 << bit); | ||
933 | aic3x_write(codec, reg, val | (!!state << bit)); | ||
934 | } | ||
935 | EXPORT_SYMBOL_GPL(aic3x_set_gpio); | ||
936 | |||
937 | int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio) | ||
938 | { | ||
939 | u8 reg = gpio ? AIC3X_GPIO2_REG : AIC3X_GPIO1_REG; | ||
940 | u8 val, bit = gpio ? 2: 1; | ||
941 | |||
942 | aic3x_read(codec, reg, &val); | ||
943 | return (val >> bit) & 1; | ||
944 | } | ||
945 | EXPORT_SYMBOL_GPL(aic3x_get_gpio); | ||
946 | |||
947 | int aic3x_headset_detected(struct snd_soc_codec *codec) | ||
948 | { | ||
949 | u8 val; | ||
950 | aic3x_read(codec, AIC3X_RT_IRQ_FLAGS_REG, &val); | ||
951 | return (val >> 2) & 1; | ||
952 | } | ||
953 | EXPORT_SYMBOL_GPL(aic3x_headset_detected); | ||
954 | |||
914 | #define AIC3X_RATES SNDRV_PCM_RATE_8000_96000 | 955 | #define AIC3X_RATES SNDRV_PCM_RATE_8000_96000 |
915 | #define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ | 956 | #define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ |
916 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) | 957 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) |
@@ -977,6 +1018,7 @@ static int aic3x_resume(struct platform_device *pdev) | |||
977 | static int aic3x_init(struct snd_soc_device *socdev) | 1018 | static int aic3x_init(struct snd_soc_device *socdev) |
978 | { | 1019 | { |
979 | struct snd_soc_codec *codec = socdev->codec; | 1020 | struct snd_soc_codec *codec = socdev->codec; |
1021 | struct aic3x_setup_data *setup = socdev->codec_data; | ||
980 | int reg, ret = 0; | 1022 | int reg, ret = 0; |
981 | 1023 | ||
982 | codec->name = "aic3x"; | 1024 | codec->name = "aic3x"; |
@@ -1067,6 +1109,10 @@ static int aic3x_init(struct snd_soc_device *socdev) | |||
1067 | /* off, with power on */ | 1109 | /* off, with power on */ |
1068 | aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot); | 1110 | aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot); |
1069 | 1111 | ||
1112 | /* setup GPIO functions */ | ||
1113 | aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4); | ||
1114 | aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4); | ||
1115 | |||
1070 | aic3x_add_controls(codec); | 1116 | aic3x_add_controls(codec); |
1071 | aic3x_add_widgets(codec); | 1117 | aic3x_add_widgets(codec); |
1072 | ret = snd_soc_register_card(socdev); | 1118 | ret = snd_soc_register_card(socdev); |
@@ -1174,6 +1220,12 @@ static struct i2c_client client_template = { | |||
1174 | .name = "AIC3X", | 1220 | .name = "AIC3X", |
1175 | .driver = &aic3x_i2c_driver, | 1221 | .driver = &aic3x_i2c_driver, |
1176 | }; | 1222 | }; |
1223 | |||
1224 | static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len) | ||
1225 | { | ||
1226 | value[0] = i2c_smbus_read_byte_data(client, value[0]); | ||
1227 | return (len == 1); | ||
1228 | } | ||
1177 | #endif | 1229 | #endif |
1178 | 1230 | ||
1179 | static int aic3x_probe(struct platform_device *pdev) | 1231 | static int aic3x_probe(struct platform_device *pdev) |
@@ -1208,6 +1260,7 @@ static int aic3x_probe(struct platform_device *pdev) | |||
1208 | if (setup->i2c_address) { | 1260 | if (setup->i2c_address) { |
1209 | normal_i2c[0] = setup->i2c_address; | 1261 | normal_i2c[0] = setup->i2c_address; |
1210 | codec->hw_write = (hw_write_t) i2c_master_send; | 1262 | codec->hw_write = (hw_write_t) i2c_master_send; |
1263 | codec->hw_read = (hw_read_t) aic3x_i2c_read; | ||
1211 | ret = i2c_add_driver(&aic3x_i2c_driver); | 1264 | ret = i2c_add_driver(&aic3x_i2c_driver); |
1212 | if (ret != 0) | 1265 | if (ret != 0) |
1213 | printk(KERN_ERR "can't add i2c driver"); | 1266 | printk(KERN_ERR "can't add i2c driver"); |
diff --git a/sound/soc/codecs/tlv320aic3x.h b/sound/soc/codecs/tlv320aic3x.h index d49d001e6e4c..c1dd1ac0ceac 100644 --- a/sound/soc/codecs/tlv320aic3x.h +++ b/sound/soc/codecs/tlv320aic3x.h | |||
@@ -108,8 +108,14 @@ | |||
108 | #define DACR1_2_RLOPM_VOL 92 | 108 | #define DACR1_2_RLOPM_VOL 92 |
109 | #define LLOPM_CTRL 86 | 109 | #define LLOPM_CTRL 86 |
110 | #define RLOPM_CTRL 93 | 110 | #define RLOPM_CTRL 93 |
111 | /* Clock generation control register */ | 111 | /* GPIO/IRQ registers */ |
112 | #define AIC3X_STICKY_IRQ_FLAGS_REG 96 | ||
113 | #define AIC3X_RT_IRQ_FLAGS_REG 97 | ||
114 | #define AIC3X_GPIO1_REG 98 | ||
115 | #define AIC3X_GPIO2_REG 99 | ||
116 | #define AIC3X_GPIOA_REG 100 | ||
112 | #define AIC3X_GPIOB_REG 101 | 117 | #define AIC3X_GPIOB_REG 101 |
118 | /* Clock generation control register */ | ||
113 | #define AIC3X_CLKGEN_CTRL_REG 102 | 119 | #define AIC3X_CLKGEN_CTRL_REG 102 |
114 | 120 | ||
115 | /* Page select register bits */ | 121 | /* Page select register bits */ |
@@ -175,8 +181,49 @@ | |||
175 | /* Default input volume */ | 181 | /* Default input volume */ |
176 | #define DEFAULT_GAIN 0x20 | 182 | #define DEFAULT_GAIN 0x20 |
177 | 183 | ||
184 | /* GPIO API */ | ||
185 | enum { | ||
186 | AIC3X_GPIO1_FUNC_DISABLED = 0, | ||
187 | AIC3X_GPIO1_FUNC_AUDIO_WORDCLK_ADC = 1, | ||
188 | AIC3X_GPIO1_FUNC_CLOCK_MUX = 2, | ||
189 | AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV2 = 3, | ||
190 | AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV4 = 4, | ||
191 | AIC3X_GPIO1_FUNC_CLOCK_MUX_DIV8 = 5, | ||
192 | AIC3X_GPIO1_FUNC_SHORT_CIRCUIT_IRQ = 6, | ||
193 | AIC3X_GPIO1_FUNC_AGC_NOISE_IRQ = 7, | ||
194 | AIC3X_GPIO1_FUNC_INPUT = 8, | ||
195 | AIC3X_GPIO1_FUNC_OUTPUT = 9, | ||
196 | AIC3X_GPIO1_FUNC_DIGITAL_MIC_MODCLK = 10, | ||
197 | AIC3X_GPIO1_FUNC_AUDIO_WORDCLK = 11, | ||
198 | AIC3X_GPIO1_FUNC_BUTTON_IRQ = 12, | ||
199 | AIC3X_GPIO1_FUNC_HEADSET_DETECT_IRQ = 13, | ||
200 | AIC3X_GPIO1_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 14, | ||
201 | AIC3X_GPIO1_FUNC_ALL_IRQ = 16 | ||
202 | }; | ||
203 | |||
204 | enum { | ||
205 | AIC3X_GPIO2_FUNC_DISABLED = 0, | ||
206 | AIC3X_GPIO2_FUNC_HEADSET_DETECT_IRQ = 2, | ||
207 | AIC3X_GPIO2_FUNC_INPUT = 3, | ||
208 | AIC3X_GPIO2_FUNC_OUTPUT = 4, | ||
209 | AIC3X_GPIO2_FUNC_DIGITAL_MIC_INPUT = 5, | ||
210 | AIC3X_GPIO2_FUNC_AUDIO_BITCLK = 8, | ||
211 | AIC3X_GPIO2_FUNC_HEADSET_DETECT_OR_BUTTON_IRQ = 9, | ||
212 | AIC3X_GPIO2_FUNC_ALL_IRQ = 10, | ||
213 | AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_OR_AGC_IRQ = 11, | ||
214 | AIC3X_GPIO2_FUNC_HEADSET_OR_BUTTON_PRESS_OR_SHORT_CIRCUIT_IRQ = 12, | ||
215 | AIC3X_GPIO2_FUNC_SHORT_CIRCUIT_IRQ = 13, | ||
216 | AIC3X_GPIO2_FUNC_AGC_NOISE_IRQ = 14, | ||
217 | AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ = 15 | ||
218 | }; | ||
219 | |||
220 | void aic3x_set_gpio(struct snd_soc_codec *codec, int gpio, int state); | ||
221 | int aic3x_get_gpio(struct snd_soc_codec *codec, int gpio); | ||
222 | int aic3x_headset_detected(struct snd_soc_codec *codec); | ||
223 | |||
178 | struct aic3x_setup_data { | 224 | struct aic3x_setup_data { |
179 | unsigned short i2c_address; | 225 | unsigned short i2c_address; |
226 | unsigned int gpio_func[2]; | ||
180 | }; | 227 | }; |
181 | 228 | ||
182 | extern struct snd_soc_codec_dai aic3x_dai; | 229 | extern struct snd_soc_codec_dai aic3x_dai; |