|
@@ -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 const 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) {
|