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

Unverified Commit 540f1ba7 authored by Peter Ujfalusi's avatar Peter Ujfalusi Committed by Mark Brown
Browse files

ASoC: ti: davinci-mcasp: Add support for GPIO mode of the pins



All McASP pin can be configured as GPIO.
Add gpiochip support for McASP and only enable it when the
gpio-controller is present in the DT node.

Signed-off-by: default avatarPeter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 4664b94c
Loading
Loading
Loading
Loading
+156 −3
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <linux/platform_data/davinci_asp.h>
#include <linux/math64.h>
#include <linux/bitmap.h>
#include <linux/gpio/driver.h>

#include <sound/asoundef.h>
#include <sound/core.h>
@@ -54,6 +55,7 @@ static u32 context_regs[] = {
	DAVINCI_MCASP_AHCLKXCTL_REG,
	DAVINCI_MCASP_AHCLKRCTL_REG,
	DAVINCI_MCASP_PDIR_REG,
	DAVINCI_MCASP_PFUNC_REG,
	DAVINCI_MCASP_RXMASK_REG,
	DAVINCI_MCASP_TXMASK_REG,
	DAVINCI_MCASP_RXTDM_REG,
@@ -108,6 +110,10 @@ struct davinci_mcasp {
	/* Used for comstraint setting on the second stream */
	u32	channels;

#ifdef CONFIG_GPIOLIB
	struct gpio_chip gpio_chip;
#endif

#ifdef CONFIG_PM
	struct davinci_mcasp_context context;
#endif
@@ -818,9 +824,6 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
	if (mcasp->version < MCASP_VERSION_3)
		mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);

	/* All PINS as McASP */
	mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);

	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
		mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
		mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
@@ -1845,6 +1848,147 @@ static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata)
	return offset;
}

#ifdef CONFIG_GPIOLIB
static int davinci_mcasp_gpio_request(struct gpio_chip *chip, unsigned offset)
{
	struct davinci_mcasp *mcasp = gpiochip_get_data(chip);

	if (mcasp->num_serializer && offset < mcasp->num_serializer &&
	    mcasp->serial_dir[offset] != INACTIVE_MODE) {
		dev_err(mcasp->dev, "AXR%u pin is  used for audio\n", offset);
		return -EBUSY;
	}

	/* Do not change the PIN yet */

	return pm_runtime_get_sync(mcasp->dev);
}

static void davinci_mcasp_gpio_free(struct gpio_chip *chip, unsigned offset)
{
	struct davinci_mcasp *mcasp = gpiochip_get_data(chip);

	/* Set the direction to input */
	mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset));

	/* Set the pin as McASP pin */
	mcasp_clr_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset));

	pm_runtime_put_sync(mcasp->dev);
}

static int davinci_mcasp_gpio_direction_out(struct gpio_chip *chip,
					    unsigned offset, int value)
{
	struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
	u32 val;

	if (value)
		mcasp_set_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
	else
		mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));

	val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PFUNC_REG);
	if (!(val & BIT(offset))) {
		/* Set the pin as GPIO pin */
		mcasp_set_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset));

		/* Set the direction to output */
		mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset));
	}

	return 0;
}

static void davinci_mcasp_gpio_set(struct gpio_chip *chip, unsigned offset,
				  int value)
{
	struct davinci_mcasp *mcasp = gpiochip_get_data(chip);

	if (value)
		mcasp_set_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
	else
		mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
}

static int davinci_mcasp_gpio_direction_in(struct gpio_chip *chip,
					   unsigned offset)
{
	struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
	u32 val;

	val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PFUNC_REG);
	if (!(val & BIT(offset))) {
		/* Set the direction to input */
		mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(offset));

		/* Set the pin as GPIO pin */
		mcasp_set_bits(mcasp, DAVINCI_MCASP_PFUNC_REG, BIT(offset));
	}

	return 0;
}

static int davinci_mcasp_gpio_get(struct gpio_chip *chip, unsigned offset)
{
	struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
	u32 val;

	val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDSET_REG);
	if (val & BIT(offset))
		return 1;

	return 0;
}

static int davinci_mcasp_gpio_get_direction(struct gpio_chip *chip,
					    unsigned offset)
{
	struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
	u32 val;

	val = mcasp_get_reg(mcasp, DAVINCI_MCASP_PDIR_REG);
	if (val & BIT(offset))
		return 0;

	return 1;
}

static const struct gpio_chip davinci_mcasp_template_chip = {
	.owner			= THIS_MODULE,
	.request		= davinci_mcasp_gpio_request,
	.free			= davinci_mcasp_gpio_free,
	.direction_output	= davinci_mcasp_gpio_direction_out,
	.set			= davinci_mcasp_gpio_set,
	.direction_input	= davinci_mcasp_gpio_direction_in,
	.get			= davinci_mcasp_gpio_get,
	.get_direction		= davinci_mcasp_gpio_get_direction,
	.base			= -1,
	.ngpio			= 32,
};

static int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp)
{
	if (!of_property_read_bool(mcasp->dev->of_node, "gpio-controller"))
		return 0;

	mcasp->gpio_chip = davinci_mcasp_template_chip;
	mcasp->gpio_chip.label = dev_name(mcasp->dev);
	mcasp->gpio_chip.parent = mcasp->dev;
#ifdef CONFIG_OF_GPIO
	mcasp->gpio_chip.of_node = mcasp->dev->of_node;
#endif

	return devm_gpiochip_add_data(mcasp->dev, &mcasp->gpio_chip, mcasp);
}

#else /* CONFIG_GPIOLIB */
static inline int davinci_mcasp_init_gpiochip(struct davinci_mcasp *mcasp)
{
	return 0;
}
#endif /* CONFIG_GPIOLIB */

static int davinci_mcasp_probe(struct platform_device *pdev)
{
	struct snd_dmaengine_dai_dma_data *dma_data;
@@ -2069,6 +2213,15 @@ static int davinci_mcasp_probe(struct platform_device *pdev)

	mcasp_reparent_fck(pdev);

	/* All PINS as McASP */
	pm_runtime_get_sync(mcasp->dev);
	mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000);
	pm_runtime_put(mcasp->dev);

	ret = davinci_mcasp_init_gpiochip(mcasp);
	if (ret)
		goto err;

	ret = devm_snd_soc_register_component(&pdev->dev,
					&davinci_mcasp_component,
					&davinci_mcasp_dai[pdata->op_mode], 1);