Skip to content

Commit b9045b9

Browse files
Dan Murphybroonie
authored andcommitted
ASoC: tlv320aic32x4: Add gpio configuration to the codec
Add the ability to configure the MFP1->MFP5 registers as GPIOs. In addition adding ALSA controls to get and set the GPIO state. Per the data sheet each MFP can be configured as a GPIO input only, output only or either an input or output. Signed-off-by: Dan Murphy <[email protected]> Signed-off-by: Mark Brown <[email protected]>
1 parent fde0543 commit b9045b9

File tree

4 files changed

+241
-0
lines changed

4 files changed

+241
-0
lines changed

Documentation/devicetree/bindings/sound/tlv320aic32x4.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Optional properties:
2020
- reset-gpios: Reset-GPIO phandle with args as described in gpio/gpio.txt
2121
- clocks/clock-names: Clock named 'mclk' for the master clock of the codec.
2222
See clock/clock-bindings.txt for information about the detailed format.
23+
- aic32x4-gpio-func - <array of 5 int>
24+
- Types are defined in include/sound/tlv320aic32x4.h
2325

2426

2527
Example:
@@ -29,4 +31,11 @@ codec: tlv320aic32x4@18 {
2931
reg = <0x18>;
3032
clocks = <&clks 201>;
3133
clock-names = "mclk";
34+
aic32x4-gpio-func= <
35+
0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
36+
0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
37+
0x04 /* MFP3 AIC32X4_MFP3_GPIO_ENABLED */
38+
0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
39+
0x08 /* MFP5 AIC32X4_MFP5_GPIO_INPUT */
40+
>;
3241
};

include/sound/tlv320aic32x4.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,30 @@
2222
#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K 0x00000001
2323
#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K 0x00000002
2424

