mbox series

[v6,00/10] Add support for audio on SC7280 based targets

Message ID 1637928282-2819-1-git-send-email-srivasam@codeaurora.org
Headers show
Series Add support for audio on SC7280 based targets | expand

Message

Srinivasa Rao Mandadapu Nov. 26, 2021, 12:04 p.m. UTC
This patch set is to add support for Audio over wcd codec,
digital mics, through digital codecs and without ADSP.

Changes Since V5:
    -- Include MI2S primary node to snd_soc_dai_driver in lpass-sc7280 platform driver.
    -- Move dependency patch list to corresponding patch.
    -- Add support for missing cdc-dma ports.
    -- Change if/else conditional statements to switch cases.
    -- Add missing error handlings.
    -- Typo errors fix.
Changes Since V4:
    -- Remove unused variable in lpass-sc7280 platform driver.
Changes Since V3:
    -- Remove redundant power domain controls. As power domains can be configured from dtsi.
Changes Since V2:
    -- Split lpass sc7280 cpu driver patch and create regmap config patch.
    -- Create patches based on latest kernel tip.
    -- Add helper function to get dma control and lpaif handle.
    -- Remove unused variables.
Changes Since V1:
    -- Typo errors fix
    -- CPU driver readable/writable apis optimization.
    -- Add Missing config patch
    -- Add Common api for repeated dmactl initialization.

Srinivasa Rao Mandadapu (10):
  ASoC: qcom: Move lpass_pcm_data structure to lpass header
  ASoC: qcom: lpass: Add dma fields for codec dma lpass interface
  ASoC: qcom: Add register definition for codec rddma and wrdma
  ASoC: qcom: Add lpass CPU driver for codec dma control
  ASoC: qcom: Add helper function to get dma control and lpaif handle
  ASoC: qcom: Add support for codec dma driver
  ASoC: qcom: Add regmap config support for codec dma driver
  ASoC: dt-bindings: Add SC7280 lpass cpu bindings
  ASoC: qcom: lpass-sc7280: Add platform driver for lpass audio
  ASoC: qcom: SC7280: Update config for building codec dma drivers

 .../devicetree/bindings/sound/qcom,lpass-cpu.yaml  |  69 ++-
 sound/soc/qcom/Kconfig                             |  13 +
 sound/soc/qcom/Makefile                            |   4 +
 sound/soc/qcom/lpass-cdc-dma.c                     | 275 ++++++++++
 sound/soc/qcom/lpass-cpu.c                         | 244 ++++++++-
 sound/soc/qcom/lpass-lpaif-reg.h                   | 103 +++-
 sound/soc/qcom/lpass-platform.c                    | 578 ++++++++++++++++++---
 sound/soc/qcom/lpass-sc7280.c                      | 434 ++++++++++++++++
 sound/soc/qcom/lpass.h                             | 151 ++++++
 9 files changed, 1784 insertions(+), 87 deletions(-)
 create mode 100644 sound/soc/qcom/lpass-cdc-dma.c
 create mode 100644 sound/soc/qcom/lpass-sc7280.c

Comments

Srinivas Kandagatla Dec. 1, 2021, 2:51 p.m. UTC | #1
On 26/11/2021 12:04, Srinivasa Rao Mandadapu wrote:
> Upadate lpass cpu and platform driver to support audio over codec dma
> in ADSP bypass use case.
> 
> Signed-off-by: Srinivasa Rao Mandadapu <srivasam@codeaurora.org>
> Co-developed-by: Venkata Prasad Potturu <potturu@codeaurora.org>
> Signed-off-by: Venkata Prasad Potturu <potturu@codeaurora.org>
> ---
>   sound/soc/qcom/lpass-cpu.c      |  59 +++++-
>   sound/soc/qcom/lpass-platform.c | 460 ++++++++++++++++++++++++++++++++++++++--
>   sound/soc/qcom/lpass.h          |  12 ++
>   3 files changed, 510 insertions(+), 21 deletions(-)
> 
> diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
> index 3bd9eb3..70feb9e 100644
> --- a/sound/soc/qcom/lpass-cpu.c
> +++ b/sound/soc/qcom/lpass-cpu.c
> @@ -857,7 +857,9 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
>   		}
...

