Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b9045b9c authored by Dan Murphy's avatar Dan Murphy Committed by Mark Brown
Browse files

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: default avatarDan Murphy <dmurphy@ti.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent fde0543f
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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 */
		>;
};
+23 −0
Original line number Diff line number Diff line
@@ -22,7 +22,30 @@
#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;
+206 −0
Original line number Diff line number Diff line
@@ -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) {
+3 −0
Original line number Diff line number Diff line
@@ -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