25+
/* GPIO API */
26+
#define AIC32X4_MFPX_DEFAULT_VALUE 0xff
27+
28+
#define AIC32X4_MFP1_DIN_DISABLED 0
29+
#define AIC32X4_MFP1_DIN_ENABLED 0x2
30+
#define AIC32X4_MFP1_GPIO_IN 0x4
31+
32+
#define AIC32X4_MFP2_GPIO_OUT_LOW 0x0
33+
#define AIC32X4_MFP2_GPIO_OUT_HIGH 0x1
34+
35+
#define AIC32X4_MFP_GPIO_ENABLED 0x4
36+
37+
#define AIC32X4_MFP5_GPIO_DISABLED 0x0
38+
#define AIC32X4_MFP5_GPIO_INPUT 0x8
39+
#define AIC32X4_MFP5_GPIO_OUTPUT 0xc
40+
#define AIC32X4_MFP5_GPIO_OUT_LOW 0x0
41+
#define AIC32X4_MFP5_GPIO_OUT_HIGH 0x1
42+
43+
struct aic32x4_setup_data {
44+
unsigned int gpio_func[5];
45+
};
46+
2547
struct aic32x4_pdata {
48+
struct aic32x4_setup_data *setup;
2649
u32 power_cfg;
2750
u32 micpga_routing;
2851
bool swapdacs;

sound/soc/codecs/tlv320aic32x4.c

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,152 @@ struct aic32x4_priv {
7474
struct regulator *supply_iov;
7575
struct regulator *supply_dv;
7676
struct regulator *supply_av;
77+
78+
struct aic32x4_setup_data *setup;
79+
struct device *dev;
80+
};
81+
82+
static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol,
83+
struct snd_ctl_elem_value *ucontrol)
84+
{
85+
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
86+
u8 val;
87+
88+
val = snd_soc_read(codec, AIC32X4_DINCTL);
89+
90+
ucontrol->value.integer.value[0] = (val & 0x01);
91+
92+
return 0;
93+
};
94+
95+
static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol,
96+
struct snd_ctl_elem_value *ucontrol)
97+
{
98+
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
99+
u8 val;
100+
u8 gpio_check;
101+
102+
val = snd_soc_read(codec, AIC32X4_DOUTCTL);
103+
gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED);
104+
if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) {
105+
printk(KERN_ERR "%s: MFP2 is not configure as a GPIO output\n",
106+
__func__);
107+
return -EINVAL;
108+
}
109+
110+
if (ucontrol->value.integer.value[0] == (val & AIC32X4_MFP2_GPIO_OUT_HIGH))
111+
return 0;
112+
113+
if (ucontrol->value.integer.value[0])
114+
val |= ucontrol->value.integer.value[0];
115+
else
116+
val &= ~AIC32X4_MFP2_GPIO_OUT_HIGH;
117+
118+
snd_soc_write(codec, AIC32X4_DOUTCTL, val);
119+
120+
return 0;
121+
};
122+
123+
static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol,
124+
struct snd_ctl_elem_value *ucontrol)
125+
{
126+
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
127+
u8 val;
128+
129+
val = snd_soc_read(codec, AIC32X4_SCLKCTL);
130+
131+
ucontrol->value.integer.value[0] = (val & 0x01);
132+
133+
return 0;
134+
};
135+
136+
static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol,
137+
struct snd_ctl_elem_value *ucontrol)
138+
{
139+
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
140+
u8 val;
141+
u8 gpio_check;
142+
143+
val = snd_soc_read(codec, AIC32X4_MISOCTL);
144+
gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED);
145+
if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) {
146+
printk(KERN_ERR "%s: MFP4 is not configure as a GPIO output\n",
147+
__func__);
148+
return -EINVAL;
149+
}
150+
151+
if (ucontrol->value.integer.value[0] == (val & AIC32X4_MFP5_GPIO_OUT_HIGH))
152+
return 0;
153+
154+
if (ucontrol->value.integer.value[0])
155+
val |= ucontrol->value.integer.value[0];
156+
else
157+
val &= ~AIC32X4_MFP5_GPIO_OUT_HIGH;
158+
159+
snd_soc_write(codec, AIC32X4_MISOCTL, val);
160+
161+
return 0;
162+
};
163+
164+
static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol,
165+
struct snd_ctl_elem_value *ucontrol)
166+
{
167+
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
168+
u8 val;
169+
170+
val = snd_soc_read(codec, AIC32X4_GPIOCTL);
171+
ucontrol->value.integer.value[0] = ((val & 0x2) >> 1);
172+
173+
return 0;
174+
};
175+
176+
static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol,
177+
struct snd_ctl_elem_value *ucontrol)
178+
{
179+
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
180+
u8 val;
181+
u8 gpio_check;
182+
183+
val = snd_soc_read(codec, AIC32X4_GPIOCTL);
184+
gpio_check = (val & AIC32X4_MFP5_GPIO_OUTPUT);
185+
if (gpio_check != AIC32X4_MFP5_GPIO_OUTPUT) {
186+
printk(KERN_ERR "%s: MFP5 is not configure as a GPIO output\n",
187+
__func__);
188+
return -EINVAL;
189+
}
190+
191+
if (ucontrol->value.integer.value[0] == (val & 0x1))
192+
return 0;
193+
194+
if (ucontrol->value.integer.value[0])
195+
val |= ucontrol->value.integer.value[0];
196+
else
197+
val &= 0xfe;
198+
199+
snd_soc_write(codec, AIC32X4_GPIOCTL, val);
200+
201+
return 0;
202+
};
203+
204+
static const struct snd_kcontrol_new aic32x4_mfp1[] = {
205+
SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL),
206+
};
207+
208+
static const struct snd_kcontrol_new aic32x4_mfp2[] = {
209+
SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio),
210+
};
211+
212+
static const struct snd_kcontrol_new aic32x4_mfp3[] = {
213+
SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL),
214+
};
215+
216+
static const struct snd_kcontrol_new aic32x4_mfp4[] = {
217+
SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio),
218+
};
219+
220+
static const struct snd_kcontrol_new aic32x4_mfp5[] = {
221+
SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio,
222+
aic32x4_set_mfp5_gpio),
77223
};
78224

79225
/* 0dB min, 0.5dB steps */
@@ -734,6 +880,52 @@ static struct snd_soc_dai_driver aic32x4_dai = {
734880
.symmetric_rates = 1,
735881
};
736882

883+
static void aic32x4_setup_gpios(struct snd_soc_codec *codec)
884+
{
885+
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
886+
887+
/* setup GPIO functions */
888+
/* MFP1 */
889+
if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) {
890+
snd_soc_write(codec, AIC32X4_DINCTL,
891+
aic32x4->setup->gpio_func[0]);
892+
snd_soc_add_codec_controls(codec, aic32x4_mfp1,
893+
ARRAY_SIZE(aic32x4_mfp1));
894+
}
895+
896+
/* MFP2 */
897+
if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) {
898+
snd_soc_write(codec, AIC32X4_DOUTCTL,
899+
aic32x4->setup->gpio_func[1]);
900+
snd_soc_add_codec_controls(codec, aic32x4_mfp2,
901+
ARRAY_SIZE(aic32x4_mfp2));
902+
}
903+
904+
/* MFP3 */
905+
if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) {
906+
snd_soc_write(codec, AIC32X4_SCLKCTL,
907+
aic32x4->setup->gpio_func[2]);
908+
snd_soc_add_codec_controls(codec, aic32x4_mfp3,
909+
ARRAY_SIZE(aic32x4_mfp3));
910+
}
911+
912+
/* MFP4 */
913+
if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) {
914+
snd_soc_write(codec, AIC32X4_MISOCTL,
915+
aic32x4->setup->gpio_func[3]);
916+
snd_soc_add_codec_controls(codec, aic32x4_mfp4,
917+
ARRAY_SIZE(aic32x4_mfp4));
918+
}
919+
920+
/* MFP5 */
921+
if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) {
922+
snd_soc_write(codec, AIC32X4_GPIOCTL,
923+
aic32x4->setup->gpio_func[4]);
924+
snd_soc_add_codec_controls(codec, aic32x4_mfp5,
925+
ARRAY_SIZE(aic32x4_mfp5));
926+
}
927+
}
928+
737929
static int aic32x4_codec_probe(struct snd_soc_codec *codec)
738930
{
739931
struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
@@ -746,6 +938,9 @@ static int aic32x4_codec_probe(struct snd_soc_codec *codec)
746938

747939
snd_soc_write(codec, AIC32X4_RESET, 0x01);
748940

941+
if (aic32x4->setup)
942+
aic32x4_setup_gpios(codec);
943+
749944
/* Power platform configuration */
750945
if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
751946
snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN |
@@ -810,10 +1005,20 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
8101005
static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
8111006
struct device_node *np)
8121007
{
1008+
struct aic32x4_setup_data *aic32x4_setup;
1009+
1010+
aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup),
1011+
GFP_KERNEL);
1012+
if (!aic32x4_setup)
1013+
return -ENOMEM;
1014+
8131015
aic32x4->swapdacs = false;
8141016
aic32x4->micpga_routing = 0;
8151017
aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
8161018

1019+
if (of_property_read_u32_array(np, "aic32x4-gpio-func",
1020+
aic32x4_setup->gpio_func, 5) >= 0)
1021+
aic32x4->setup = aic32x4_setup;
8171022
return 0;
8181023
}
8191024

@@ -932,6 +1137,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
9321137
if (aic32x4 == NULL)
9331138
return -ENOMEM;
9341139

1140+
aic32x4->dev = dev;
9351141
dev_set_drvdata(dev, aic32x4);
9361142

9371143
if (pdata) {

sound/soc/codecs/tlv320aic32x4.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,11 @@ int aic32x4_remove(struct device *dev);
4444
#define AIC32X4_IFACE4 31
4545
#define AIC32X4_IFACE5 32
4646
#define AIC32X4_IFACE6 33
47+
#define AIC32X4_GPIOCTL 52
4748
#define AIC32X4_DOUTCTL 53
4849
#define AIC32X4_DINCTL 54
50+
#define AIC32X4_MISOCTL 55
51+
#define AIC32X4_SCLKCTL 56
4952
#define AIC32X4_DACSPB 60
5053
#define AIC32X4_ADCSPB 61
5154
#define AIC32X4_DACSETUP 63

0 commit comments

Comments
 (0)