>   
>   		drvdata->mi2s_osr_clk[dai_id] = devm_clk_get_optional(dev,
> diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
> index 5d77240..4419d3b 100644
> --- a/sound/soc/qcom/lpass-platform.c
> +++ b/sound/soc/qcom/lpass-platform.c
> @@ -20,6 +20,9 @@
>   
>   
>   static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
>   	.info			=	SNDRV_PCM_INFO_MMAP |
> @@ -45,6 +48,99 @@ static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
>   	.fifo_size		=	0,
>   };
>   

...
> +static int lpass_platform_pcmops_mmap(struct snd_soc_component *component,
> +				      struct snd_pcm_substream *substream,
> +				      struct vm_area_struct *vma)
> +{
> +	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
> +	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
> +	unsigned int dai_id = cpu_dai->driver->id;
> +
> +	if (is_cdc_dma_port(dai_id))
> +		return lpass_platform_cdc_dma_mmap(component, substream, vma);
> +
> +	return 0;

Now we have broken what was working before,

This should proably be

if (is_cdc_dma_port(dai_id))
	err = lpass_platform_cdc_dma_mmap(component, substream, vma);
else
	err = snd_pcm_lib_default_mmap(substream, vma);

return err;

> +}
> +
>   static irqreturn_t lpass_dma_interrupt_handler(
>   			struct snd_pcm_substream *substream,
>   			struct lpass_data *drvdata,
> @@ -674,6 +908,17 @@ static irqreturn_t lpass_dma_interrupt_handler(
>   		reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
>   		val = 0;
>   	break;
> +	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
> +	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
> +		map = drvdata->rxtx_lpaif_map;
> +		reg = LPAIF_RXTX_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST, dai_id);
> +		val = 0;
> +	break;
> +	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
> +		map = drvdata->va_lpaif_map;
> +		reg = LPAIF_RXTX_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST, dai_id);
> +		val = 0;
> +	break;
>   	default:
>   	dev_err(soc_runtime->dev, "%s: invalid  %d interface\n", __func__, dai_id);
>   	return -EINVAL;
> @@ -781,18 +1026,123 @@ static irqreturn_t lpass_platform_hdmiif_irq(int irq, void *data)
>   				return rv;
>   		}
>   	}
> +	return IRQ_HANDLED;
> +}
>   
> +static irqreturn_t lpass_platform_rxtxif_irq(int irq, void *data)
> +{
> +	struct lpass_data *drvdata = data;
> +	struct lpass_variant *v = drvdata->variant;
> +	unsigned int irqs;
> +	int rv, chan;
> +
> +	rv = regmap_read(drvdata->rxtx_lpaif_map,
> +			LPAIF_RXTX_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST, LPASS_CDC_DMA_RX0), &irqs);
> +	if (rv)
> +		return IRQ_NONE;
> +	/* Handle per channel interrupts */
> +	for (chan = 0; chan < LPASS_MAX_CDC_DMA_CHANNELS; chan++) {
> +		if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->rxtx_substream[chan]) {
> +			rv = lpass_dma_interrupt_handler(
> +						drvdata->rxtx_substream[chan],
> +						drvdata, chan, irqs);
> +			if (rv != IRQ_HANDLED)
> +				return rv;
> +		}
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t lpass_platform_vaif_irq(int irq, void *data)
> +{
> +	struct lpass_data *drvdata = data;
> +	struct lpass_variant *v = drvdata->variant;
> +	unsigned int irqs;
> +	int rv, chan;
> +
> +	rv = regmap_read(drvdata->va_lpaif_map,
> +			LPAIF_RXTX_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST,
> +			LPASS_CDC_DMA_VA_TX0), &irqs);
> +	if (rv)
> +		return IRQ_NONE;
> +	/* Handle per channel interrupts */
> +	for (chan = 0; chan < LPASS_MAX_VA_CDC_DMA_CHANNELS; chan++) {
> +		if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->va_substream[chan]) {
> +			rv = lpass_dma_interrupt_handler(
> +						drvdata->va_substream[chan],
> +						drvdata, chan, irqs);
> +			if (rv != IRQ_HANDLED)
> +				return rv;
> +		}
> +	}
>   	return IRQ_HANDLED;
>   }
>   
> +static int lpass_platform_prealloc_cdc_dma_buffer(struct snd_soc_component *component,
> +			struct snd_pcm *pcm, int dai_id)
> +{
> +	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
> +	struct snd_pcm_substream *substream;
> +	struct snd_dma_buffer *buf;
> +	int ret;
> +
> +	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
> +		substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
> +	else
> +		substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
> +
> +	ret = dma_coerce_mask_and_coherent(pcm->card->dev, DMA_BIT_MASK(64));
> +	if (ret)
> +		return ret;
> +
> +	buf = &substream->dma_buffer;
> +	buf->dev.dev = pcm->card->dev;
> +	buf->private_data = NULL;
> +
> +	/* Assign Codec DMA buffer pointers */
> +	buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS;
> +
> +	switch (dai_id) {
> +	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
> +		buf->bytes = lpass_platform_rxtx_hardware.buffer_bytes_max;
> +		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf;
> +		break;
> +	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
> +		buf->bytes = lpass_platform_rxtx_hardware.buffer_bytes_max;
> +		buf->addr = drvdata->rxtx_cdc_dma_lpm_buf + LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE;
> +		break;
> +	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
> +		buf->bytes = lpass_platform_va_hardware.buffer_bytes_max;
> +		buf->addr = drvdata->va_cdc_dma_lpm_buf;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	buf->area = (unsigned char * __force)ioremap(buf->addr, buf->bytes);
> +
> +	return 0;
> +}
> +
>   static int lpass_platform_pcm_new(struct snd_soc_component *component,
>   				  struct snd_soc_pcm_runtime *soc_runtime)
>   {
>   	struct snd_pcm *pcm = soc_runtime->pcm;
> +	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
> +	unsigned int dai_id = cpu_dai->driver->id;
> +
>   	size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
>   
> -	return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
> -					    component->dev, size);
> +	/*
> +	 * Lpass codec dma can access only lpass lpm hardware memory.
> +	 * ioremap is for HLOS to access hardware memory.
> +	 */
> +	if (is_cdc_dma_port(dai_id))
> +		return lpass_platform_prealloc_cdc_dma_buffer(component, pcm, dai_id);
> +	else
> +		return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
> +						component->dev, size);
>   }
>   
>   static int lpass_platform_pcmops_suspend(struct snd_soc_component *component)
> @@ -827,6 +1177,22 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
>   	return regcache_sync(map);
>   }
>   
> +int lpass_platform_copy(struct snd_soc_component *component,
> +			 struct snd_pcm_substream *substream, int channel,
> +			 unsigned long pos, void __user *buf, unsigned long bytes)
> +{
> +	struct snd_pcm_runtime *rt = substream->runtime;
> +
> +	unsigned char *dma_buf = rt->dma_area + pos +
> +				channel * (rt->dma_bytes / rt->channels);
> +
> +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
> +		return copy_from_user_toio(dma_buf, buf, bytes);
> +	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> +		return copy_to_user_fromio(buf, dma_buf, bytes);

Should this be:

if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
	if (is_cdc_dma_port(dai_id))
		ret = copy_from_user_toio(dma_buf, buf, bytes);
	else
		ret = copy_from_user((void __force *)dma_buf, buf, bytes);
} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
	if (is_cdc_dma_port(dai_id))
		ret = copy_to_user_fromio(buf, dma_buf, bytes);
	else
		ret = copy_to_user(buf, dma_buf, bytes);
}

