Add support for audio on SC7280 based targets
Merge series from Srinivasa Rao Mandadapu <quic_srivasam@quicinc.com>: This patch set is to add support for Audio over wcd codec, digital mics, through digital codecs and without ADSP. Changes Since V15: -- Bisect patches to avoid build failure in other architectures. -- Remove redundant variables lpass variant structure. Changes Since V14: -- Split common wrapper function to separate wrapper for each handle in platform driver. -- Update cdc dma buffer handling with memremap with ioremap. -- Remove redundant error prints. -- Update irq flag. Changes Since V13: -- Change bulk cdc clock voting to individual clock voting. -- Remove redundant code, conditional check and prints. -- Fix typo errors. Changes Since V12: -- Fix arguments type mismatch. Changes Since V11: -- Fix kernel robot issue on arguments type mismatch. Changes Since V10: -- Split bulk clock voting to individual clock voting as per use case in cdc-dma driver. -- Add missing codec dma clocks. -- Update rxtx lpm buffer size. Changes Since V9: -- Change individual clock voting to bulk clock voting of lpass-sc7280 platform driver. -- Remove redundant clocks in lpass variant structure. -- Add mclk for MI2S based headset path. -- Remove unused lpass variant structure members in lpass header. Changes Since V8: -- Fix errors in sc7280 lpass cpu dt-bindings. -- Move to quicinc domain email id's. Changes Since V7: -- Fix indentation errors. -- Bisect patches to avoid interdependency. Changes Since V6: -- Split cdc dma regmap config macros. -- Add write dma reg fields for i2s path. -- Add helper function to distinguish rxtx and va dma ports. -- Optimizing clock and reg name in cpu dt-bindings. -- Update buffer management for cdc dma path. -- Remove Kconfig fields of machine driver. 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 (9): ASoC: qcom: Move lpass_pcm_data structure to lpass header ASoC: qcom: lpass: Add dma fields for codec dma lpass interface ASoC: qcom: Add helper function to get dma control and lpaif handle ASoC: qcom: Add register definition for codec rddma and wrdma ASoC: qcom: Add regmap config support for codec dma driver ASoC: qcom: Add support for codec dma driver ASoC: qcom: Add lpass CPU driver for codec dma control ASoC: dt-bindings: Add SC7280 lpass cpu bindings ASoC: qcom: lpass-sc7280: Add platform driver for lpass audio .../devicetree/bindings/sound/qcom,lpass-cpu.yaml | 75 ++- sound/soc/qcom/Kconfig | 11 + sound/soc/qcom/Makefile | 4 + sound/soc/qcom/lpass-cdc-dma.c | 301 ++++++++++ sound/soc/qcom/lpass-cpu.c | 253 ++++++++- sound/soc/qcom/lpass-lpaif-reg.h | 127 ++++- sound/soc/qcom/lpass-platform.c | 628 ++++++++++++++++++--- sound/soc/qcom/lpass-sc7280.c | 438 ++++++++++++++ sound/soc/qcom/lpass.h | 141 +++++ 9 files changed, 1890 insertions(+), 88 deletions(-) create mode 100644 sound/soc/qcom/lpass-cdc-dma.c create mode 100644 sound/soc/qcom/lpass-sc7280.c -- 2.7.4
This commit is contained in:
Коммит
6e1dda5fcd
|
@ -22,35 +22,41 @@ properties:
|
|||
- qcom,lpass-cpu
|
||||
- qcom,apq8016-lpass-cpu
|
||||
- qcom,sc7180-lpass-cpu
|
||||
- qcom,sc7280-lpass-cpu
|
||||
|
||||
reg:
|
||||
maxItems: 2
|
||||
minItems: 2
|
||||
maxItems: 6
|
||||
description: LPAIF core registers
|
||||
|
||||
reg-names:
|
||||
maxItems: 2
|
||||
minItems: 2
|
||||
maxItems: 6
|
||||
|
||||
clocks:
|
||||
minItems: 3
|
||||
maxItems: 6
|
||||
maxItems: 7
|
||||
|
||||
clock-names:
|
||||
minItems: 3
|
||||
maxItems: 6
|
||||
maxItems: 7
|
||||
|
||||
interrupts:
|
||||
maxItems: 2
|
||||
minItems: 2
|
||||
maxItems: 4
|
||||
description: LPAIF DMA buffer interrupt
|
||||
|
||||
interrupt-names:
|
||||
maxItems: 2
|
||||
minItems: 2
|
||||
maxItems: 4
|
||||
|
||||
qcom,adsp:
|
||||
$ref: /schemas/types.yaml#/definitions/phandle
|
||||
description: Phandle for the audio DSP node
|
||||
|
||||
iommus:
|
||||
maxItems: 2
|
||||
minItems: 2
|
||||
maxItems: 3
|
||||
description: Phandle to apps_smmu node with sid mask
|
||||
|
||||
power-domains:
|
||||
|
@ -69,7 +75,7 @@ patternProperties:
|
|||
"^dai-link@[0-9a-f]$":
|
||||
type: object
|
||||
description: |
|
||||
LPASS CPU dai node for each I2S device. Bindings of each node
|
||||
LPASS CPU dai node for each I2S device or Soundwire device. Bindings of each node
|
||||
depends on the specific driver providing the functionality and
|
||||
properties.
|
||||
properties:
|
||||
|
@ -174,6 +180,59 @@ allOf:
|
|||
- iommus
|
||||
- power-domains
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: qcom,sc7280-lpass-cpu
|
||||
|
||||
then:
|
||||
properties:
|
||||
clock-names:
|
||||
oneOf:
|
||||
- items: #for I2S
|
||||
- const: aon_cc_audio_hm_h
|
||||
- const: core_cc_sysnoc_mport_core
|
||||
- const: core_cc_ext_if1_ibit
|
||||
- items: #for Soundwire
|
||||
- const: aon_cc_audio_hm_h
|
||||
- const: audio_cc_codec_mem0
|
||||
- const: audio_cc_codec_mem1
|
||||
- const: audio_cc_codec_mem2
|
||||
- items: #for HDMI
|
||||
- const: aon_cc_audio_hm_h
|
||||
|
||||
reg-names:
|
||||
anyOf:
|
||||
- items: #for I2S
|
||||
- const: lpass-lpaif
|
||||
- items: #for I2S and HDMI
|
||||
- const: lpass-hdmiif
|
||||
- const: lpass-lpaif
|
||||
- items: #for I2S, soundwire and HDMI
|
||||
- const: lpass-hdmiif
|
||||
- const: lpass-lpaif
|
||||
- const: lpass-rxtx-cdc-dma-lpm
|
||||
- const: lpass-rxtx-lpaif
|
||||
- const: lpass-va-lpaif
|
||||
- const: lpass-va-cdc-dma-lpm
|
||||
interrupt-names:
|
||||
anyOf:
|
||||
- items: #for I2S
|
||||
- const: lpass-irq-lpaif
|
||||
- items: #for I2S and HDMI
|
||||
- const: lpass-irq-lpaif
|
||||
- const: lpass-irq-hdmi
|
||||
- items: #for I2S, soundwire and HDMI
|
||||
- const: lpass-irq-lpaif
|
||||
- const: lpass-irq-hdmi
|
||||
- const: lpass-irq-vaif
|
||||
- const: lpass-irq-rxtxif
|
||||
|
||||
required:
|
||||
- iommus
|
||||
- power-domains
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/sound/sc7180-lpass.h>
|
||||
|
|
|
@ -20,6 +20,10 @@ config SND_SOC_LPASS_PLATFORM
|
|||
tristate
|
||||
select REGMAP_MMIO
|
||||
|
||||
config SND_SOC_LPASS_CDC_DMA
|
||||
tristate
|
||||
select REGMAP_MMIO
|
||||
|
||||
config SND_SOC_LPASS_IPQ806X
|
||||
tristate
|
||||
select SND_SOC_LPASS_CPU
|
||||
|
@ -36,6 +40,13 @@ config SND_SOC_LPASS_SC7180
|
|||
select SND_SOC_LPASS_PLATFORM
|
||||
select SND_SOC_LPASS_HDMI
|
||||
|
||||
config SND_SOC_LPASS_SC7280
|
||||
tristate
|
||||
select SND_SOC_LPASS_CPU
|
||||
select SND_SOC_LPASS_PLATFORM
|
||||
select SND_SOC_LPASS_HDMI
|
||||
select SND_SOC_LPASS_CDC_DMA
|
||||
|
||||
config SND_SOC_STORM
|
||||
tristate "ASoC I2S support for Storm boards"
|
||||
depends on GPIOLIB
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Platform
|
||||
snd-soc-lpass-cpu-objs := lpass-cpu.o
|
||||
snd-soc-lpass-cdc-dma-objs := lpass-cdc-dma.o
|
||||
snd-soc-lpass-hdmi-objs := lpass-hdmi.o
|
||||
snd-soc-lpass-platform-objs := lpass-platform.o
|
||||
snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
|
||||
snd-soc-lpass-apq8016-objs := lpass-apq8016.o
|
||||
snd-soc-lpass-sc7180-objs := lpass-sc7180.o
|
||||
snd-soc-lpass-sc7280-objs := lpass-sc7280.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_CDC_DMA) += snd-soc-lpass-cdc-dma.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_HDMI) += snd-soc-lpass-hdmi.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_SC7180) += snd-soc-lpass-sc7180.o
|
||||
obj-$(CONFIG_SND_SOC_LPASS_SC7280) += snd-soc-lpass-sc7280.o
|
||||
|
||||
# Machine
|
||||
snd-soc-storm-objs := storm.o
|
||||
|
|
|
@ -0,0 +1,301 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* lpass-cdc-dma.c -- ALSA SoC CDC DMA CPU DAI driver for QTi LPASS
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/export.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/soc-dai.h>
|
||||
|
||||
#include "lpass-lpaif-reg.h"
|
||||
#include "lpass.h"
|
||||
|
||||
#define CODEC_MEM_HZ_NORMAL 153600000
|
||||
|
||||
enum codec_dma_interfaces {
|
||||
LPASS_CDC_DMA_INTERFACE1 = 1,
|
||||
LPASS_CDC_DMA_INTERFACE2,
|
||||
LPASS_CDC_DMA_INTERFACE3,
|
||||
LPASS_CDC_DMA_INTERFACE4,
|
||||
LPASS_CDC_DMA_INTERFACE5,
|
||||
LPASS_CDC_DMA_INTERFACE6,
|
||||
LPASS_CDC_DMA_INTERFACE7,
|
||||
LPASS_CDC_DMA_INTERFACE8,
|
||||
LPASS_CDC_DMA_INTERFACE9,
|
||||
LPASS_CDC_DMA_INTERFACE10,
|
||||
};
|
||||
|
||||
static void __lpass_get_dmactl_handle(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
|
||||
struct lpaif_dmactl **dmactl, int *id)
|
||||
{
|
||||
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);
|
||||
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||
struct snd_pcm_runtime *rt = substream->runtime;
|
||||
struct lpass_pcm_data *pcm_data = rt->private_data;
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
unsigned int dai_id = cpu_dai->driver->id;
|
||||
|
||||
switch (dai_id) {
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
*dmactl = drvdata->rxtx_rd_dmactl;
|
||||
*id = pcm_data->dma_ch;
|
||||
break;
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
*dmactl = drvdata->rxtx_wr_dmactl;
|
||||
*id = pcm_data->dma_ch - v->rxtx_wrdma_channel_start;
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
*dmactl = drvdata->va_wr_dmactl;
|
||||
*id = pcm_data->dma_ch - v->va_wrdma_channel_start;
|
||||
break;
|
||||
default:
|
||||
dev_err(soc_runtime->dev, "invalid dai id for dma ctl: %d\n", dai_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int __lpass_get_codec_dma_intf_type(int dai_id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (dai_id) {
|
||||
case LPASS_CDC_DMA_RX0:
|
||||
case LPASS_CDC_DMA_TX0:
|
||||
case LPASS_CDC_DMA_VA_TX0:
|
||||
ret = LPASS_CDC_DMA_INTERFACE1;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX1:
|
||||
case LPASS_CDC_DMA_TX1:
|
||||
case LPASS_CDC_DMA_VA_TX1:
|
||||
ret = LPASS_CDC_DMA_INTERFACE2;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX2:
|
||||
case LPASS_CDC_DMA_TX2:
|
||||
case LPASS_CDC_DMA_VA_TX2:
|
||||
ret = LPASS_CDC_DMA_INTERFACE3;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX3:
|
||||
case LPASS_CDC_DMA_TX3:
|
||||
case LPASS_CDC_DMA_VA_TX3:
|
||||
ret = LPASS_CDC_DMA_INTERFACE4;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX4:
|
||||
case LPASS_CDC_DMA_TX4:
|
||||
case LPASS_CDC_DMA_VA_TX4:
|
||||
ret = LPASS_CDC_DMA_INTERFACE5;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX5:
|
||||
case LPASS_CDC_DMA_TX5:
|
||||
case LPASS_CDC_DMA_VA_TX5:
|
||||
ret = LPASS_CDC_DMA_INTERFACE6;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX6:
|
||||
case LPASS_CDC_DMA_TX6:
|
||||
case LPASS_CDC_DMA_VA_TX6:
|
||||
ret = LPASS_CDC_DMA_INTERFACE7;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX7:
|
||||
case LPASS_CDC_DMA_TX7:
|
||||
case LPASS_CDC_DMA_VA_TX7:
|
||||
ret = LPASS_CDC_DMA_INTERFACE8;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX8:
|
||||
case LPASS_CDC_DMA_TX8:
|
||||
case LPASS_CDC_DMA_VA_TX8:
|
||||
ret = LPASS_CDC_DMA_INTERFACE9;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX9:
|
||||
ret = LPASS_CDC_DMA_INTERFACE10;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __lpass_platform_codec_intf_init(struct snd_soc_dai *dai,
|
||||
struct snd_pcm_substream *substream)
|
||||
{
|
||||
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);
|
||||
struct lpaif_dmactl *dmactl = NULL;
|
||||
struct device *dev = soc_runtime->dev;
|
||||
int ret, id, codec_intf;
|
||||
unsigned int dai_id = cpu_dai->driver->id;
|
||||
|
||||
codec_intf = __lpass_get_codec_dma_intf_type(dai_id);
|
||||
if (codec_intf < 0) {
|
||||
dev_err(dev, "failed to get codec_intf: %d\n", codec_intf);
|
||||
return codec_intf;
|
||||
}
|
||||
|
||||
__lpass_get_dmactl_handle(substream, dai, &dmactl, &id);
|
||||
if (!dmactl)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_fields_write(dmactl->codec_intf, id, codec_intf);
|
||||
if (ret) {
|
||||
dev_err(dev, "error writing to dmactl codec_intf reg field: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_fields_write(dmactl->codec_fs_sel, id, 0x0);
|
||||
if (ret) {
|
||||
dev_err(dev, "error writing to dmactl codec_fs_sel reg field: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_fields_write(dmactl->codec_fs_delay, id, 0x0);
|
||||
if (ret) {
|
||||
dev_err(dev, "error writing to dmactl codec_fs_delay reg field: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_fields_write(dmactl->codec_pack, id, 0x1);
|
||||
if (ret) {
|
||||
dev_err(dev, "error writing to dmactl codec_pack reg field: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_ON);
|
||||
if (ret) {
|
||||
dev_err(dev, "error writing to dmactl codec_enable reg field: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||
struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
|
||||
|
||||
switch (dai->id) {
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
clk_set_rate(drvdata->codec_mem0, CODEC_MEM_HZ_NORMAL);
|
||||
clk_prepare_enable(drvdata->codec_mem0);
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
|
||||
clk_set_rate(drvdata->va_mem0, CODEC_MEM_HZ_NORMAL);
|
||||
clk_prepare_enable(drvdata->va_mem0);
|
||||
break;
|
||||
default:
|
||||
dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai->id);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpass_cdc_dma_daiops_shutdown(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
|
||||
struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
|
||||
|
||||
switch (dai->id) {
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
clk_disable_unprepare(drvdata->codec_mem0);
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
|
||||
clk_disable_unprepare(drvdata->va_mem0);
|
||||
break;
|
||||
default:
|
||||
dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai->id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int lpass_cdc_dma_daiops_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
|
||||
struct lpaif_dmactl *dmactl = NULL;
|
||||
unsigned int ret, regval;
|
||||
unsigned int channels = params_channels(params);
|
||||
int id;
|
||||
|
||||
switch (channels) {
|
||||
case 1:
|
||||
regval = LPASS_CDC_DMA_INTF_ONE_CHANNEL;
|
||||
break;
|
||||
case 2:
|
||||
regval = LPASS_CDC_DMA_INTF_TWO_CHANNEL;
|
||||
break;
|
||||
case 4:
|
||||
regval = LPASS_CDC_DMA_INTF_FOUR_CHANNEL;
|
||||
break;
|
||||
case 6:
|
||||
regval = LPASS_CDC_DMA_INTF_SIX_CHANNEL;
|
||||
break;
|
||||
case 8:
|
||||
regval = LPASS_CDC_DMA_INTF_EIGHT_CHANNEL;
|
||||
break;
|
||||
default:
|
||||
dev_err(soc_runtime->dev, "invalid PCM config\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
__lpass_get_dmactl_handle(substream, dai, &dmactl, &id);
|
||||
if (!dmactl)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_fields_write(dmactl->codec_channel, id, regval);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev,
|
||||
"error writing to dmactl codec_channel reg field: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpass_cdc_dma_daiops_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
|
||||
struct lpaif_dmactl *dmactl;
|
||||
int ret = 0, id;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
__lpass_platform_codec_intf_init(dai, substream);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
__lpass_get_dmactl_handle(substream, dai, &dmactl, &id);
|
||||
if (!dmactl)
|
||||
return -EINVAL;
|
||||
|
||||
ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_OFF);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev,
|
||||
"error writing to dmactl codec_enable reg: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, cmd);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops = {
|
||||
.startup = lpass_cdc_dma_daiops_startup,
|
||||
.shutdown = lpass_cdc_dma_daiops_shutdown,
|
||||
.hw_params = lpass_cdc_dma_daiops_hw_params,
|
||||
.trigger = lpass_cdc_dma_daiops_trigger,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cdc_dma_dai_ops);
|
||||
|
||||
MODULE_DESCRIPTION("QTi LPASS CDC DMA Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -28,6 +28,8 @@
|
|||
#define LPASS_CPU_I2S_SD2_3_MASK GENMASK(3, 2)
|
||||
#define LPASS_CPU_I2S_SD0_1_2_MASK GENMASK(2, 0)
|
||||
#define LPASS_CPU_I2S_SD0_1_2_3_MASK GENMASK(3, 0)
|
||||
#define LPASS_REG_READ 1
|
||||
#define LPASS_REG_WRITE 0
|
||||
|
||||
/*
|
||||
* Channel maps for Quad channel playbacks on MI2S Secondary
|
||||
|
@ -798,6 +800,189 @@ static struct regmap_config lpass_hdmi_regmap_config = {
|
|||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static bool __lpass_rxtx_regmap_accessible(struct device *dev, unsigned int reg, bool rw)
|
||||
{
|
||||
struct lpass_data *drvdata = dev_get_drvdata(dev);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < v->rxtx_irq_ports; ++i) {
|
||||
if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_RXTX_IRQEN_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < v->rxtx_rdma_channels; ++i) {
|
||||
if (reg == LPAIF_CDC_RXTX_RDMACTL_REG(v, i, LPASS_CDC_DMA_RX0))
|
||||
return true;
|
||||
if (reg == LPAIF_CDC_RXTX_RDMABASE_REG(v, i, LPASS_CDC_DMA_RX0))
|
||||
return true;
|
||||
if (reg == LPAIF_CDC_RXTX_RDMABUFF_REG(v, i, LPASS_CDC_DMA_RX0))
|
||||
return true;
|
||||
if (rw == LPASS_REG_READ) {
|
||||
if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0))
|
||||
return true;
|
||||
}
|
||||
if (reg == LPAIF_CDC_RXTX_RDMAPER_REG(v, i, LPASS_CDC_DMA_RX0))
|
||||
return true;
|
||||
if (reg == LPAIF_CDC_RXTX_RDMA_INTF_REG(v, i, LPASS_CDC_DMA_RX0))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < v->rxtx_wrdma_channels; ++i) {
|
||||
if (reg == LPAIF_CDC_RXTX_WRDMACTL_REG(v, i + v->rxtx_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_TX3))
|
||||
return true;
|
||||
if (reg == LPAIF_CDC_RXTX_WRDMABASE_REG(v, i + v->rxtx_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_TX3))
|
||||
return true;
|
||||
if (reg == LPAIF_CDC_RXTX_WRDMABUFF_REG(v, i + v->rxtx_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_TX3))
|
||||
return true;
|
||||
if (rw == LPASS_REG_READ) {
|
||||
if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i, LPASS_CDC_DMA_RX0))
|
||||
return true;
|
||||
}
|
||||
if (reg == LPAIF_CDC_RXTX_WRDMAPER_REG(v, i + v->rxtx_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_TX3))
|
||||
return true;
|
||||
if (reg == LPAIF_CDC_RXTX_WRDMA_INTF_REG(v, i + v->rxtx_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_TX3))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool lpass_rxtx_regmap_writeable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return __lpass_rxtx_regmap_accessible(dev, reg, LPASS_REG_WRITE);
|
||||
}
|
||||
|
||||
static bool lpass_rxtx_regmap_readable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return __lpass_rxtx_regmap_accessible(dev, reg, LPASS_REG_READ);
|
||||
}
|
||||
|
||||
static bool lpass_rxtx_regmap_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct lpass_data *drvdata = dev_get_drvdata(dev);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < v->rxtx_irq_ports; ++i) {
|
||||
if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < v->rxtx_rdma_channels; ++i)
|
||||
if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0))
|
||||
return true;
|
||||
|
||||
for (i = 0; i < v->rxtx_wrdma_channels; ++i)
|
||||
if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i + v->rxtx_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_TX3))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool __lpass_va_regmap_accessible(struct device *dev, unsigned int reg, bool rw)
|
||||
{
|
||||
struct lpass_data *drvdata = dev_get_drvdata(dev);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < v->va_irq_ports; ++i) {
|
||||
if (reg == LPAIF_VA_IRQCLEAR_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_VA_IRQEN_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_VA_IRQSTAT_REG(v, i))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < v->va_wrdma_channels; ++i) {
|
||||
if (reg == LPAIF_CDC_VA_WRDMACTL_REG(v, i + v->va_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_VA_TX0))
|
||||
return true;
|
||||
if (reg == LPAIF_CDC_VA_WRDMABASE_REG(v, i + v->va_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_VA_TX0))
|
||||
return true;
|
||||
if (reg == LPAIF_CDC_VA_WRDMABUFF_REG(v, i + v->va_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_VA_TX0))
|
||||
return true;
|
||||
if (rw == LPASS_REG_READ) {
|
||||
if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_VA_TX0))
|
||||
return true;
|
||||
}
|
||||
if (reg == LPAIF_CDC_VA_WRDMAPER_REG(v, i + v->va_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_VA_TX0))
|
||||
return true;
|
||||
if (reg == LPAIF_CDC_VA_WRDMA_INTF_REG(v, i + v->va_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_VA_TX0))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool lpass_va_regmap_writeable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return __lpass_va_regmap_accessible(dev, reg, LPASS_REG_WRITE);
|
||||
}
|
||||
|
||||
static bool lpass_va_regmap_readable(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return __lpass_va_regmap_accessible(dev, reg, LPASS_REG_READ);
|
||||
}
|
||||
|
||||
static bool lpass_va_regmap_volatile(struct device *dev, unsigned int reg)
|
||||
{
|
||||
struct lpass_data *drvdata = dev_get_drvdata(dev);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < v->va_irq_ports; ++i) {
|
||||
if (reg == LPAIF_VA_IRQCLEAR_REG(v, i))
|
||||
return true;
|
||||
if (reg == LPAIF_VA_IRQSTAT_REG(v, i))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (i = 0; i < v->va_wrdma_channels; ++i) {
|
||||
if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start,
|
||||
LPASS_CDC_DMA_VA_TX0))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct regmap_config lpass_rxtx_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.writeable_reg = lpass_rxtx_regmap_writeable,
|
||||
.readable_reg = lpass_rxtx_regmap_readable,
|
||||
.volatile_reg = lpass_rxtx_regmap_volatile,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static struct regmap_config lpass_va_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.writeable_reg = lpass_va_regmap_writeable,
|
||||
.readable_reg = lpass_va_regmap_readable,
|
||||
.volatile_reg = lpass_va_regmap_volatile,
|
||||
.cache_type = REGCACHE_FLAT,
|
||||
};
|
||||
|
||||
static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev,
|
||||
struct device_node *node,
|
||||
const char *name)
|
||||
|
@ -857,6 +1042,8 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
|
|||
}
|
||||
if (id == LPASS_DP_RX) {
|
||||
data->hdmi_port_enable = 1;
|
||||
} else if (is_cdc_dma_port(id)) {
|
||||
data->codec_dma_enable = 1;
|
||||
} else {
|
||||
data->mi2s_playback_sd_mode[id] =
|
||||
of_lpass_cpu_parse_sd_lines(dev, node,
|
||||
|
@ -868,10 +1055,33 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int of_lpass_cdc_dma_clks_parse(struct device *dev,
|
||||
struct lpass_data *data)
|
||||
{
|
||||
data->codec_mem0 = devm_clk_get(dev, "audio_cc_codec_mem0");
|
||||
if (IS_ERR(data->codec_mem0))
|
||||
return PTR_ERR(data->codec_mem0);
|
||||
|
||||
data->codec_mem1 = devm_clk_get(dev, "audio_cc_codec_mem1");
|
||||
if (IS_ERR(data->codec_mem1))
|
||||
return PTR_ERR(data->codec_mem1);
|
||||
|
||||
data->codec_mem2 = devm_clk_get(dev, "audio_cc_codec_mem2");
|
||||
if (IS_ERR(data->codec_mem2))
|
||||
return PTR_ERR(data->codec_mem2);
|
||||
|
||||
data->va_mem0 = devm_clk_get(dev, "aon_cc_va_mem0");
|
||||
if (IS_ERR(data->va_mem0))
|
||||
return PTR_ERR(data->va_mem0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lpass_data *drvdata;
|
||||
struct device_node *dsp_of_node;
|
||||
struct resource *res;
|
||||
struct lpass_variant *variant;
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct of_device_id *match;
|
||||
|
@ -897,6 +1107,47 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
|
|||
|
||||
of_lpass_cpu_parse_dai_data(dev, drvdata);
|
||||
|
||||
if (drvdata->codec_dma_enable) {
|
||||
drvdata->rxtx_lpaif =
|
||||
devm_platform_ioremap_resource_byname(pdev, "lpass-rxtx-lpaif");
|
||||
if (IS_ERR(drvdata->rxtx_lpaif))
|
||||
return PTR_ERR(drvdata->rxtx_lpaif);
|
||||
|
||||
drvdata->va_lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-va-lpaif");
|
||||
if (IS_ERR(drvdata->va_lpaif))
|
||||
return PTR_ERR(drvdata->va_lpaif);
|
||||
|
||||
lpass_rxtx_regmap_config.max_register = LPAIF_CDC_RXTX_WRDMAPER_REG(variant,
|
||||
variant->rxtx_wrdma_channels +
|
||||
variant->rxtx_wrdma_channel_start, LPASS_CDC_DMA_TX3);
|
||||
|
||||
drvdata->rxtx_lpaif_map = devm_regmap_init_mmio(dev, drvdata->rxtx_lpaif,
|
||||
&lpass_rxtx_regmap_config);
|
||||
if (IS_ERR(drvdata->rxtx_lpaif_map))
|
||||
return PTR_ERR(drvdata->rxtx_lpaif_map);
|
||||
|
||||
lpass_va_regmap_config.max_register = LPAIF_CDC_VA_WRDMAPER_REG(variant,
|
||||
variant->va_wrdma_channels +
|
||||
variant->va_wrdma_channel_start, LPASS_CDC_DMA_VA_TX0);
|
||||
|
||||
drvdata->va_lpaif_map = devm_regmap_init_mmio(dev, drvdata->va_lpaif,
|
||||
&lpass_va_regmap_config);
|
||||
if (IS_ERR(drvdata->va_lpaif_map))
|
||||
return PTR_ERR(drvdata->va_lpaif_map);
|
||||
|
||||
ret = of_lpass_cdc_dma_clks_parse(dev, drvdata);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get cdc dma clocks %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm");
|
||||
drvdata->rxtx_cdc_dma_lpm_buf = res->start;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm");
|
||||
drvdata->va_cdc_dma_lpm_buf = res->start;
|
||||
}
|
||||
|
||||
drvdata->lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-lpaif");
|
||||
if (IS_ERR(drvdata->lpaif))
|
||||
return PTR_ERR(drvdata->lpaif);
|
||||
|
@ -939,7 +1190,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
|
|||
|
||||
for (i = 0; i < variant->num_dai; i++) {
|
||||
dai_id = variant->dai_driver[i].id;
|
||||
if (dai_id == LPASS_DP_RX)
|
||||
if (dai_id == LPASS_DP_RX || is_cdc_dma_port(dai_id))
|
||||
continue;
|
||||
|
||||
drvdata->mi2s_osr_clk[dai_id] = devm_clk_get_optional(dev,
|
||||
|
|
|
@ -74,6 +74,21 @@
|
|||
#define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port))
|
||||
#define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port))
|
||||
|
||||
/* 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) LPAIF_RXTX_IRQ_REG_ADDR(v, 0x0, port)
|
||||
#define LPAIF_RXTX_IRQSTAT_REG(v, port) LPAIF_RXTX_IRQ_REG_ADDR(v, 0x4, port)
|
||||
#define LPAIF_RXTX_IRQCLEAR_REG(v, port) 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) LPAIF_VA_IRQ_REG_ADDR(v, 0x0, port)
|
||||
#define LPAIF_VA_IRQSTAT_REG(v, port) LPAIF_VA_IRQ_REG_ADDR(v, 0x4, port)
|
||||
#define LPAIF_VA_IRQCLEAR_REG(v, port) LPAIF_VA_IRQ_REG_ADDR(v, 0xC, port)
|
||||
|
||||
#define LPASS_HDMITX_APP_IRQ_REG_ADDR(v, addr) \
|
||||
((v->hdmi_irq_reg_base) + (addr))
|
||||
|
@ -139,12 +154,112 @@
|
|||
(LPAIF_INTFDMA_REG(v, chan, reg, dai_id)) : \
|
||||
LPAIF_WRDMA##reg##_REG(v, chan))
|
||||
|
||||
#define LPAIF_DMACTL_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, CTL, dai_id)
|
||||
#define LPAIF_DMABASE_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, BASE, dai_id)
|
||||
#define LPAIF_DMABUFF_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, BUFF, dai_id)
|
||||
#define LPAIF_DMACURR_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, CURR, dai_id)
|
||||
#define LPAIF_DMAPER_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, PER, dai_id)
|
||||
#define LPAIF_DMAPERCNT_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, PERCNT, dai_id)
|
||||
#define LPAIF_DMACTL_REG(v, chan, dir, dai_id) \
|
||||
(is_cdc_dma_port(dai_id) ? \
|
||||
__LPAIF_CDC_DMA_REG(v, chan, dir, CTL, dai_id) : \
|
||||
__LPAIF_DMA_REG(v, chan, dir, CTL, dai_id))
|
||||
#define LPAIF_DMABASE_REG(v, chan, dir, dai_id) \
|
||||
(is_cdc_dma_port(dai_id) ? \
|
||||
__LPAIF_CDC_DMA_REG(v, chan, dir, BASE, dai_id) : \
|
||||
__LPAIF_DMA_REG(v, chan, dir, BASE, dai_id))
|
||||
#define LPAIF_DMABUFF_REG(v, chan, dir, dai_id) \
|
||||
(is_cdc_dma_port(dai_id) ? \
|
||||
__LPAIF_CDC_DMA_REG(v, chan, dir, BUFF, dai_id) : \
|
||||
__LPAIF_DMA_REG(v, chan, dir, BUFF, dai_id))
|
||||
#define LPAIF_DMACURR_REG(v, chan, dir, dai_id) \
|
||||
(is_cdc_dma_port(dai_id) ? \
|
||||
__LPAIF_CDC_DMA_REG(v, chan, dir, CURR, dai_id) : \
|
||||
__LPAIF_DMA_REG(v, chan, dir, CURR, dai_id))
|
||||
#define LPAIF_DMAPER_REG(v, chan, dir, dai_id) \
|
||||
(is_cdc_dma_port(dai_id) ? \
|
||||
__LPAIF_CDC_DMA_REG(v, chan, dir, PER, dai_id) : \
|
||||
__LPAIF_DMA_REG(v, chan, dir, PER, dai_id))
|
||||
#define LPAIF_DMAPERCNT_REG(v, chan, dir, dai_id) \
|
||||
(is_cdc_dma_port(dai_id) ? \
|
||||
__LPAIF_CDC_DMA_REG(v, chan, dir, PERCNT, dai_id) : \
|
||||
__LPAIF_DMA_REG(v, chan, dir, PERCNT, dai_id))
|
||||
|
||||
#define LPAIF_CDC_RDMA_REG_ADDR(v, addr, chan, dai_id) \
|
||||
(is_rxtx_cdc_dma_port(dai_id) ? \
|
||||
(v->rxtx_rdma_reg_base + (addr) + v->rxtx_rdma_reg_stride * (chan)) : \
|
||||
(v->va_rdma_reg_base + (addr) + v->va_rdma_reg_stride * (chan)))
|
||||
|
||||
#define LPAIF_CDC_RXTX_RDMACTL_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_RDMA_REG_ADDR(v, 0x00, (chan), dai_id)
|
||||
#define LPAIF_CDC_RXTX_RDMABASE_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_RDMA_REG_ADDR(v, 0x04, (chan), dai_id)
|
||||
#define LPAIF_CDC_RXTX_RDMABUFF_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_RDMA_REG_ADDR(v, 0x08, (chan), dai_id)
|
||||
#define LPAIF_CDC_RXTX_RDMACURR_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_RDMA_REG_ADDR(v, 0x0C, (chan), dai_id)
|
||||
#define LPAIF_CDC_RXTX_RDMAPER_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_RDMA_REG_ADDR(v, 0x10, (chan), dai_id)
|
||||
#define LPAIF_CDC_RXTX_RDMA_INTF_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_RDMA_REG_ADDR(v, 0x50, (chan), dai_id)
|
||||
|
||||
#define LPAIF_CDC_VA_RDMACTL_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x00, (chan), dai_id)
|
||||
#define LPAIF_CDC_VA_RDMABASE_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x04, (chan), dai_id)
|
||||
#define LPAIF_CDC_VA_RDMABUFF_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x08, (chan), dai_id)
|
||||
#define LPAIF_CDC_VA_RDMACURR_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x0C, (chan), dai_id)
|
||||
#define LPAIF_CDC_VA_RDMAPER_REG(v, chan, dai_id) LPAIF_CDC_RDMA_REG_ADDR(v, 0x10, (chan), dai_id)
|
||||
#define LPAIF_CDC_VA_RDMA_INTF_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_RDMA_REG_ADDR(v, 0x50, (chan), dai_id)
|
||||
|
||||
#define LPAIF_CDC_WRDMA_REG_ADDR(v, addr, chan, dai_id) \
|
||||
(is_rxtx_cdc_dma_port(dai_id) ? \
|
||||
(v->rxtx_wrdma_reg_base + (addr) + \
|
||||
v->rxtx_wrdma_reg_stride * (chan - v->rxtx_wrdma_channel_start)) : \
|
||||
(v->va_wrdma_reg_base + (addr) + \
|
||||
v->va_wrdma_reg_stride * (chan - v->va_wrdma_channel_start)))
|
||||
|
||||
#define LPAIF_CDC_RXTX_WRDMACTL_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x00, (chan), dai_id)
|
||||
#define LPAIF_CDC_RXTX_WRDMABASE_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x04, (chan), dai_id)
|
||||
#define LPAIF_CDC_RXTX_WRDMABUFF_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x08, (chan), dai_id)
|
||||
#define LPAIF_CDC_RXTX_WRDMACURR_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x0C, (chan), dai_id)
|
||||
#define LPAIF_CDC_RXTX_WRDMAPER_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x10, (chan), dai_id)
|
||||
#define LPAIF_CDC_RXTX_WRDMA_INTF_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x50, (chan), dai_id)
|
||||
|
||||
#define LPAIF_CDC_VA_WRDMACTL_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x00, (chan), dai_id)
|
||||
#define LPAIF_CDC_VA_WRDMABASE_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x04, (chan), dai_id)
|
||||
#define LPAIF_CDC_VA_WRDMABUFF_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x08, (chan), dai_id)
|
||||
#define LPAIF_CDC_VA_WRDMACURR_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x0C, (chan), dai_id)
|
||||
#define LPAIF_CDC_VA_WRDMAPER_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x10, (chan), dai_id)
|
||||
#define LPAIF_CDC_VA_WRDMA_INTF_REG(v, chan, dai_id) \
|
||||
LPAIF_CDC_WRDMA_REG_ADDR(v, 0x50, (chan), dai_id)
|
||||
|
||||
#define __LPAIF_CDC_RDDMA_REG(v, chan, dir, reg, dai_id) \
|
||||
(is_rxtx_cdc_dma_port(dai_id) ? LPAIF_CDC_RXTX_RDMA##reg##_REG(v, chan, dai_id) : \
|
||||
LPAIF_CDC_VA_RDMA##reg##_REG(v, chan, dai_id))
|
||||
|
||||
#define __LPAIF_CDC_WRDMA_REG(v, chan, dir, reg, dai_id) \
|
||||
(is_rxtx_cdc_dma_port(dai_id) ? LPAIF_CDC_RXTX_WRDMA##reg##_REG(v, chan, dai_id) : \
|
||||
LPAIF_CDC_VA_WRDMA##reg##_REG(v, chan, dai_id))
|
||||
|
||||
#define __LPAIF_CDC_DMA_REG(v, chan, dir, reg, dai_id) \
|
||||
((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \
|
||||
__LPAIF_CDC_RDDMA_REG(v, chan, dir, reg, dai_id) : \
|
||||
__LPAIF_CDC_WRDMA_REG(v, chan, dir, reg, dai_id))
|
||||
|
||||
#define LPAIF_CDC_INTF_REG(v, chan, dir, dai_id) \
|
||||
((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \
|
||||
LPAIF_CDC_RDMA_INTF_REG(v, chan, dai_id) : \
|
||||
LPAIF_CDC_WRDMA_INTF_REG(v, chan, dai_id))
|
||||
|
||||
#define LPAIF_INTF_REG(v, chan, dir, dai_id) \
|
||||
(is_cdc_dma_port(dai_id) ? \
|
||||
LPAIF_CDC_INTF_REG(v, chan, dir, dai_id) : \
|
||||
LPAIF_DMACTL_REG(v, chan, dir, dai_id))
|
||||
|
||||
#define LPAIF_DMACTL_BURSTEN_SINGLE 0
|
||||
#define LPAIF_DMACTL_BURSTEN_INCR4 1
|
||||
|
|
|
@ -18,13 +18,11 @@
|
|||
|
||||
#define DRV_NAME "lpass-platform"
|
||||
|
||||
struct lpass_pcm_data {
|
||||
int dma_ch;
|
||||
int i2s_port;
|
||||
};
|
||||
|
||||
#define LPASS_PLATFORM_BUFFER_SIZE (24 * 2 * 1024)
|
||||
#define LPASS_PLATFORM_PERIODS 2
|
||||
#define LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE (8 * 1024)
|
||||
#define LPASS_VA_CDC_DMA_LPM_BUFF_SIZE (12 * 1024)
|
||||
#define LPASS_CDC_DMA_REGISTER_FIELDS_MAX 15
|
||||
|
||||
static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
|
@ -50,6 +48,99 @@ static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
|
|||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hardware lpass_platform_rxtx_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16 |
|
||||
SNDRV_PCM_FMTBIT_S24 |
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 192000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.buffer_bytes_max = LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE,
|
||||
.period_bytes_max = LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE /
|
||||
LPASS_PLATFORM_PERIODS,
|
||||
.period_bytes_min = LPASS_RXTX_CDC_DMA_LPM_BUFF_SIZE /
|
||||
LPASS_PLATFORM_PERIODS,
|
||||
.periods_min = LPASS_PLATFORM_PERIODS,
|
||||
.periods_max = LPASS_PLATFORM_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static const struct snd_pcm_hardware lpass_platform_va_hardware = {
|
||||
.info = SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE |
|
||||
SNDRV_PCM_INFO_RESUME,
|
||||
.formats = SNDRV_PCM_FMTBIT_S16 |
|
||||
SNDRV_PCM_FMTBIT_S24 |
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
.rates = SNDRV_PCM_RATE_8000_192000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 192000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 8,
|
||||
.buffer_bytes_max = LPASS_VA_CDC_DMA_LPM_BUFF_SIZE,
|
||||
.period_bytes_max = LPASS_VA_CDC_DMA_LPM_BUFF_SIZE /
|
||||
LPASS_PLATFORM_PERIODS,
|
||||
.period_bytes_min = LPASS_VA_CDC_DMA_LPM_BUFF_SIZE /
|
||||
LPASS_PLATFORM_PERIODS,
|
||||
.periods_min = LPASS_PLATFORM_PERIODS,
|
||||
.periods_max = LPASS_PLATFORM_PERIODS,
|
||||
.fifo_size = 0,
|
||||
};
|
||||
|
||||
static int lpass_platform_alloc_rxtx_dmactl_fields(struct device *dev,
|
||||
struct regmap *map)
|
||||
{
|
||||
struct lpass_data *drvdata = dev_get_drvdata(dev);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
struct lpaif_dmactl *rd_dmactl, *wr_dmactl;
|
||||
int rval;
|
||||
|
||||
rd_dmactl = devm_kzalloc(dev, sizeof(*rd_dmactl), GFP_KERNEL);
|
||||
if (!rd_dmactl)
|
||||
return -ENOMEM;
|
||||
|
||||
wr_dmactl = devm_kzalloc(dev, sizeof(*wr_dmactl), GFP_KERNEL);
|
||||
if (!wr_dmactl)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->rxtx_rd_dmactl = rd_dmactl;
|
||||
drvdata->rxtx_wr_dmactl = wr_dmactl;
|
||||
|
||||
rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->intf,
|
||||
&v->rxtx_rdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX);
|
||||
if (rval)
|
||||
return rval;
|
||||
|
||||
return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf,
|
||||
&v->rxtx_wrdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX);
|
||||
}
|
||||
|
||||
static int lpass_platform_alloc_va_dmactl_fields(struct device *dev,
|
||||
struct regmap *map)
|
||||
{
|
||||
struct lpass_data *drvdata = dev_get_drvdata(dev);
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
struct lpaif_dmactl *wr_dmactl;
|
||||
|
||||
wr_dmactl = devm_kzalloc(dev, sizeof(*wr_dmactl), GFP_KERNEL);
|
||||
if (!wr_dmactl)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->va_wr_dmactl = wr_dmactl;
|
||||
return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf,
|
||||
&v->va_wrdma_intf, LPASS_CDC_DMA_REGISTER_FIELDS_MAX);
|
||||
}
|
||||
|
||||
|
||||
static int lpass_platform_alloc_dmactl_fields(struct device *dev,
|
||||
struct regmap *map)
|
||||
{
|
||||
|
@ -128,25 +219,55 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
|
|||
return dma_ch;
|
||||
}
|
||||
|
||||
if (cpu_dai->driver->id == LPASS_DP_RX) {
|
||||
map = drvdata->hdmiif_map;
|
||||
drvdata->hdmi_substream[dma_ch] = substream;
|
||||
} else {
|
||||
switch (dai_id) {
|
||||
case MI2S_PRIMARY ... MI2S_QUINARY:
|
||||
map = drvdata->lpaif_map;
|
||||
drvdata->substream[dma_ch] = substream;
|
||||
break;
|
||||
case LPASS_DP_RX:
|
||||
map = drvdata->hdmiif_map;
|
||||
drvdata->hdmi_substream[dma_ch] = substream;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
map = drvdata->rxtx_lpaif_map;
|
||||
drvdata->rxtx_substream[dma_ch] = substream;
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
map = drvdata->va_lpaif_map;
|
||||
drvdata->va_substream[dma_ch] = substream;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
data->dma_ch = dma_ch;
|
||||
ret = regmap_write(map,
|
||||
LPAIF_DMACTL_REG(v, dma_ch, dir, data->i2s_port), 0);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev,
|
||||
"error writing to rdmactl reg: %d\n", ret);
|
||||
return ret;
|
||||
switch (dai_id) {
|
||||
case MI2S_PRIMARY ... MI2S_QUINARY:
|
||||
case LPASS_DP_RX:
|
||||
ret = regmap_write(map, LPAIF_DMACTL_REG(v, dma_ch, dir, data->i2s_port), 0);
|
||||
if (ret) {
|
||||
kfree(data);
|
||||
dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
|
||||
runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
snd_soc_set_runtime_hwparams(substream, &lpass_platform_rxtx_hardware);
|
||||
runtime->dma_bytes = lpass_platform_rxtx_hardware.buffer_bytes_max;
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
snd_soc_set_runtime_hwparams(substream, &lpass_platform_va_hardware);
|
||||
runtime->dma_bytes = lpass_platform_va_hardware.buffer_bytes_max;
|
||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
|
||||
|
||||
runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
|
||||
|
||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
if (ret < 0) {
|
||||
|
@ -171,10 +292,25 @@ static int lpass_platform_pcmops_close(struct snd_soc_component *component,
|
|||
unsigned int dai_id = cpu_dai->driver->id;
|
||||
|
||||
data = runtime->private_data;
|
||||
if (dai_id == LPASS_DP_RX)
|
||||
drvdata->hdmi_substream[data->dma_ch] = NULL;
|
||||
else
|
||||
|
||||
switch (dai_id) {
|
||||
case MI2S_PRIMARY ... MI2S_QUINARY:
|
||||
drvdata->substream[data->dma_ch] = NULL;
|
||||
break;
|
||||
case LPASS_DP_RX:
|
||||
drvdata->hdmi_substream[data->dma_ch] = NULL;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
drvdata->rxtx_substream[data->dma_ch] = NULL;
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
drvdata->va_substream[data->dma_ch] = NULL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (v->free_dma_channel)
|
||||
v->free_dma_channel(drvdata, data->dma_ch, dai_id);
|
||||
|
||||
|
@ -182,6 +318,100 @@ static int lpass_platform_pcmops_close(struct snd_soc_component *component,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct lpaif_dmactl *__lpass_get_dmactl_handle(const struct snd_pcm_substream *substream,
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
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);
|
||||
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
|
||||
struct lpaif_dmactl *dmactl = NULL;
|
||||
|
||||
switch (cpu_dai->driver->id) {
|
||||
case MI2S_PRIMARY ... MI2S_QUINARY:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
dmactl = drvdata->rd_dmactl;
|
||||
else
|
||||
dmactl = drvdata->wr_dmactl;
|
||||
break;
|
||||
case LPASS_DP_RX:
|
||||
dmactl = drvdata->hdmi_rd_dmactl;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
dmactl = drvdata->rxtx_rd_dmactl;
|
||||
break;
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
dmactl = drvdata->rxtx_wr_dmactl;
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
dmactl = drvdata->va_wr_dmactl;
|
||||
break;
|
||||
}
|
||||
|
||||
return dmactl;
|
||||
}
|
||||
|
||||
static int __lpass_get_id(const struct snd_pcm_substream *substream,
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
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);
|
||||
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
|
||||
struct snd_pcm_runtime *rt = substream->runtime;
|
||||
struct lpass_pcm_data *pcm_data = rt->private_data;
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int id;
|
||||
|
||||
switch (cpu_dai->driver->id) {
|
||||
case MI2S_PRIMARY ... MI2S_QUINARY:
|
||||
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
id = pcm_data->dma_ch;
|
||||
else
|
||||
id = pcm_data->dma_ch - v->wrdma_channel_start;
|
||||
break;
|
||||
case LPASS_DP_RX:
|
||||
id = pcm_data->dma_ch;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
id = pcm_data->dma_ch;
|
||||
break;
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
id = pcm_data->dma_ch - v->rxtx_wrdma_channel_start;
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
id = pcm_data->dma_ch - v->va_wrdma_channel_start;
|
||||
break;
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
static struct regmap *__lpass_get_regmap_handle(const struct snd_pcm_substream *substream,
|
||||
struct snd_soc_component *component)
|
||||
{
|
||||
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);
|
||||
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
|
||||
struct regmap *map = NULL;
|
||||
|
||||
switch (cpu_dai->driver->id) {
|
||||
case MI2S_PRIMARY ... MI2S_QUINARY:
|
||||
map = drvdata->lpaif_map;
|
||||
break;
|
||||
case LPASS_DP_RX:
|
||||
map = drvdata->hdmiif_map;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
map = drvdata->rxtx_lpaif_map;
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
map = drvdata->va_lpaif_map;
|
||||
break;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
|
||||
struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
|
@ -196,22 +426,13 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
|
|||
unsigned int channels = params_channels(params);
|
||||
unsigned int regval;
|
||||
struct lpaif_dmactl *dmactl;
|
||||
int id, dir = substream->stream;
|
||||
int id;
|
||||
int bitwidth;
|
||||
int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
|
||||
unsigned int dai_id = cpu_dai->driver->id;
|
||||
|
||||
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
id = pcm_data->dma_ch;
|
||||
if (dai_id == LPASS_DP_RX)
|
||||
dmactl = drvdata->hdmi_rd_dmactl;
|
||||
else
|
||||
dmactl = drvdata->rd_dmactl;
|
||||
|
||||
} else {
|
||||
dmactl = drvdata->wr_dmactl;
|
||||
id = pcm_data->dma_ch - v->wrdma_channel_start;
|
||||
}
|
||||
dmactl = __lpass_get_dmactl_handle(substream, component);
|
||||
id = __lpass_get_id(substream, component);
|
||||
|
||||
bitwidth = snd_pcm_format_width(format);
|
||||
if (bitwidth < 0) {
|
||||
|
@ -266,6 +487,10 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component,
|
|||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
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_TX0:
|
||||
break;
|
||||
default:
|
||||
dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai_id);
|
||||
|
@ -355,10 +580,9 @@ static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component,
|
|||
struct regmap *map;
|
||||
unsigned int dai_id = cpu_dai->driver->id;
|
||||
|
||||
if (dai_id == LPASS_DP_RX)
|
||||
map = drvdata->hdmiif_map;
|
||||
else
|
||||
map = drvdata->lpaif_map;
|
||||
if (is_cdc_dma_port(dai_id))
|
||||
return 0;
|
||||
map = __lpass_get_regmap_handle(substream, component);
|
||||
|
||||
reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream, dai_id);
|
||||
ret = regmap_write(map, reg, 0);
|
||||
|
@ -384,23 +608,11 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component,
|
|||
int ret, id, ch, dir = substream->stream;
|
||||
unsigned int dai_id = cpu_dai->driver->id;
|
||||
|
||||
|
||||
ch = pcm_data->dma_ch;
|
||||
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
if (dai_id == LPASS_DP_RX) {
|
||||
dmactl = drvdata->hdmi_rd_dmactl;
|
||||
map = drvdata->hdmiif_map;
|
||||
} else {
|
||||
dmactl = drvdata->rd_dmactl;
|
||||
map = drvdata->lpaif_map;
|
||||
}
|
||||
|
||||
id = pcm_data->dma_ch;
|
||||
} else {
|
||||
dmactl = drvdata->wr_dmactl;
|
||||
id = pcm_data->dma_ch - v->wrdma_channel_start;
|
||||
map = drvdata->lpaif_map;
|
||||
}
|
||||
dmactl = __lpass_get_dmactl_handle(substream, component);
|
||||
id = __lpass_get_id(substream, component);
|
||||
map = __lpass_get_regmap_handle(substream, component);
|
||||
|
||||
ret = regmap_write(map, LPAIF_DMABASE_REG(v, ch, dir, dai_id),
|
||||
runtime->dma_addr);
|
||||
|
@ -426,6 +638,14 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (is_cdc_dma_port(dai_id)) {
|
||||
ret = regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "error writing fifowm field to dmactl reg: %d, id: %d\n",
|
||||
ret, id);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = regmap_fields_write(dmactl->enable, id, LPAIF_DMACTL_ENABLE_ON);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
|
||||
|
@ -449,26 +669,14 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
|
|||
struct lpaif_dmactl *dmactl;
|
||||
struct regmap *map;
|
||||
int ret, ch, id;
|
||||
int dir = substream->stream;
|
||||
unsigned int reg_irqclr = 0, val_irqclr = 0;
|
||||
unsigned int reg_irqen = 0, val_irqen = 0, val_mask = 0;
|
||||
unsigned int dai_id = cpu_dai->driver->id;
|
||||
|
||||
ch = pcm_data->dma_ch;
|
||||
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
id = pcm_data->dma_ch;
|
||||
if (dai_id == LPASS_DP_RX) {
|
||||
dmactl = drvdata->hdmi_rd_dmactl;
|
||||
map = drvdata->hdmiif_map;
|
||||
} else {
|
||||
dmactl = drvdata->rd_dmactl;
|
||||
map = drvdata->lpaif_map;
|
||||
}
|
||||
} else {
|
||||
dmactl = drvdata->wr_dmactl;
|
||||
id = pcm_data->dma_ch - v->wrdma_channel_start;
|
||||
map = drvdata->lpaif_map;
|
||||
}
|
||||
dmactl = __lpass_get_dmactl_handle(substream, component);
|
||||
id = __lpass_get_id(substream, component);
|
||||
map = __lpass_get_regmap_handle(substream, component);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
|
@ -519,6 +727,35 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
|
|||
val_mask = LPAIF_IRQ_ALL(ch);
|
||||
val_irqen = LPAIF_IRQ_ALL(ch);
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_ON);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev,
|
||||
"error writing to rdmactl reg field: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
reg_irqclr = LPAIF_RXTX_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
|
||||
val_irqclr = LPAIF_IRQ_ALL(ch);
|
||||
|
||||
reg_irqen = LPAIF_RXTX_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
|
||||
val_mask = LPAIF_IRQ_ALL(ch);
|
||||
val_irqen = LPAIF_IRQ_ALL(ch);
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_ON);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev,
|
||||
"error writing to rdmactl reg field: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
reg_irqclr = LPAIF_VA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
|
||||
val_irqclr = LPAIF_IRQ_ALL(ch);
|
||||
|
||||
reg_irqen = LPAIF_VA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
|
||||
val_mask = LPAIF_IRQ_ALL(ch);
|
||||
val_irqen = LPAIF_IRQ_ALL(ch);
|
||||
break;
|
||||
default:
|
||||
dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
|
||||
return -EINVAL;
|
||||
|
@ -570,6 +807,37 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component,
|
|||
val_mask = LPAIF_IRQ_ALL(ch);
|
||||
val_irqen = 0;
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_OFF);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev,
|
||||
"error writing to rdmactl reg field: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg_irqclr = LPAIF_RXTX_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
|
||||
val_irqclr = LPAIF_IRQ_ALL(ch);
|
||||
|
||||
reg_irqen = LPAIF_RXTX_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
|
||||
val_mask = LPAIF_IRQ_ALL(ch);
|
||||
val_irqen = LPAIF_IRQ_ALL(ch);
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
ret = regmap_fields_write(dmactl->dyncclk, id, LPAIF_DMACTL_DYNCLK_OFF);
|
||||
if (ret) {
|
||||
dev_err(soc_runtime->dev,
|
||||
"error writing to rdmactl reg field: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
reg_irqclr = LPAIF_VA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
|
||||
val_irqclr = LPAIF_IRQ_ALL(ch);
|
||||
|
||||
reg_irqen = LPAIF_VA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST);
|
||||
val_mask = LPAIF_IRQ_ALL(ch);
|
||||
val_irqen = LPAIF_IRQ_ALL(ch);
|
||||
break;
|
||||
default:
|
||||
dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
|
||||
return -EINVAL;
|
||||
|
@ -602,11 +870,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
|
|||
struct regmap *map;
|
||||
unsigned int dai_id = cpu_dai->driver->id;
|
||||
|
||||
if (dai_id == LPASS_DP_RX)
|
||||
map = drvdata->hdmiif_map;
|
||||
else
|
||||
map = drvdata->lpaif_map;
|
||||
|
||||
map = __lpass_get_regmap_handle(substream, component);
|
||||
ch = pcm_data->dma_ch;
|
||||
|
||||
ret = regmap_read(map,
|
||||
|
@ -628,6 +892,35 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
|
|||
return bytes_to_frames(substream->runtime, curr_addr - base_addr);
|
||||
}
|
||||
|
||||
static int lpass_platform_cdc_dma_mmap(struct snd_pcm_substream *substream,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
unsigned long size, offset;
|
||||
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
size = vma->vm_end - vma->vm_start;
|
||||
offset = vma->vm_pgoff << PAGE_SHIFT;
|
||||
return io_remap_pfn_range(vma, vma->vm_start,
|
||||
(runtime->dma_addr + offset) >> PAGE_SHIFT,
|
||||
size, vma->vm_page_prot);
|
||||
|
||||
}
|
||||
|
||||
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(substream, vma);
|
||||
|
||||
return snd_pcm_lib_default_mmap(substream, vma);
|
||||
}
|
||||
|
||||
static irqreturn_t lpass_dma_interrupt_handler(
|
||||
struct snd_pcm_substream *substream,
|
||||
struct lpass_data *drvdata,
|
||||
|
@ -660,6 +953,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);
|
||||
val = 0;
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
map = drvdata->va_lpaif_map;
|
||||
reg = LPAIF_VA_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST);
|
||||
val = 0;
|
||||
break;
|
||||
default:
|
||||
dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id);
|
||||
return -EINVAL;
|
||||
|
@ -767,16 +1071,115 @@ 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;
|
||||
irqreturn_t rv;
|
||||
int chan;
|
||||
|
||||
rv = regmap_read(drvdata->rxtx_lpaif_map,
|
||||
LPAIF_RXTX_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
|
||||
|
||||
/* 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;
|
||||
irqreturn_t rv;
|
||||
int chan;
|
||||
|
||||
rv = regmap_read(drvdata->va_lpaif_map,
|
||||
LPAIF_VA_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
|
||||
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
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)memremap(buf->addr, buf->bytes, MEMREMAP_WT);
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
|
||||
component->dev, size);
|
||||
}
|
||||
|
@ -813,6 +1216,31 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
|
|||
return regcache_sync(map);
|
||||
}
|
||||
|
||||
static 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 int dai_id = component->id;
|
||||
int ret = 0;
|
||||
|
||||
void __iomem *dma_buf = (void __iomem *) (rt->dma_area + pos +
|
||||
channel * (rt->dma_bytes / rt->channels));
|
||||
|
||||
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, (void __force *)dma_buf, bytes);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct snd_soc_component_driver lpass_component_driver = {
|
||||
.name = DRV_NAME,
|
||||
|
@ -823,9 +1251,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,
|
||||
|
||||
};
|
||||
|
||||
|
@ -863,6 +1293,58 @@ 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), 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_VA_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 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, 0, "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, 0, "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)
|
||||
|
|
|
@ -0,0 +1,438 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* lpass-sc7180.c -- ALSA SoC platform-machine driver for QTi LPASS
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <dt-bindings/sound/sc7180-lpass.h>
|
||||
|
||||
#include "lpass-lpaif-reg.h"
|
||||
#include "lpass.h"
|
||||
|
||||
static struct snd_soc_dai_driver sc7280_lpass_cpu_dai_driver[] = {
|
||||
{
|
||||
.id = MI2S_PRIMARY,
|
||||
.name = "Primary MI2S",
|
||||
.playback = {
|
||||
.stream_name = "Primary Playback",
|
||||
.formats = SNDRV_PCM_FMTBIT_S16,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.capture = {
|
||||
.stream_name = "Primary Capture",
|
||||
.formats = SNDRV_PCM_FMTBIT_S16 |
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.probe = &asoc_qcom_lpass_cpu_dai_probe,
|
||||
.ops = &asoc_qcom_lpass_cpu_dai_ops,
|
||||
}, {
|
||||
.id = MI2S_SECONDARY,
|
||||
.name = "Secondary MI2S",
|
||||
.playback = {
|
||||
.stream_name = "Secondary MI2S Playback",
|
||||
.formats = SNDRV_PCM_FMTBIT_S16,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.probe = &asoc_qcom_lpass_cpu_dai_probe,
|
||||
.ops = &asoc_qcom_lpass_cpu_dai_ops,
|
||||
}, {
|
||||
.id = LPASS_DP_RX,
|
||||
.name = "Hdmi",
|
||||
.playback = {
|
||||
.stream_name = "DP Playback",
|
||||
.formats = SNDRV_PCM_FMTBIT_S24,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.ops = &asoc_qcom_lpass_hdmi_dai_ops,
|
||||
}, {
|
||||
.id = LPASS_CDC_DMA_RX0,
|
||||
.name = "CDC DMA RX",
|
||||
.playback = {
|
||||
.stream_name = "WCD Playback",
|
||||
.formats = SNDRV_PCM_FMTBIT_S16,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.ops = &asoc_qcom_lpass_cdc_dma_dai_ops,
|
||||
}, {
|
||||
.id = LPASS_CDC_DMA_TX3,
|
||||
.name = "CDC DMA TX",
|
||||
.capture = {
|
||||
.stream_name = "WCD Capture",
|
||||
.formats = SNDRV_PCM_FMTBIT_S16,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 1,
|
||||
},
|
||||
.ops = &asoc_qcom_lpass_cdc_dma_dai_ops,
|
||||
}, {
|
||||
.id = LPASS_CDC_DMA_VA_TX0,
|
||||
.name = "CDC DMA VA",
|
||||
.capture = {
|
||||
.stream_name = "DMIC Capture",
|
||||
.formats = SNDRV_PCM_FMTBIT_S16,
|
||||
.rates = SNDRV_PCM_RATE_48000,
|
||||
.rate_min = 48000,
|
||||
.rate_max = 48000,
|
||||
.channels_min = 2,
|
||||
.channels_max = 4,
|
||||
},
|
||||
.ops = &asoc_qcom_lpass_cdc_dma_dai_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static int sc7280_lpass_alloc_dma_channel(struct lpass_data *drvdata,
|
||||
int direction, unsigned int dai_id)
|
||||
{
|
||||
struct lpass_variant *v = drvdata->variant;
|
||||
int chan = 0;
|
||||
|
||||
switch (dai_id) {
|
||||
case MI2S_PRIMARY ... MI2S_QUINARY:
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
|
||||
v->rdma_channels);
|
||||
|
||||
if (chan >= v->rdma_channels)
|
||||
return -EBUSY;
|
||||
} else {
|
||||
chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
|
||||
v->wrdma_channel_start +
|
||||
v->wrdma_channels,
|
||||
v->wrdma_channel_start);
|
||||
|
||||
if (chan >= v->wrdma_channel_start + v->wrdma_channels)
|
||||
return -EBUSY;
|
||||
}
|
||||
set_bit(chan, &drvdata->dma_ch_bit_map);
|
||||
break;
|
||||
case LPASS_DP_RX:
|
||||
chan = find_first_zero_bit(&drvdata->hdmi_dma_ch_bit_map,
|
||||
v->hdmi_rdma_channels);
|
||||
if (chan >= v->hdmi_rdma_channels)
|
||||
return -EBUSY;
|
||||
set_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
chan = find_first_zero_bit(&drvdata->rxtx_dma_ch_bit_map,
|
||||
v->rxtx_rdma_channels);
|
||||
if (chan >= v->rxtx_rdma_channels)
|
||||
return -EBUSY;
|
||||
break;
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
chan = find_next_zero_bit(&drvdata->rxtx_dma_ch_bit_map,
|
||||
v->rxtx_wrdma_channel_start +
|
||||
v->rxtx_wrdma_channels,
|
||||
v->rxtx_wrdma_channel_start);
|
||||
if (chan >= v->rxtx_wrdma_channel_start + v->rxtx_wrdma_channels)
|
||||
return -EBUSY;
|
||||
set_bit(chan, &drvdata->rxtx_dma_ch_bit_map);
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
chan = find_next_zero_bit(&drvdata->va_dma_ch_bit_map,
|
||||
v->va_wrdma_channel_start +
|
||||
v->va_wrdma_channels,
|
||||
v->va_wrdma_channel_start);
|
||||
if (chan >= v->va_wrdma_channel_start + v->va_wrdma_channels)
|
||||
return -EBUSY;
|
||||
set_bit(chan, &drvdata->va_dma_ch_bit_map);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
static int sc7280_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id)
|
||||
{
|
||||
switch (dai_id) {
|
||||
case MI2S_PRIMARY ... MI2S_QUINARY:
|
||||
clear_bit(chan, &drvdata->dma_ch_bit_map);
|
||||
break;
|
||||
case LPASS_DP_RX:
|
||||
clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map);
|
||||
break;
|
||||
case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
|
||||
case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
|
||||
clear_bit(chan, &drvdata->rxtx_dma_ch_bit_map);
|
||||
break;
|
||||
case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
|
||||
clear_bit(chan, &drvdata->va_dma_ch_bit_map);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc7280_lpass_init(struct platform_device *pdev)
|
||||
{
|
||||
struct lpass_data *drvdata = platform_get_drvdata(pdev);
|
||||
struct lpass_variant *variant = drvdata->variant;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret, i;
|
||||
|
||||
drvdata->clks = devm_kcalloc(dev, variant->num_clks,
|
||||
sizeof(*drvdata->clks), GFP_KERNEL);
|
||||
if (!drvdata->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
drvdata->num_clks = variant->num_clks;
|
||||
|
||||
for (i = 0; i < drvdata->num_clks; i++)
|
||||
drvdata->clks[i].id = variant->clk_name[i];
|
||||
|
||||
ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to get clocks %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks);
|
||||
if (ret) {
|
||||
dev_err(dev, "sc7280 clk_enable failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc7280_lpass_exit(struct platform_device *pdev)
|
||||
{
|
||||
struct lpass_data *drvdata = platform_get_drvdata(pdev);
|
||||
|
||||
clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct lpass_variant sc7280_data = {
|
||||
.i2sctrl_reg_base = 0x1000,
|
||||
.i2sctrl_reg_stride = 0x1000,
|
||||
.i2s_ports = 3,
|
||||
.irq_reg_base = 0x9000,
|
||||
.irq_reg_stride = 0x1000,
|
||||
.irq_ports = 3,
|
||||
.rdma_reg_base = 0xC000,
|
||||
.rdma_reg_stride = 0x1000,
|
||||
.rdma_channels = 5,
|
||||
.rxtx_rdma_reg_base = 0xC000,
|
||||
.rxtx_rdma_reg_stride = 0x1000,
|
||||
.rxtx_rdma_channels = 8,
|
||||
.hdmi_rdma_reg_base = 0x64000,
|
||||
.hdmi_rdma_reg_stride = 0x1000,
|
||||
.hdmi_rdma_channels = 4,
|
||||
.dmactl_audif_start = 1,
|
||||
.wrdma_reg_base = 0x18000,
|
||||
.wrdma_reg_stride = 0x1000,
|
||||
.wrdma_channel_start = 5,
|
||||
.wrdma_channels = 4,
|
||||
.rxtx_irq_reg_base = 0x9000,
|
||||
.rxtx_irq_reg_stride = 0x1000,
|
||||
.rxtx_irq_ports = 3,
|
||||
.rxtx_wrdma_reg_base = 0x18000,
|
||||
.rxtx_wrdma_reg_stride = 0x1000,
|
||||
.rxtx_wrdma_channel_start = 5,
|
||||
.rxtx_wrdma_channels = 6,
|
||||
.va_wrdma_reg_base = 0x18000,
|
||||
.va_wrdma_reg_stride = 0x1000,
|
||||
.va_wrdma_channel_start = 5,
|
||||
.va_wrdma_channels = 3,
|
||||
.va_irq_reg_base = 0x9000,
|
||||
.va_irq_reg_stride = 0x1000,
|
||||
.va_irq_ports = 3,
|
||||
|
||||
.loopback = REG_FIELD_ID(0x1000, 17, 17, 3, 0x1000),
|
||||
.spken = REG_FIELD_ID(0x1000, 16, 16, 3, 0x1000),
|
||||
.spkmode = REG_FIELD_ID(0x1000, 11, 15, 3, 0x1000),
|
||||
.spkmono = REG_FIELD_ID(0x1000, 10, 10, 3, 0x1000),
|
||||
.micen = REG_FIELD_ID(0x1000, 9, 9, 3, 0x1000),
|
||||
.micmode = REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000),
|
||||
.micmono = REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000),
|
||||
.wssrc = REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000),
|
||||
.bitwidth = REG_FIELD_ID(0x1000, 0, 1, 3, 0x1000),
|
||||
|
||||
.rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000),
|
||||
.rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000),
|
||||
.rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000),
|
||||
.rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000),
|
||||
.rdma_fifowm = REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000),
|
||||
.rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000),
|
||||
|
||||
.wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 4, 0x1000),
|
||||
.wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 4, 0x1000),
|
||||
.wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 4, 0x1000),
|
||||
.wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 4, 0x1000),
|
||||
.wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 5, 4, 0x1000),
|
||||
.wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 4, 0x1000),
|
||||
|
||||
.rxtx_rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 7, 0x1000),
|
||||
.rxtx_rdma_fifowm = REG_FIELD_ID(0xC000, 1, 11, 7, 0x1000),
|
||||
.rxtx_rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 7, 0x1000),
|
||||
.rxtx_rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 7, 0x1000),
|
||||
.rxtx_rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 7, 0x1000),
|
||||
.rxtx_rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 7, 0x1000),
|
||||
|
||||
.rxtx_rdma_codec_ch = REG_FIELD_ID(0xC050, 0, 7, 7, 0x1000),
|
||||
.rxtx_rdma_codec_intf = REG_FIELD_ID(0xC050, 16, 19, 7, 0x1000),
|
||||
.rxtx_rdma_codec_fs_delay = REG_FIELD_ID(0xC050, 21, 24, 7, 0x1000),
|
||||
.rxtx_rdma_codec_fs_sel = REG_FIELD_ID(0xC050, 25, 27, 7, 0x1000),
|
||||
.rxtx_rdma_codec_pack = REG_FIELD_ID(0xC050, 29, 29, 5, 0x1000),
|
||||
.rxtx_rdma_codec_enable = REG_FIELD_ID(0xC050, 30, 30, 7, 0x1000),
|
||||
|
||||
.rxtx_wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 5, 0x1000),
|
||||
.rxtx_wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 11, 5, 0x1000),
|
||||
.rxtx_wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 5, 0x1000),
|
||||
.rxtx_wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 5, 0x1000),
|
||||
.rxtx_wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 5, 0x1000),
|
||||
.rxtx_wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 5, 0x1000),
|
||||
|
||||
.rxtx_wrdma_codec_ch = REG_FIELD_ID(0x18050, 0, 7, 5, 0x1000),
|
||||
.rxtx_wrdma_codec_intf = REG_FIELD_ID(0x18050, 16, 19, 5, 0x1000),
|
||||
.rxtx_wrdma_codec_fs_delay = REG_FIELD_ID(0x18050, 21, 24, 5, 0x1000),
|
||||
.rxtx_wrdma_codec_fs_sel = REG_FIELD_ID(0x18050, 25, 27, 5, 0x1000),
|
||||
.rxtx_wrdma_codec_pack = REG_FIELD_ID(0x18050, 29, 29, 5, 0x1000),
|
||||
.rxtx_wrdma_codec_enable = REG_FIELD_ID(0x18050, 30, 30, 5, 0x1000),
|
||||
|
||||
.va_wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 5, 0x1000),
|
||||
.va_wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 11, 5, 0x1000),
|
||||
.va_wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 5, 0x1000),
|
||||
.va_wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 5, 0x1000),
|
||||
.va_wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 5, 0x1000),
|
||||
.va_wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 5, 0x1000),
|
||||
|
||||
.va_wrdma_codec_ch = REG_FIELD_ID(0x18050, 0, 7, 5, 0x1000),
|
||||
.va_wrdma_codec_intf = REG_FIELD_ID(0x18050, 16, 19, 5, 0x1000),
|
||||
.va_wrdma_codec_fs_delay = REG_FIELD_ID(0x18050, 21, 24, 5, 0x1000),
|
||||
.va_wrdma_codec_fs_sel = REG_FIELD_ID(0x18050, 25, 27, 5, 0x1000),
|
||||
.va_wrdma_codec_pack = REG_FIELD_ID(0x18050, 29, 29, 5, 0x1000),
|
||||
.va_wrdma_codec_enable = REG_FIELD_ID(0x18050, 30, 30, 5, 0x1000),
|
||||
|
||||
.hdmi_tx_ctl_addr = 0x1000,
|
||||
.hdmi_legacy_addr = 0x1008,
|
||||
.hdmi_vbit_addr = 0x610c0,
|
||||
.hdmi_ch_lsb_addr = 0x61048,
|
||||
.hdmi_ch_msb_addr = 0x6104c,
|
||||
.ch_stride = 0x8,
|
||||
.hdmi_parity_addr = 0x61034,
|
||||
.hdmi_dmactl_addr = 0x61038,
|
||||
.hdmi_dma_stride = 0x4,
|
||||
.hdmi_DP_addr = 0x610c8,
|
||||
.hdmi_sstream_addr = 0x6101c,
|
||||
.hdmi_irq_reg_base = 0x63000,
|
||||
.hdmi_irq_ports = 1,
|
||||
|
||||
.hdmi_rdma_dyncclk = REG_FIELD_ID(0x64000, 14, 14, 4, 0x1000),
|
||||
.hdmi_rdma_bursten = REG_FIELD_ID(0x64000, 13, 13, 4, 0x1000),
|
||||
.hdmi_rdma_burst8 = REG_FIELD_ID(0x64000, 15, 15, 4, 0x1000),
|
||||
.hdmi_rdma_burst16 = REG_FIELD_ID(0x64000, 16, 16, 4, 0x1000),
|
||||
.hdmi_rdma_dynburst = REG_FIELD_ID(0x64000, 18, 18, 4, 0x1000),
|
||||
.hdmi_rdma_wpscnt = REG_FIELD_ID(0x64000, 10, 12, 4, 0x1000),
|
||||
.hdmi_rdma_fifowm = REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000),
|
||||
.hdmi_rdma_enable = REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000),
|
||||
|
||||
.sstream_en = REG_FIELD(0x6101c, 0, 0),
|
||||
.dma_sel = REG_FIELD(0x6101c, 1, 2),
|
||||
.auto_bbit_en = REG_FIELD(0x6101c, 3, 3),
|
||||
.layout = REG_FIELD(0x6101c, 4, 4),
|
||||
.layout_sp = REG_FIELD(0x6101c, 5, 8),
|
||||
.set_sp_on_en = REG_FIELD(0x6101c, 10, 10),
|
||||
.dp_audio = REG_FIELD(0x6101c, 11, 11),
|
||||
.dp_staffing_en = REG_FIELD(0x6101c, 12, 12),
|
||||
.dp_sp_b_hw_en = REG_FIELD(0x6101c, 13, 13),
|
||||
|
||||
.mute = REG_FIELD(0x610c8, 0, 0),
|
||||
.as_sdp_cc = REG_FIELD(0x610c8, 1, 3),
|
||||
.as_sdp_ct = REG_FIELD(0x610c8, 4, 7),
|
||||
.aif_db4 = REG_FIELD(0x610c8, 8, 15),
|
||||
.frequency = REG_FIELD(0x610c8, 16, 21),
|
||||
.mst_index = REG_FIELD(0x610c8, 28, 29),
|
||||
.dptx_index = REG_FIELD(0x610c8, 30, 31),
|
||||
|
||||
.soft_reset = REG_FIELD(0x1000, 31, 31),
|
||||
.force_reset = REG_FIELD(0x1000, 30, 30),
|
||||
|
||||
.use_hw_chs = REG_FIELD(0x61038, 0, 0),
|
||||
.use_hw_usr = REG_FIELD(0x61038, 1, 1),
|
||||
.hw_chs_sel = REG_FIELD(0x61038, 2, 4),
|
||||
.hw_usr_sel = REG_FIELD(0x61038, 5, 6),
|
||||
|
||||
.replace_vbit = REG_FIELD(0x610c0, 0, 0),
|
||||
.vbit_stream = REG_FIELD(0x610c0, 1, 1),
|
||||
|
||||
.legacy_en = REG_FIELD(0x1008, 0, 0),
|
||||
.calc_en = REG_FIELD(0x61034, 0, 0),
|
||||
.lsb_bits = REG_FIELD(0x61048, 0, 31),
|
||||
.msb_bits = REG_FIELD(0x6104c, 0, 31),
|
||||
|
||||
.clk_name = (const char*[]) {
|
||||
"core_cc_sysnoc_mport_core"
|
||||
},
|
||||
.num_clks = 1,
|
||||
|
||||
.dai_driver = sc7280_lpass_cpu_dai_driver,
|
||||
.num_dai = ARRAY_SIZE(sc7280_lpass_cpu_dai_driver),
|
||||
.dai_osr_clk_names = (const char *[]) {
|
||||
"audio_cc_ext_mclk0",
|
||||
"null"
|
||||
},
|
||||
.dai_bit_clk_names = (const char *[]) {
|
||||
"core_cc_ext_if0_ibit",
|
||||
"core_cc_ext_if1_ibit"
|
||||
},
|
||||
.init = sc7280_lpass_init,
|
||||
.exit = sc7280_lpass_exit,
|
||||
.alloc_dma_channel = sc7280_lpass_alloc_dma_channel,
|
||||
.free_dma_channel = sc7280_lpass_free_dma_channel,
|
||||
};
|
||||
|
||||
static const struct of_device_id sc7280_lpass_cpu_device_id[] = {
|
||||
{.compatible = "qcom,sc7280-lpass-cpu", .data = &sc7280_data},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sc7280_lpass_cpu_device_id);
|
||||
|
||||
static struct platform_driver sc7280_lpass_cpu_platform_driver = {
|
||||
.driver = {
|
||||
.name = "sc7280-lpass-cpu",
|
||||
.of_match_table = of_match_ptr(sc7280_lpass_cpu_device_id),
|
||||
},
|
||||
.probe = asoc_qcom_lpass_cpu_platform_probe,
|
||||
.remove = asoc_qcom_lpass_cpu_platform_remove,
|
||||
.shutdown = asoc_qcom_lpass_cpu_platform_shutdown,
|
||||
};
|
||||
|
||||
module_platform_driver(sc7280_lpass_cpu_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SC7280 LPASS CPU DRIVER");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -20,6 +20,16 @@
|
|||
#define LPASS_MAX_MI2S_PORTS (8)
|
||||
#define LPASS_MAX_DMA_CHANNELS (8)
|
||||
#define LPASS_MAX_HDMI_DMA_CHANNELS (4)
|
||||
#define LPASS_MAX_CDC_DMA_CHANNELS (8)
|
||||
#define LPASS_MAX_VA_CDC_DMA_CHANNELS (8)
|
||||
#define LPASS_CDC_DMA_INTF_ONE_CHANNEL (0x01)
|
||||
#define LPASS_CDC_DMA_INTF_TWO_CHANNEL (0x03)
|
||||
#define LPASS_CDC_DMA_INTF_FOUR_CHANNEL (0x0F)
|
||||
#define LPASS_CDC_DMA_INTF_SIX_CHANNEL (0x3F)
|
||||
#define LPASS_CDC_DMA_INTF_EIGHT_CHANNEL (0xFF)
|
||||
|
||||
#define LPASS_ACTIVE_PDS (4)
|
||||
#define LPASS_PROXY_PDS (8)
|
||||
|
||||
#define QCOM_REGMAP_FIELD_ALLOC(d, m, f, mf) \
|
||||
do { \
|
||||
|
@ -28,6 +38,27 @@
|
|||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct lpaif_i2sctl {
|
||||
struct regmap_field *loopback;
|
||||
struct regmap_field *spken;
|
||||
|
@ -51,6 +82,12 @@ struct lpaif_dmactl {
|
|||
struct regmap_field *burst8;
|
||||
struct regmap_field *burst16;
|
||||
struct regmap_field *dynburst;
|
||||
struct regmap_field *codec_enable;
|
||||
struct regmap_field *codec_pack;
|
||||
struct regmap_field *codec_intf;
|
||||
struct regmap_field *codec_fs_sel;
|
||||
struct regmap_field *codec_channel;
|
||||
struct regmap_field *codec_fs_delay;
|
||||
};
|
||||
|
||||
/* Both the CPU DAI and platform drivers will access this data */
|
||||
|
@ -65,6 +102,11 @@ struct lpass_data {
|
|||
/* MI2S bit clock (derived from system clock by a divider */
|
||||
struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS];
|
||||
|
||||
struct clk *codec_mem0;
|
||||
struct clk *codec_mem1;
|
||||
struct clk *codec_mem2;
|
||||
struct clk *va_mem0;
|
||||
|
||||
/* MI2S SD lines to use for playback/capture */
|
||||
unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS];
|
||||
unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS];
|
||||
|
@ -73,28 +115,43 @@ struct lpass_data {
|
|||
bool mi2s_was_prepared[LPASS_MAX_MI2S_PORTS];
|
||||
|
||||
int hdmi_port_enable;
|
||||
int codec_dma_enable;
|
||||
|
||||
/* low-power audio interface (LPAIF) registers */
|
||||
void __iomem *lpaif;
|
||||
void __iomem *hdmiif;
|
||||
void __iomem *rxtx_lpaif;
|
||||
void __iomem *va_lpaif;
|
||||
|
||||
u32 rxtx_cdc_dma_lpm_buf;
|
||||
u32 va_cdc_dma_lpm_buf;
|
||||
|
||||
/* regmap backed by the low-power audio interface (LPAIF) registers */
|
||||
struct regmap *lpaif_map;
|
||||
struct regmap *hdmiif_map;
|
||||
struct regmap *rxtx_lpaif_map;
|
||||
struct regmap *va_lpaif_map;
|
||||
|
||||
/* interrupts from the low-power audio interface (LPAIF) */
|
||||
int lpaif_irq;
|
||||
int hdmiif_irq;
|
||||
int rxtxif_irq;
|
||||
int vaif_irq;
|
||||
|
||||
/* SOC specific variations in the LPASS IP integration */
|
||||
struct lpass_variant *variant;
|
||||
|
||||
/* bit map to keep track of static channel allocations */
|
||||
unsigned long dma_ch_bit_map;
|
||||
unsigned long hdmi_dma_ch_bit_map;
|
||||
unsigned long rxtx_dma_ch_bit_map;
|
||||
unsigned long va_dma_ch_bit_map;
|
||||
|
||||
/* used it for handling interrupt per dma channel */
|
||||
struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
|
||||
struct snd_pcm_substream *hdmi_substream[LPASS_MAX_HDMI_DMA_CHANNELS];
|
||||
struct snd_pcm_substream *rxtx_substream[LPASS_MAX_CDC_DMA_CHANNELS];
|
||||
struct snd_pcm_substream *va_substream[LPASS_MAX_CDC_DMA_CHANNELS];
|
||||
|
||||
/* SOC specific clock list */
|
||||
struct clk_bulk_data *clks;
|
||||
|
@ -105,6 +162,12 @@ struct lpass_data {
|
|||
struct lpaif_dmactl *rd_dmactl;
|
||||
struct lpaif_dmactl *wr_dmactl;
|
||||
struct lpaif_dmactl *hdmi_rd_dmactl;
|
||||
|
||||
/* Regmap fields of CODEC DMA CTRL registers */
|
||||
struct lpaif_dmactl *rxtx_rd_dmactl;
|
||||
struct lpaif_dmactl *rxtx_wr_dmactl;
|
||||
struct lpaif_dmactl *va_wr_dmactl;
|
||||
|
||||
/* Regmap fields of HDMI_CTRL registers*/
|
||||
struct regmap_field *hdmitx_legacy_en;
|
||||
struct regmap_field *hdmitx_parity_calc_en;
|
||||
|
@ -131,6 +194,24 @@ struct lpass_variant {
|
|||
u32 wrdma_reg_base;
|
||||
u32 wrdma_reg_stride;
|
||||
u32 wrdma_channels;
|
||||
u32 rxtx_irq_reg_base;
|
||||
u32 rxtx_irq_reg_stride;
|
||||
u32 rxtx_irq_ports;
|
||||
u32 rxtx_rdma_reg_base;
|
||||
u32 rxtx_rdma_reg_stride;
|
||||
u32 rxtx_rdma_channels;
|
||||
u32 rxtx_wrdma_reg_base;
|
||||
u32 rxtx_wrdma_reg_stride;
|
||||
u32 rxtx_wrdma_channels;
|
||||
u32 va_irq_reg_base;
|
||||
u32 va_irq_reg_stride;
|
||||
u32 va_irq_ports;
|
||||
u32 va_rdma_reg_base;
|
||||
u32 va_rdma_reg_stride;
|
||||
u32 va_rdma_channels;
|
||||
u32 va_wrdma_reg_base;
|
||||
u32 va_wrdma_reg_stride;
|
||||
u32 va_wrdma_channels;
|
||||
u32 i2sctrl_reg_base;
|
||||
u32 i2sctrl_reg_stride;
|
||||
u32 i2s_ports;
|
||||
|
@ -234,12 +315,66 @@ struct lpass_variant {
|
|||
struct reg_field wrdma_enable;
|
||||
struct reg_field wrdma_dyncclk;
|
||||
|
||||
/* CDC RXTX RD_DMA */
|
||||
struct reg_field rxtx_rdma_intf;
|
||||
struct reg_field rxtx_rdma_bursten;
|
||||
struct reg_field rxtx_rdma_wpscnt;
|
||||
struct reg_field rxtx_rdma_fifowm;
|
||||
struct reg_field rxtx_rdma_enable;
|
||||
struct reg_field rxtx_rdma_dyncclk;
|
||||
struct reg_field rxtx_rdma_burst8;
|
||||
struct reg_field rxtx_rdma_burst16;
|
||||
struct reg_field rxtx_rdma_dynburst;
|
||||
struct reg_field rxtx_rdma_codec_enable;
|
||||
struct reg_field rxtx_rdma_codec_pack;
|
||||
struct reg_field rxtx_rdma_codec_intf;
|
||||
struct reg_field rxtx_rdma_codec_fs_sel;
|
||||
struct reg_field rxtx_rdma_codec_ch;
|
||||
struct reg_field rxtx_rdma_codec_fs_delay;
|
||||
|
||||
/* CDC RXTX WR_DMA */
|
||||
struct reg_field rxtx_wrdma_intf;
|
||||
struct reg_field rxtx_wrdma_bursten;
|
||||
struct reg_field rxtx_wrdma_wpscnt;
|
||||
struct reg_field rxtx_wrdma_fifowm;
|
||||
struct reg_field rxtx_wrdma_enable;
|
||||
struct reg_field rxtx_wrdma_dyncclk;
|
||||
struct reg_field rxtx_wrdma_burst8;
|
||||
struct reg_field rxtx_wrdma_burst16;
|
||||
struct reg_field rxtx_wrdma_dynburst;
|
||||
struct reg_field rxtx_wrdma_codec_enable;
|
||||
struct reg_field rxtx_wrdma_codec_pack;
|
||||
struct reg_field rxtx_wrdma_codec_intf;
|
||||
struct reg_field rxtx_wrdma_codec_fs_sel;
|
||||
struct reg_field rxtx_wrdma_codec_ch;
|
||||
struct reg_field rxtx_wrdma_codec_fs_delay;
|
||||
|
||||
/* CDC VA WR_DMA */
|
||||
struct reg_field va_wrdma_intf;
|
||||
struct reg_field va_wrdma_bursten;
|
||||
struct reg_field va_wrdma_wpscnt;
|
||||
struct reg_field va_wrdma_fifowm;
|
||||
struct reg_field va_wrdma_enable;
|
||||
struct reg_field va_wrdma_dyncclk;
|
||||
struct reg_field va_wrdma_burst8;
|
||||
struct reg_field va_wrdma_burst16;
|
||||
struct reg_field va_wrdma_dynburst;
|
||||
struct reg_field va_wrdma_codec_enable;
|
||||
struct reg_field va_wrdma_codec_pack;
|
||||
struct reg_field va_wrdma_codec_intf;
|
||||
struct reg_field va_wrdma_codec_fs_sel;
|
||||
struct reg_field va_wrdma_codec_ch;
|
||||
struct reg_field va_wrdma_codec_fs_delay;
|
||||
|
||||
/**
|
||||
* on SOCs like APQ8016 the channel control bits start
|
||||
* at different offset to ipq806x
|
||||
**/
|
||||
u32 dmactl_audif_start;
|
||||
u32 wrdma_channel_start;
|
||||
u32 rxtx_wrdma_channel_start;
|
||||
u32 va_wrdma_channel_start;
|
||||
|
||||
/* SOC specific initialization like clocks */
|
||||
int (*init)(struct platform_device *pdev);
|
||||
int (*exit)(struct platform_device *pdev);
|
||||
|
@ -257,6 +392,11 @@ struct lpass_variant {
|
|||
int num_clks;
|
||||
};
|
||||
|
||||
struct lpass_pcm_data {
|
||||
int dma_ch;
|
||||
int i2s_port;
|
||||
};
|
||||
|
||||
/* register the platform driver from the CPU DAI driver */
|
||||
int asoc_qcom_lpass_platform_register(struct platform_device *);
|
||||
int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev);
|
||||
|
@ -266,5 +406,6 @@ int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai);
|
|||
extern const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops;
|
||||
int lpass_cpu_pcm_new(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_soc_dai *dai);
|
||||
extern const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops;
|
||||
|
||||
#endif /* __LPASS_H__ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче