ASoC: tlv320aic32x4: Add gpio configuration to the codec
authorDan Murphy <dmurphy@ti.com>
Wed, 12 Jul 2017 18:37:00 +0000 (13:37 -0500)
committerMark Brown <broonie@kernel.org>
Mon, 17 Jul 2017 15:22:28 +0000 (16:22 +0100)
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 <dmurphy@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Documentation/devicetree/bindings/sound/tlv320aic32x4.txt
include/sound/tlv320aic32x4.h
sound/soc/codecs/tlv320aic32x4.c
sound/soc/codecs/tlv320aic32x4.h

index 9651fc7..ca75890 100644 (file)
@@ -20,6 +20,8 @@ Optional properties:
  - reset-gpios: Reset-GPIO phandle with args as described in gpio/gpio.txt
  - clocks/clock-names: Clock named 'mclk' for the master clock of the codec.
    See clock/clock-bindings.txt for information about the detailed format.
+ - aic32x4-gpio-func - <array of 5 int>
+       - Types are defined in include/sound/tlv320aic32x4.h
 
 
 Example:
@@ -29,4 +31,11 @@ codec: tlv320aic32x4@18 {
        reg = <0x18>;
        clocks = <&clks 201>;
        clock-names = "mclk";
+       aic32x4-gpio-func= <
+                       0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
+                       0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
+                       0x04 /* MFP3 AIC32X4_MFP3_GPIO_ENABLED */
+                       0xff /* AIC32X4_MFPX_DEFAULT_VALUE */
+                       0x08 /* MFP5 AIC32X4_MFP5_GPIO_INPUT */
+               >;
 };
index 24e5d99..22305c0 100644 (file)
 #define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K     0x00000001
 #define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K     0x00000002
 
+/* GPIO API */
+#define AIC32X4_MFPX_DEFAULT_VALUE     0xff
+
+#define AIC32X4_MFP1_DIN_DISABLED      0
+#define AIC32X4_MFP1_DIN_ENABLED       0x2
+#define AIC32X4_MFP1_GPIO_IN           0x4
+
+#define AIC32X4_MFP2_GPIO_OUT_LOW      0x0
+#define AIC32X4_MFP2_GPIO_OUT_HIGH     0x1
+
+#define AIC32X4_MFP_GPIO_ENABLED       0x4
+
+#define AIC32X4_MFP5_GPIO_DISABLED     0x0
+#define AIC32X4_MFP5_GPIO_INPUT                0x8
+#define AIC32X4_MFP5_GPIO_OUTPUT       0xc
+#define AIC32X4_MFP5_GPIO_OUT_LOW      0x0
+#define AIC32X4_MFP5_GPIO_OUT_HIGH     0x1
+
+struct aic32x4_setup_data {
+       unsigned int gpio_func[5];
+};
+
 struct aic32x4_pdata {
+       struct aic32x4_setup_data *setup;
        u32 power_cfg;
        u32 micpga_routing;
        bool swapdacs;
index 28fdfc5..300ee28 100644 (file)
@@ -74,6 +74,152 @@ struct aic32x4_priv {
        struct regulator *supply_iov;
        struct regulator *supply_dv;
        struct regulator *supply_av;
+
+       struct aic32x4_setup_data *setup;
+       struct device *dev;
+};
+
+static int aic32x4_get_mfp1_gpio(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 val;
+
+       val = snd_soc_read(codec, AIC32X4_DINCTL);
+
+       ucontrol->value.integer.value[0] = (val & 0x01);
+
+       return 0;
+};
+
+static int aic32x4_set_mfp2_gpio(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 val;
+       u8 gpio_check;
+
+       val = snd_soc_read(codec, AIC32X4_DOUTCTL);
+       gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED);
+       if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) {
+               printk(KERN_ERR "%s: MFP2 is not configure as a GPIO output\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (ucontrol->value.integer.value[0] == (val & AIC32X4_MFP2_GPIO_OUT_HIGH))
+               return 0;
+
+       if (ucontrol->value.integer.value[0])
+               val |= ucontrol->value.integer.value[0];
+       else
+               val &= ~AIC32X4_MFP2_GPIO_OUT_HIGH;
+
+       snd_soc_write(codec, AIC32X4_DOUTCTL, val);
+
+       return 0;
+};
+
+static int aic32x4_get_mfp3_gpio(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 val;
+
+       val = snd_soc_read(codec, AIC32X4_SCLKCTL);
+
+       ucontrol->value.integer.value[0] = (val & 0x01);
+
+       return 0;
+};
+
+static int aic32x4_set_mfp4_gpio(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 val;
+       u8 gpio_check;
+
+       val = snd_soc_read(codec, AIC32X4_MISOCTL);
+       gpio_check = (val & AIC32X4_MFP_GPIO_ENABLED);
+       if (gpio_check != AIC32X4_MFP_GPIO_ENABLED) {
+               printk(KERN_ERR "%s: MFP4 is not configure as a GPIO output\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (ucontrol->value.integer.value[0] == (val & AIC32X4_MFP5_GPIO_OUT_HIGH))
+               return 0;
+
+       if (ucontrol->value.integer.value[0])
+               val |= ucontrol->value.integer.value[0];
+       else
+               val &= ~AIC32X4_MFP5_GPIO_OUT_HIGH;
+
+       snd_soc_write(codec, AIC32X4_MISOCTL, val);
+
+       return 0;
+};
+
+static int aic32x4_get_mfp5_gpio(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 val;
+
+       val = snd_soc_read(codec, AIC32X4_GPIOCTL);
+       ucontrol->value.integer.value[0] = ((val & 0x2) >> 1);
+
+       return 0;
+};
+
+static int aic32x4_set_mfp5_gpio(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u8 val;
+       u8 gpio_check;
+
+       val = snd_soc_read(codec, AIC32X4_GPIOCTL);
+       gpio_check = (val & AIC32X4_MFP5_GPIO_OUTPUT);
+       if (gpio_check != AIC32X4_MFP5_GPIO_OUTPUT) {
+               printk(KERN_ERR "%s: MFP5 is not configure as a GPIO output\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       if (ucontrol->value.integer.value[0] == (val & 0x1))
+               return 0;
+
+       if (ucontrol->value.integer.value[0])
+               val |= ucontrol->value.integer.value[0];
+       else
+               val &= 0xfe;
+
+       snd_soc_write(codec, AIC32X4_GPIOCTL, val);
+
+       return 0;
+};
+
+static const struct snd_kcontrol_new aic32x4_mfp1[] = {
+       SOC_SINGLE_BOOL_EXT("MFP1 GPIO", 0, aic32x4_get_mfp1_gpio, NULL),
+};
+
+static const struct snd_kcontrol_new aic32x4_mfp2[] = {
+       SOC_SINGLE_BOOL_EXT("MFP2 GPIO", 0, NULL, aic32x4_set_mfp2_gpio),
+};
+
+static const struct snd_kcontrol_new aic32x4_mfp3[] = {
+       SOC_SINGLE_BOOL_EXT("MFP3 GPIO", 0, aic32x4_get_mfp3_gpio, NULL),
+};
+
+static const struct snd_kcontrol_new aic32x4_mfp4[] = {
+       SOC_SINGLE_BOOL_EXT("MFP4 GPIO", 0, NULL, aic32x4_set_mfp4_gpio),
+};
+
+static const struct snd_kcontrol_new aic32x4_mfp5[] = {
+       SOC_SINGLE_BOOL_EXT("MFP5 GPIO", 0, aic32x4_get_mfp5_gpio,
+               aic32x4_set_mfp5_gpio),
 };
 
 /* 0dB min, 0.5dB steps */
@@ -734,6 +880,52 @@ static struct snd_soc_dai_driver aic32x4_dai = {
        .symmetric_rates = 1,
 };
 
+static void aic32x4_setup_gpios(struct snd_soc_codec *codec)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+
+       /* setup GPIO functions */
+       /* MFP1 */
+       if (aic32x4->setup->gpio_func[0] != AIC32X4_MFPX_DEFAULT_VALUE) {
+               snd_soc_write(codec, AIC32X4_DINCTL,
+                     aic32x4->setup->gpio_func[0]);
+               snd_soc_add_codec_controls(codec, aic32x4_mfp1,
+                       ARRAY_SIZE(aic32x4_mfp1));
+       }
+
+       /* MFP2 */
+       if (aic32x4->setup->gpio_func[1] != AIC32X4_MFPX_DEFAULT_VALUE) {
+               snd_soc_write(codec, AIC32X4_DOUTCTL,
+                     aic32x4->setup->gpio_func[1]);
+               snd_soc_add_codec_controls(codec, aic32x4_mfp2,
+                       ARRAY_SIZE(aic32x4_mfp2));
+       }
+
+       /* MFP3 */
+       if (aic32x4->setup->gpio_func[2] != AIC32X4_MFPX_DEFAULT_VALUE) {
+               snd_soc_write(codec, AIC32X4_SCLKCTL,
+                     aic32x4->setup->gpio_func[2]);
+               snd_soc_add_codec_controls(codec, aic32x4_mfp3,
+                       ARRAY_SIZE(aic32x4_mfp3));
+       }
+
+       /* MFP4 */
+       if (aic32x4->setup->gpio_func[3] != AIC32X4_MFPX_DEFAULT_VALUE) {
+               snd_soc_write(codec, AIC32X4_MISOCTL,
+                     aic32x4->setup->gpio_func[3]);
+               snd_soc_add_codec_controls(codec, aic32x4_mfp4,
+                       ARRAY_SIZE(aic32x4_mfp4));
+       }
+
+       /* MFP5 */
+       if (aic32x4->setup->gpio_func[4] != AIC32X4_MFPX_DEFAULT_VALUE) {
+               snd_soc_write(codec, AIC32X4_GPIOCTL,
+                     aic32x4->setup->gpio_func[4]);
+               snd_soc_add_codec_controls(codec, aic32x4_mfp5,
+                       ARRAY_SIZE(aic32x4_mfp5));
+       }
+}
+
 static int aic32x4_codec_probe(struct snd_soc_codec *codec)
 {
        struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
@@ -746,6 +938,9 @@ static int aic32x4_codec_probe(struct snd_soc_codec *codec)
 
        snd_soc_write(codec, AIC32X4_RESET, 0x01);
 
+       if (aic32x4->setup)
+               aic32x4_setup_gpios(codec);
+
        /* Power platform configuration */
        if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
                snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN |
@@ -810,10 +1005,20 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
 static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
                struct device_node *np)
 {
+       struct aic32x4_setup_data *aic32x4_setup;
+
+       aic32x4_setup = devm_kzalloc(aic32x4->dev, sizeof(*aic32x4_setup),
+                                                       GFP_KERNEL);
+       if (!aic32x4_setup)
+               return -ENOMEM;
+
        aic32x4->swapdacs = false;
        aic32x4->micpga_routing = 0;
        aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
 
+       if (of_property_read_u32_array(np, "aic32x4-gpio-func",
+                               aic32x4_setup->gpio_func, 5) >= 0)
+               aic32x4->setup = aic32x4_setup;
        return 0;
 }
 
@@ -932,6 +1137,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
        if (aic32x4 == NULL)
                return -ENOMEM;
 
+       aic32x4->dev = dev;
        dev_set_drvdata(dev, aic32x4);
 
        if (pdata) {
index a197dd5..da7cec4 100644 (file)
@@ -44,8 +44,11 @@ int aic32x4_remove(struct device *dev);
 #define AIC32X4_IFACE4         31
 #define AIC32X4_IFACE5         32
 #define AIC32X4_IFACE6         33
+#define AIC32X4_GPIOCTL                52
 #define AIC32X4_DOUTCTL                53
 #define AIC32X4_DINCTL         54
+#define AIC32X4_MISOCTL                55
+#define AIC32X4_SCLKCTL                56
 #define AIC32X4_DACSPB         60
 #define AIC32X4_ADCSPB         61
 #define AIC32X4_DACSETUP       63