return ret;

--srini

> +
> +	return 0;
> +}
>   
>   static const struct snd_soc_component_driver lpass_component_driver = {
>   	.name		= DRV_NAME,
> @@ -837,9 +1203,11 @@ static const struct snd_soc_component_driver lpass_component_driver = {
>   	.prepare	= lpass_platform_pcmops_prepare,
>   	.trigger	= lpass_platform_pcmops_trigger,
>   	.pointer	= lpass_platform_pcmops_pointer,
> +	.mmap		= lpass_platform_pcmops_mmap,
>   	.pcm_construct	= lpass_platform_pcm_new,
>   	.suspend		= lpass_platform_pcmops_suspend,
>   	.resume			= lpass_platform_pcmops_resume,
> +	.copy_user		= lpass_platform_copy,
>   
>   };
>   
> @@ -877,6 +1245,60 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
>   		return ret;
>   	}
>   
> +	if (drvdata->codec_dma_enable) {
> +		ret = regmap_write(drvdata->rxtx_lpaif_map,
> +			LPAIF_RXTX_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST, LPASS_CDC_DMA_TX3), 0x0);
> +		if (ret) {
> +			dev_err(&pdev->dev, "error writing to rxtx irqen reg: %d\n", ret);
> +			return ret;
> +		}
> +		ret = regmap_write(drvdata->va_lpaif_map,
> +			LPAIF_RXTX_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST, LPASS_CDC_DMA_VA_TX0), 0x0);
> +		if (ret) {
> +			dev_err(&pdev->dev, "error writing to rxtx irqen reg: %d\n", ret);
> +			return ret;
> +		}
> +		drvdata->rxtxif_irq = platform_get_irq_byname(pdev, "lpass-irq-rxtxif");
> +		if (drvdata->rxtxif_irq < 0)
> +			return -ENODEV;
> +
> +		ret = devm_request_irq(&pdev->dev, drvdata->rxtxif_irq,
> +				lpass_platform_rxtxif_irq, IRQF_TRIGGER_RISING,
> +				"lpass-irq-rxtxif", drvdata);
> +		if (ret) {
> +			dev_err(&pdev->dev, "rxtx irq request failed: %d\n", ret);
> +			return ret;
> +		}
> +
> +		ret = lpass_platform_alloc_rxtx_dmactl_fields(&pdev->dev,
> +						 drvdata->rxtx_lpaif_map);
> +		if (ret) {
> +			dev_err(&pdev->dev,
> +				"error initializing rxtx dmactl fields: %d\n", ret);
> +			return ret;
> +		}
> +
> +		drvdata->vaif_irq = platform_get_irq_byname(pdev, "lpass-irq-vaif");
> +		if (drvdata->vaif_irq < 0)
> +			return -ENODEV;
> +
> +		ret = devm_request_irq(&pdev->dev, drvdata->vaif_irq,
> +				lpass_platform_vaif_irq, IRQF_TRIGGER_RISING,
> +				"lpass-irq-vaif", drvdata);
> +		if (ret) {
> +			dev_err(&pdev->dev, "va irq request failed: %d\n", ret);
> +			return ret;
> +		}
> +
> +		ret = lpass_platform_alloc_va_dmactl_fields(&pdev->dev,
> +						 drvdata->va_lpaif_map);
> +		if (ret) {
> +			dev_err(&pdev->dev,
> +				"error initializing va dmactl fields: %d\n", ret);
> +			return ret;
> +		}
> +	}
> +
>   	if (drvdata->hdmi_port_enable) {
>   		drvdata->hdmiif_irq = platform_get_irq_byname(pdev, "lpass-irq-hdmi");
>   		if (drvdata->hdmiif_irq < 0)
> diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
> index 48602c1..d35dc93 100644
> --- a/sound/soc/qcom/lpass.h
> +++ b/sound/soc/qcom/lpass.h
> @@ -38,6 +38,18 @@
>   			return -EINVAL;         \
>   	} while (0)
>   
> +static inline bool is_cdc_dma_port(int dai_id)
> +{
> +	switch (dai_id) {
> +	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
> +	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
> +	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
>   struct lpaif_i2sctl {
>   	struct regmap_field *loopback;
>   	struct regmap_field *spken;
>
Srinivas Kandagatla Dec. 2, 2021, 11:22 a.m. UTC | #2
On 02/12/2021 10:55, Srinivasa Rao Mandadapu wrote:
>>>   +/* LPAIF RXTX IRQ */
>>> +#define LPAIF_RXTX_IRQ_REG_ADDR(v, addr, port, dai_id) \
>>> +        ((dai_id == LPASS_CDC_DMA_RX0 || dai_id == 
>>> LPASS_CDC_DMA_TX3) ? \
>>> +        (v->rxtx_irq_reg_base + (addr) + v->rxtx_irq_reg_stride * 
>>> (port)) : \
>>> +        (v->va_irq_reg_base + (addr) + v->va_irq_reg_stride * (port)))
>>> +
>>> +#define LPAIF_RXTX_IRQEN_REG(v, port, dai_id) 
>>> LPAIF_RXTX_IRQ_REG_ADDR(v, 0x0, port, dai_id)
>>> +#define LPAIF_RXTX_IRQSTAT_REG(v, port, dai_id) 
>>> LPAIF_RXTX_IRQ_REG_ADDR(v, 0x4, port, dai_id)
>>> +#define LPAIF_RXTX_IRQCLEAR_REG(v, port, dai_id) 
>>> LPAIF_RXTX_IRQ_REG_ADDR(v, 0xC, port, dai_id)
>>> +
>>
>> How about doing like this:
>>
>>
>> /* LPAIF RXTX IRQ */
>> #define LPAIF_RXTX_IRQ_REG_ADDR(v, addr, port) \
>>         (v->rxtx_irq_reg_base + (addr) + v->rxtx_irq_reg_stride * (port))
>>
>> #define LPAIF_RXTX_IRQEN_REG(v, port, dai_id) 
>> LPAIF_RXTX_IRQ_REG_ADDR(v, 0x0, port)
>> #define LPAIF_RXTX_IRQSTAT_REG(v, port, dai_id) 
>> LPAIF_RXTX_IRQ_REG_ADDR(v, 0x4, port)
>> #define LPAIF_RXTX_IRQCLEAR_REG(v, port, dai_id) 
>> LPAIF_RXTX_IRQ_REG_ADDR(v, 0xC, port)
>>
>> /* LPAIF VA IRQ */
>> #define LPAIF_VA_IRQ_REG_ADDR(v, addr, port) \
>>         (v->va_irq_reg_base + (addr) + v->va_irq_reg_stride * (port))
>>
>> #define LPAIF_VA_IRQEN_REG(v, port, dai_id) LPAIF_VA_IRQ_REG_ADDR(v, 
>> 0x0, port)
>> #define LPAIF_VA_IRQSTAT_REG(v, port, dai_id) LPAIF_VA_IRQ_REG_ADDR(v, 
>> 0x4, port)
>> #define LPAIF_VA_IRQCLEAR_REG(v, port, dai_id) 
>> LPAIF_VA_IRQ_REG_ADDR(v, 0xC, port)
>>
> With this we are seeing number macros increasing. How about handling 

Its okay to add new macros, this makes them much clear to the reader and 
inline with rest of the macros in the file.


--srini
> like below.
>> lpass.h:
> 
> static inline bool is_rxtx_cdc_dma_port(int dai_id)
> {
> 
>      switch (dai_id) {
>          case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
>          case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
>              return true;
>          default:
>              return false;
>        }
> }
> 
> 
> Usage:
> 
> #define LPAIF_RXTX_IRQ_REG_ADDR(v, addr, port, dai_id) \
> is_rxtx_cdc_dma_port(dai_id) ? \
> (v->rxtx_irq_reg_base + (addr) + v->rxtx_irq_reg_stride * (port)) : \
> (v->va_irq_reg_base + (addr) + v->va_irq_reg_stride * (port))
> 
>