Commit f2d4c127 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/pxa' and 'asoc/topic/qcom' into asoc-next

...@@ -52,7 +52,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream, ...@@ -52,7 +52,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
int freq_out, sspa_mclk, sysclk; int freq_out, sspa_mclk, sysclk;
int sspa_div;
if (params_rate(params) > 11025) { if (params_rate(params) > 11025) {
freq_out = params_rate(params) * 512; freq_out = params_rate(params) * 512;
...@@ -63,7 +62,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream, ...@@ -63,7 +62,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
sysclk = params_rate(params) * 512; sysclk = params_rate(params) * 512;
sspa_mclk = params_rate(params) * 64; sspa_mclk = params_rate(params) * 64;
} }
sspa_div = freq_out / sspa_mclk;
snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0); snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0);
snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk); snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk);
......
...@@ -11,21 +11,24 @@ config SND_SOC_LPASS_CPU ...@@ -11,21 +11,24 @@ config SND_SOC_LPASS_CPU
config SND_SOC_LPASS_PLATFORM config SND_SOC_LPASS_PLATFORM
tristate tristate
depends on HAS_DMA
select REGMAP_MMIO select REGMAP_MMIO
config SND_SOC_LPASS_IPQ806X config SND_SOC_LPASS_IPQ806X
tristate tristate
depends on HAS_DMA
select SND_SOC_LPASS_CPU select SND_SOC_LPASS_CPU
select SND_SOC_LPASS_PLATFORM select SND_SOC_LPASS_PLATFORM
config SND_SOC_LPASS_APQ8016 config SND_SOC_LPASS_APQ8016
tristate tristate
depends on HAS_DMA
select SND_SOC_LPASS_CPU select SND_SOC_LPASS_CPU
select SND_SOC_LPASS_PLATFORM select SND_SOC_LPASS_PLATFORM
config SND_SOC_STORM config SND_SOC_STORM
tristate "ASoC I2S support for Storm boards" tristate "ASoC I2S support for Storm boards"
depends on SND_SOC_QCOM depends on SND_SOC_QCOM && HAS_DMA
select SND_SOC_LPASS_IPQ806X select SND_SOC_LPASS_IPQ806X
select SND_SOC_MAX98357A select SND_SOC_MAX98357A
help help
...@@ -34,7 +37,7 @@ config SND_SOC_STORM ...@@ -34,7 +37,7 @@ config SND_SOC_STORM
config SND_SOC_APQ8016_SBC config SND_SOC_APQ8016_SBC
tristate "SoC Audio support for APQ8016 SBC platforms" tristate "SoC Audio support for APQ8016 SBC platforms"
depends on SND_SOC_QCOM depends on SND_SOC_QCOM && HAS_DMA
select SND_SOC_LPASS_APQ8016 select SND_SOC_LPASS_APQ8016
help help
Support for Qualcomm Technologies LPASS audio block in Support for Qualcomm Technologies LPASS audio block in
......
...@@ -30,6 +30,7 @@ struct apq8016_sbc_data { ...@@ -30,6 +30,7 @@ struct apq8016_sbc_data {
struct snd_soc_dai_link dai_link[]; /* dynamically allocated */ struct snd_soc_dai_link dai_link[]; /* dynamically allocated */
}; };
#define MIC_CTRL_TER_WS_SLAVE_SEL BIT(21)
#define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17) #define MIC_CTRL_QUA_WS_SLAVE_SEL_10 BIT(17)
#define MIC_CTRL_TLMM_SCLK_EN BIT(1) #define MIC_CTRL_TLMM_SCLK_EN BIT(1)
#define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16)) #define SPKR_CTL_PRI_WS_SLAVE_SEL_11 (BIT(17) | BIT(16))
...@@ -53,6 +54,12 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd) ...@@ -53,6 +54,12 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
MIC_CTRL_TLMM_SCLK_EN, MIC_CTRL_TLMM_SCLK_EN,
pdata->mic_iomux); pdata->mic_iomux);
break; break;
case MI2S_TERTIARY:
writel(readl(pdata->mic_iomux) | MIC_CTRL_TER_WS_SLAVE_SEL |
MIC_CTRL_TLMM_SCLK_EN,
pdata->mic_iomux);
break;
default: default:
dev_err(card->dev, "unsupported cpu dai configuration\n"); dev_err(card->dev, "unsupported cpu dai configuration\n");
...@@ -126,9 +133,6 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card) ...@@ -126,9 +133,6 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
} }
link->platform_of_node = link->cpu_of_node; link->platform_of_node = link->cpu_of_node;
/* For now we only support playback */
link->playback_only = true;
ret = of_property_read_string(np, "link-name", &link->name); ret = of_property_read_string(np, "link-name", &link->name);
if (ret) { if (ret) {
dev_err(card->dev, "error getting codec dai_link name\n"); dev_err(card->dev, "error getting codec dai_link name\n");
......
...@@ -133,23 +133,36 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { ...@@ -133,23 +133,36 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
}, },
}; };
static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata) static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,
int direction)
{ {
struct lpass_variant *v = drvdata->variant; struct lpass_variant *v = drvdata->variant;
int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map, int chan = 0;
if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
v->rdma_channels); v->rdma_channels);
if (chan >= v->rdma_channels) if (chan >= v->rdma_channels)
return -EBUSY; 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->rdma_ch_bit_map); set_bit(chan, &drvdata->dma_ch_bit_map);
return chan; return chan;
} }
static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
{ {
clear_bit(chan, &drvdata->rdma_ch_bit_map); clear_bit(chan, &drvdata->dma_ch_bit_map);
return 0; return 0;
} }
...@@ -212,7 +225,11 @@ static struct lpass_variant apq8016_data = { ...@@ -212,7 +225,11 @@ static struct lpass_variant apq8016_data = {
.rdma_reg_base = 0x8400, .rdma_reg_base = 0x8400,
.rdma_reg_stride = 0x1000, .rdma_reg_stride = 0x1000,
.rdma_channels = 2, .rdma_channels = 2,
.rdmactl_audif_start = 1, .dmactl_audif_start = 1,
.wrdma_reg_base = 0xB000,
.wrdma_reg_stride = 0x1000,
.wrdma_channel_start = 5,
.wrdma_channels = 2,
.dai_driver = apq8016_lpass_cpu_dai_driver, .dai_driver = apq8016_lpass_cpu_dai_driver,
.num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver), .num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
.init = apq8016_lpass_init, .init = apq8016_lpass_init,
......
...@@ -120,31 +120,60 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, ...@@ -120,31 +120,60 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
return -EINVAL; return -EINVAL;
} }
switch (channels) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
case 1: switch (channels) {
regval |= LPAIF_I2SCTL_SPKMODE_SD0; case 1:
regval |= LPAIF_I2SCTL_SPKMONO_MONO; regval |= LPAIF_I2SCTL_SPKMODE_SD0;
break; regval |= LPAIF_I2SCTL_SPKMONO_MONO;
case 2: break;
regval |= LPAIF_I2SCTL_SPKMODE_SD0; case 2:
regval |= LPAIF_I2SCTL_SPKMONO_STEREO; regval |= LPAIF_I2SCTL_SPKMODE_SD0;
break; regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
case 4: break;
regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; case 4:
regval |= LPAIF_I2SCTL_SPKMONO_STEREO; regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
break; regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
case 6: break;
regval |= LPAIF_I2SCTL_SPKMODE_6CH; case 6:
regval |= LPAIF_I2SCTL_SPKMONO_STEREO; regval |= LPAIF_I2SCTL_SPKMODE_6CH;
break; regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
case 8: break;
regval |= LPAIF_I2SCTL_SPKMODE_8CH; case 8:
regval |= LPAIF_I2SCTL_SPKMONO_STEREO; regval |= LPAIF_I2SCTL_SPKMODE_8CH;
break; regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
default: break;
dev_err(dai->dev, "%s() invalid channels given: %u\n", default:
__func__, channels); dev_err(dai->dev, "%s() invalid channels given: %u\n",
return -EINVAL; __func__, channels);
return -EINVAL;
}
} else {
switch (channels) {
case 1:
regval |= LPAIF_I2SCTL_MICMODE_SD0;
regval |= LPAIF_I2SCTL_MICMONO_MONO;
break;
case 2:
regval |= LPAIF_I2SCTL_MICMODE_SD0;
regval |= LPAIF_I2SCTL_MICMONO_STEREO;
break;
case 4:
regval |= LPAIF_I2SCTL_MICMODE_QUAD01;
regval |= LPAIF_I2SCTL_MICMONO_STEREO;
break;
case 6:
regval |= LPAIF_I2SCTL_MICMODE_6CH;
regval |= LPAIF_I2SCTL_MICMONO_STEREO;
break;
case 8:
regval |= LPAIF_I2SCTL_MICMODE_8CH;
regval |= LPAIF_I2SCTL_MICMONO_STEREO;
break;
default:
dev_err(dai->dev, "%s() invalid channels given: %u\n",
__func__, channels);
return -EINVAL;
}
} }
ret = regmap_write(drvdata->lpaif_map, ret = regmap_write(drvdata->lpaif_map,
...@@ -188,10 +217,19 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, ...@@ -188,10 +217,19 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
{ {
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret; int ret;
unsigned int val, mask;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
val = LPAIF_I2SCTL_SPKEN_ENABLE;
mask = LPAIF_I2SCTL_SPKEN_MASK;
} else {
val = LPAIF_I2SCTL_MICEN_ENABLE;
mask = LPAIF_I2SCTL_MICEN_MASK;
}
ret = regmap_update_bits(drvdata->lpaif_map, ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); mask, val);
if (ret) if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret); __func__, ret);
...@@ -204,16 +242,24 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, ...@@ -204,16 +242,24 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
{ {
struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
int ret = -EINVAL; int ret = -EINVAL;
unsigned int val, mask;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
val = LPAIF_I2SCTL_SPKEN_ENABLE;
mask = LPAIF_I2SCTL_SPKEN_MASK;
} else {
val = LPAIF_I2SCTL_MICEN_ENABLE;
mask = LPAIF_I2SCTL_MICEN_MASK;
}
ret = regmap_update_bits(drvdata->lpaif_map, ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(drvdata->variant, LPAIF_I2SCTL_REG(drvdata->variant,
dai->driver->id), dai->driver->id),
LPAIF_I2SCTL_SPKEN_MASK, mask, val);
LPAIF_I2SCTL_SPKEN_ENABLE);
if (ret) if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret); __func__, ret);
...@@ -221,11 +267,18 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, ...@@ -221,11 +267,18 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
val = LPAIF_I2SCTL_SPKEN_DISABLE;
mask = LPAIF_I2SCTL_SPKEN_MASK;
} else {
val = LPAIF_I2SCTL_MICEN_DISABLE;
mask = LPAIF_I2SCTL_MICEN_MASK;
}
ret = regmap_update_bits(drvdata->lpaif_map, ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_I2SCTL_REG(drvdata->variant, LPAIF_I2SCTL_REG(drvdata->variant,
dai->driver->id), dai->driver->id),
LPAIF_I2SCTL_SPKEN_MASK, mask, val);
LPAIF_I2SCTL_SPKEN_DISABLE);
if (ret) if (ret)
dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
__func__, ret); __func__, ret);
...@@ -294,6 +347,17 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) ...@@ -294,6 +347,17 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
return true; return true;
} }
for (i = 0; i < v->wrdma_channels; ++i) {
if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
return true;
if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
return true;
if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
return true;
if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
return true;
}
return false; return false;
} }
...@@ -327,6 +391,19 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) ...@@ -327,6 +391,19 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
return true; return true;
} }
for (i = 0; i < v->wrdma_channels; ++i) {
if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
return true;
if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
return true;
if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
return true;
if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
return true;
if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
return true;
}
return false; return false;
} }
...@@ -344,6 +421,10 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) ...@@ -344,6 +421,10 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
if (reg == LPAIF_RDMACURR_REG(v, i)) if (reg == LPAIF_RDMACURR_REG(v, i))
return true; return true;
for (i = 0; i < v->wrdma_channels; ++i)
if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
return true;
return false; return false;
} }
...@@ -398,8 +479,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) ...@@ -398,8 +479,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
return PTR_ERR((void const __force *)drvdata->lpaif); return PTR_ERR((void const __force *)drvdata->lpaif);
} }
lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant, lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant,
variant->rdma_channels); variant->wrdma_channels +
variant->wrdma_channel_start);
drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
&lpass_cpu_regmap_config); &lpass_cpu_regmap_config);
......
...@@ -63,9 +63,12 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { ...@@ -63,9 +63,12 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
.ops = &asoc_qcom_lpass_cpu_dai_ops, .ops = &asoc_qcom_lpass_cpu_dai_ops,
}; };
static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata) static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir)
{ {
return IPQ806X_LPAIF_RDMA_CHAN_MI2S; if (dir == SNDRV_PCM_STREAM_PLAYBACK)
return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
else /* Capture currently not implemented */
return -EINVAL;
} }
static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
...@@ -83,6 +86,10 @@ static struct lpass_variant ipq806x_data = { ...@@ -83,6 +86,10 @@ static struct lpass_variant ipq806x_data = {
.rdma_reg_base = 0x6000, .rdma_reg_base = 0x6000,
.rdma_reg_stride = 0x1000, .rdma_reg_stride = 0x1000,
.rdma_channels = 4, .rdma_channels = 4,
.wrdma_reg_base = 0xB000,
.wrdma_reg_stride = 0x1000,
.wrdma_channel_start = 5,
.wrdma_channels = 4,
.dai_driver = &ipq806x_lpass_cpu_dai_driver, .dai_driver = &ipq806x_lpass_cpu_dai_driver,
.num_dai = 1, .num_dai = 1,
.alloc_dma_channel = ipq806x_lpass_alloc_dma_channel, .alloc_dma_channel = ipq806x_lpass_alloc_dma_channel,
......
...@@ -47,6 +47,28 @@ ...@@ -47,6 +47,28 @@
#define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT) #define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT)
#define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT) #define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT)
#define LPAIF_I2SCTL_MICEN_MASK GENMASK(8, 8)
#define LPAIF_I2SCTL_MICEN_SHIFT 8
#define LPAIF_I2SCTL_MICEN_DISABLE (0 << LPAIF_I2SCTL_MICEN_SHIFT)
#define LPAIF_I2SCTL_MICEN_ENABLE (1 << LPAIF_I2SCTL_MICEN_SHIFT)
#define LPAIF_I2SCTL_MICMODE_MASK GENMASK(7, 4)
#define LPAIF_I2SCTL_MICMODE_SHIFT 4
#define LPAIF_I2SCTL_MICMODE_NONE (0 << LPAIF_I2SCTL_MICMODE_SHIFT)
#define LPAIF_I2SCTL_MICMODE_SD0 (1 << LPAIF_I2SCTL_MICMODE_SHIFT)
#define LPAIF_I2SCTL_MICMODE_SD1 (2 << LPAIF_I2SCTL_MICMODE_SHIFT)
#define LPAIF_I2SCTL_MICMODE_SD2 (3 << LPAIF_I2SCTL_MICMODE_SHIFT)
#define LPAIF_I2SCTL_MICMODE_SD3 (4 << LPAIF_I2SCTL_MICMODE_SHIFT)
#define LPAIF_I2SCTL_MICMODE_QUAD01 (5 << LPAIF_I2SCTL_MICMODE_SHIFT)
#define LPAIF_I2SCTL_MICMODE_QUAD23 (6 << LPAIF_I2SCTL_MICMODE_SHIFT)
#define LPAIF_I2SCTL_MICMODE_6CH (7 << LPAIF_I2SCTL_MICMODE_SHIFT)
#define LPAIF_I2SCTL_MICMODE_8CH (8 << LPAIF_I2SCTL_MICMODE_SHIFT)
#define LPAIF_I2SCTL_MIMONO_MASK GENMASK(3, 3)
#define LPAIF_I2SCTL_MICMONO_SHIFT 3
#define LPAIF_I2SCTL_MICMONO_STEREO (0 << LPAIF_I2SCTL_MICMONO_SHIFT)
#define LPAIF_I2SCTL_MICMONO_MONO (1 << LPAIF_I2SCTL_MICMONO_SHIFT)
#define LPAIF_I2SCTL_WSSRC_MASK 0x0004 #define LPAIF_I2SCTL_WSSRC_MASK 0x0004
#define LPAIF_I2SCTL_WSSRC_SHIFT 2 #define LPAIF_I2SCTL_WSSRC_SHIFT 2
#define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT) #define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT)
...@@ -90,37 +112,65 @@ ...@@ -90,37 +112,65 @@
#define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan)) #define LPAIF_RDMAPER_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
#define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan)) #define LPAIF_RDMAPERCNT_REG(v, chan) LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
#define LPAIF_RDMACTL_BURSTEN_MASK 0x800 #define LPAIF_WRDMA_REG_ADDR(v, addr, chan) \
#define LPAIF_RDMACTL_BURSTEN_SHIFT 11 (v->wrdma_reg_base + (addr) + \
#define LPAIF_RDMACTL_BURSTEN_SINGLE (0 << LPAIF_RDMACTL_BURSTEN_SHIFT) v->wrdma_reg_stride * (chan - v->wrdma_channel_start))
#define LPAIF_RDMACTL_BURSTEN_INCR4 (1 << LPAIF_RDMACTL_BURSTEN_SHIFT)
#define LPAIF_WRDMACTL_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x00, (chan))
#define LPAIF_RDMACTL_WPSCNT_MASK 0x700 #define LPAIF_WRDMABASE_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x04, (chan))
#define LPAIF_RDMACTL_WPSCNT_SHIFT 8 #define LPAIF_WRDMABUFF_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x08, (chan))
#define LPAIF_RDMACTL_WPSCNT_ONE (0 << LPAIF_RDMACTL_WPSCNT_SHIFT) #define LPAIF_WRDMACURR_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x0C, (chan))
#define LPAIF_RDMACTL_WPSCNT_TWO (1 << LPAIF_RDMACTL_WPSCNT_SHIFT) #define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan))
#define LPAIF_RDMACTL_WPSCNT_THREE (2 << LPAIF_RDMACTL_WPSCNT_SHIFT) #define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan))
#define LPAIF_RDMACTL_WPSCNT_FOUR (3 << LPAIF_RDMACTL_WPSCNT_SHIFT)
#define LPAIF_RDMACTL_WPSCNT_SIX (5 << LPAIF_RDMACTL_WPSCNT_SHIFT) #define __LPAIF_DMA_REG(v, chan, dir, reg) \
#define LPAIF_RDMACTL_WPSCNT_EIGHT (7 << LPAIF_RDMACTL_WPSCNT_SHIFT) (dir == SNDRV_PCM_STREAM_PLAYBACK) ? \
LPAIF_RDMA##reg##_REG(v, chan) : \
#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0 LPAIF_WRDMA##reg##_REG(v, chan)
#define LPAIF_RDMACTL_AUDINTF_SHIFT 4
#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL)
#define LPAIF_RDMACTL_FIFOWM_MASK 0x00E #define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE)
#define LPAIF_RDMACTL_FIFOWM_SHIFT 1 #define LPAIF_DMABUFF_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BUFF)
#define LPAIF_RDMACTL_FIFOWM_1 (0 << LPAIF_RDMACTL_FIFOWM_SHIFT) #define LPAIF_DMACURR_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CURR)
#define LPAIF_RDMACTL_FIFOWM_2 (1 << LPAIF_RDMACTL_FIFOWM_SHIFT) #define LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER)
#define LPAIF_RDMACTL_FIFOWM_3 (2 << LPAIF_RDMACTL_FIFOWM_SHIFT) #define LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT)
#define LPAIF_RDMACTL_FIFOWM_4 (3 << LPAIF_RDMACTL_FIFOWM_SHIFT)
#define LPAIF_RDMACTL_FIFOWM_5 (4 << LPAIF_RDMACTL_FIFOWM_SHIFT) #define LPAIF_DMACTL_BURSTEN_MASK 0x800
#define LPAIF_RDMACTL_FIFOWM_6 (5 << LPAIF_RDMACTL_FIFOWM_SHIFT) #define LPAIF_DMACTL_BURSTEN_SHIFT 11
#define LPAIF_RDMACTL_FIFOWM_7 (6 << LPAIF_RDMACTL_FIFOWM_SHIFT) #define LPAIF_DMACTL_BURSTEN_SINGLE (0 << LPAIF_DMACTL_BURSTEN_SHIFT)
#define LPAIF_RDMACTL_FIFOWM_8 (7 << LPAIF_RDMACTL_FIFOWM_SHIFT) #define LPAIF_DMACTL_BURSTEN_INCR4 (1 << LPAIF_DMACTL_BURSTEN_SHIFT)
#define LPAIF_RDMACTL_ENABLE_MASK 0x1 #define LPAIF_DMACTL_WPSCNT_MASK 0x700
#define LPAIF_RDMACTL_ENABLE_SHIFT 0 #define LPAIF_DMACTL_WPSCNT_SHIFT 8
#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT) #define LPAIF_DMACTL_WPSCNT_ONE (0 << LPAIF_DMACTL_WPSCNT_SHIFT)
#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT) #define LPAIF_DMACTL_WPSCNT_TWO (1 << LPAIF_DMACTL_WPSCNT_SHIFT)
#define LPAIF_DMACTL_WPSCNT_THREE (2 << LPAIF_DMACTL_WPSCNT_SHIFT)
#define LPAIF_DMACTL_WPSCNT_FOUR (3 << LPAIF_DMACTL_WPSCNT_SHIFT)
#define LPAIF_DMACTL_WPSCNT_SIX (5 << LPAIF_DMACTL_WPSCNT_SHIFT)
#define LPAIF_DMACTL_WPSCNT_EIGHT (7 << LPAIF_DMACTL_WPSCNT_SHIFT)
#define LPAIF_DMACTL_AUDINTF_MASK 0x0F0
#define LPAIF_DMACTL_AUDINTF_SHIFT 4
#define LPAIF_DMACTL_AUDINTF(id) (id << LPAIF_DMACTL_AUDINTF_SHIFT)
#define LPAIF_DMACTL_FIFOWM_MASK 0x00E
#define LPAIF_DMACTL_FIFOWM_SHIFT 1
#define LPAIF_DMACTL_FIFOWM_1 (0 << LPAIF_DMACTL_FIFOWM_SHIFT)
#define LPAIF_DMACTL_FIFOWM_2 (1 << LPAIF_DMACTL_FIFOWM_SHIFT)
#define LPAIF_DMACTL_FIFOWM_3 (2 << LPAIF_DMACTL_FIFOWM_SHIFT)
#define LPAIF_DMACTL_FIFOWM_4 (3 << LPAIF_DMACTL_FIFOWM_SHIFT)
#define LPAIF_DMACTL_FIFOWM_5 (4 << LPAIF_DMACTL_FIFOWM_SHIFT)
#define LPAIF_DMACTL_FIFOWM_6 (5 << LPAIF_DMACTL_FIFOWM_SHIFT)
#define LPAIF_DMACTL_FIFOWM_7 (6 << LPAIF_DMACTL_FIFOWM_SHIFT)
#define LPAIF_DMACTL_FIFOWM_8 (7 << LPAIF_DMACTL_FIFOWM_SHIFT)
#define LPAIF_DMACTL_ENABLE_MASK 0x1
#define LPAIF_DMACTL_ENABLE_SHIFT 0
#define LPAIF_DMACTL_ENABLE_OFF (0 << LPAIF_DMACTL_ENABLE_SHIFT)
#define LPAIF_DMACTL_ENABLE_ON (1 << LPAIF_DMACTL_ENABLE_SHIFT)
#define LPAIF_DMACTL_DYNCLK_MASK BIT(12)
#define LPAIF_DMACTL_DYNCLK_SHIFT 12
#define LPAIF_DMACTL_DYNCLK_OFF (0 << LPAIF_DMACTL_DYNCLK_SHIFT)
#define LPAIF_DMACTL_DYNCLK_ON (1 << LPAIF_DMACTL_DYNCLK_SHIFT)
#endif /* __LPASS_LPAIF_REG_H__ */ #endif /* __LPASS_LPAIF_REG_H__ */
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
struct lpass_pcm_data { struct lpass_pcm_data {
int rdma_ch; int rdma_ch;
int wrdma_ch;
int i2s_port; int i2s_port;
}; };
...@@ -90,8 +91,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, ...@@ -90,8 +91,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
snd_pcm_format_t format = params_format(params); snd_pcm_format_t format = params_format(params);
unsigned int channels = params_channels(params); unsigned int channels = params_channels(params);
unsigned int regval; unsigned int regval;
int ch, dir = substream->stream;
int bitwidth; int bitwidth;
int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start; int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
ch = pcm_data->rdma_ch;
else
ch = pcm_data->wrdma_ch;
bitwidth = snd_pcm_format_width(format); bitwidth = snd_pcm_format_width(format);
if (bitwidth < 0) { if (bitwidth < 0) {
...@@ -100,25 +107,25 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, ...@@ -100,25 +107,25 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
return bitwidth; return bitwidth;
} }
regval = LPAIF_RDMACTL_BURSTEN_INCR4 | regval = LPAIF_DMACTL_BURSTEN_INCR4 |
LPAIF_RDMACTL_AUDINTF(rdma_port) | LPAIF_DMACTL_AUDINTF(dma_port) |
LPAIF_RDMACTL_FIFOWM_8; LPAIF_DMACTL_FIFOWM_8;
switch (bitwidth) { switch (bitwidth) {
case 16: case 16:
switch (channels) { switch (channels) {
case 1: case 1:
case 2: case 2:
regval |= LPAIF_RDMACTL_WPSCNT_ONE; regval |= LPAIF_DMACTL_WPSCNT_ONE;
break; break;
case 4: case 4:
regval |= LPAIF_RDMACTL_WPSCNT_TWO; regval |= LPAIF_DMACTL_WPSCNT_TWO;
break; break;
case 6: case 6:
regval |= LPAIF_RDMACTL_WPSCNT_THREE; regval |= LPAIF_DMACTL_WPSCNT_THREE;
break; break;
case 8: case 8:
regval |= LPAIF_RDMACTL_WPSCNT_FOUR; regval |= LPAIF_DMACTL_WPSCNT_FOUR;
break; break;
default: default:
dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
...@@ -130,19 +137,19 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, ...@@ -130,19 +137,19 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
case 32: case 32:
switch (channels) { switch (channels) {
case 1: case 1:
regval |= LPAIF_RDMACTL_WPSCNT_ONE; regval |= LPAIF_DMACTL_WPSCNT_ONE;
break; break;
case 2: case 2:
regval |= LPAIF_RDMACTL_WPSCNT_TWO; regval |= LPAIF_DMACTL_WPSCNT_TWO;
break; break;
case 4: case 4:
regval |= LPAIF_RDMACTL_WPSCNT_FOUR; regval |= LPAIF_DMACTL_WPSCNT_FOUR;
break; break;
case 6: case 6:
regval |= LPAIF_RDMACTL_WPSCNT_SIX; regval |= LPAIF_DMACTL_WPSCNT_SIX;
break; break;
case 8: case 8:
regval |= LPAIF_RDMACTL_WPSCNT_EIGHT; regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
break; break;
default: default:
dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
...@@ -157,7 +164,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, ...@@ -157,7 +164,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
} }
ret = regmap_write(drvdata->lpaif_map, ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval); LPAIF_DMACTL_REG(v, ch, dir), regval);
if (ret) { if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret); __func__, ret);
...@@ -174,10 +181,15 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) ...@@ -174,10 +181,15 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
struct lpass_data *drvdata = struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform); snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant; struct lpass_variant *v = drvdata->variant;
unsigned int reg;
int ret; int ret;
ret = regmap_write(drvdata->lpaif_map, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0); reg = LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch);
else
reg = LPAIF_WRDMACTL_REG(v, pcm_data->wrdma_ch);
ret = regmap_write(drvdata->lpaif_map, reg, 0);
if (ret) if (ret)
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret); __func__, ret);
...@@ -193,10 +205,15 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) ...@@ -193,10 +205,15 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
struct lpass_data *drvdata = struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform); snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant; struct lpass_variant *v = drvdata->variant;
int ret, ch = pcm_data->rdma_ch; int ret, ch, dir = substream->stream;
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
ch = pcm_data->rdma_ch;
else
ch = pcm_data->wrdma_ch;
ret = regmap_write(drvdata->lpaif_map, ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMABASE_REG(v, ch), LPAIF_DMABASE_REG(v, ch, dir),
runtime->dma_addr); runtime->dma_addr);
if (ret) { if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
...@@ -205,7 +222,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) ...@@ -205,7 +222,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
} }
ret = regmap_write(drvdata->lpaif_map, ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMABUFF_REG(v, ch), LPAIF_DMABUFF_REG(v, ch, dir),
(snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
if (ret) { if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
...@@ -214,7 +231,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) ...@@ -214,7 +231,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
} }
ret = regmap_write(drvdata->lpaif_map, ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMAPER_REG(v, ch), LPAIF_DMAPER_REG(v, ch, dir),
(snd_pcm_lib_period_bytes(substream) >> 2) - 1); (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
if (ret) { if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
...@@ -223,8 +240,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) ...@@ -223,8 +240,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
} }
ret = regmap_update_bits(drvdata->lpaif_map, ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(v, ch), LPAIF_DMACTL_REG(v, ch, dir),
LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
if (ret) { if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret); __func__, ret);
...@@ -242,7 +259,12 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, ...@@ -242,7 +259,12 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
struct lpass_data *drvdata = struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform); snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant; struct lpass_variant *v = drvdata->variant;
int ret, ch = pcm_data->rdma_ch; int ret, ch, dir = substream->stream;
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
ch = pcm_data->rdma_ch;
else
ch = pcm_data->wrdma_ch;
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
...@@ -269,9 +291,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, ...@@ -269,9 +291,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
} }
ret = regmap_update_bits(drvdata->lpaif_map, ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(v, ch), LPAIF_DMACTL_REG(v, ch, dir),
LPAIF_RDMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_MASK,
LPAIF_RDMACTL_ENABLE_ON); LPAIF_DMACTL_ENABLE_ON);
if (ret) { if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret); __func__, ret);
...@@ -282,9 +304,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, ...@@ -282,9 +304,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_update_bits(drvdata->lpaif_map, ret = regmap_update_bits(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(v, ch), LPAIF_DMACTL_REG(v, ch, dir),
LPAIF_RDMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_MASK,
LPAIF_RDMACTL_ENABLE_OFF); LPAIF_DMACTL_ENABLE_OFF);
if (ret) { if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret); __func__, ret);
...@@ -314,10 +336,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( ...@@ -314,10 +336,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
snd_soc_platform_get_drvdata(soc_runtime->platform); snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant; struct lpass_variant *v = drvdata->variant;
unsigned int base_addr, curr_addr; unsigned int base_addr, curr_addr;
int ret, ch = pcm_data->rdma_ch; int ret, ch, dir = substream->stream;
if (dir == SNDRV_PCM_STREAM_PLAYBACK)
ch = pcm_data->rdma_ch;
else
ch = pcm_data->wrdma_ch;
ret = regmap_read(drvdata->lpaif_map, ret = regmap_read(drvdata->lpaif_map,
LPAIF_RDMABASE_REG(v, ch), &base_addr); LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
if (ret) { if (ret) {
dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
__func__, ret); __func__, ret);
...@@ -325,7 +352,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( ...@@ -325,7 +352,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
} }
ret = regmap_read(drvdata->lpaif_map, ret = regmap_read(drvdata->lpaif_map,
LPAIF_RDMACURR_REG(v, ch), &curr_addr); LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
if (ret) { if (ret) {
dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
__func__, ret); __func__, ret);
...@@ -439,101 +466,124 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) ...@@ -439,101 +466,124 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rt)
{
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = rt->platform->dev;
buf->private_data = NULL;
buf->area = dma_alloc_coherent(rt->platform->dev, size, &buf->addr,
GFP_KERNEL);
if (!buf->area) {
dev_err(rt->platform->dev, "%s: Could not allocate DMA buffer\n",
__func__);
return -ENOMEM;
}
buf->bytes = size;
return 0;
}
static void lpass_platform_free_buffer(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rt)
{
struct snd_dma_buffer *buf = &substream->dma_buffer;
if (buf->area) {
dma_free_coherent(rt->dev, buf->bytes, buf->area,
buf->addr);
}
buf->area = NULL;
}
static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
{ {
struct snd_pcm *pcm = soc_runtime->pcm; struct snd_pcm *pcm = soc_runtime->pcm;
struct snd_pcm_substream *substream = struct snd_pcm_substream *psubstream, *csubstream;
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
struct lpass_data *drvdata = struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform); snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant; struct lpass_variant *v = drvdata->variant;
int ret; int ret;
struct lpass_pcm_data *data; struct lpass_pcm_data *data;
size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
if (v->alloc_dma_channel) data->i2s_port = cpu_dai->driver->id;
data->rdma_ch = v->alloc_dma_channel(drvdata); snd_soc_pcm_set_drvdata(soc_runtime, data);
if (IS_ERR_VALUE(data->rdma_ch)) psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
return data->rdma_ch; if (psubstream) {
if (v->alloc_dma_channel)
data->rdma_ch = v->alloc_dma_channel(drvdata,
SNDRV_PCM_STREAM_PLAYBACK);
drvdata->substream[data->rdma_ch] = substream; if (IS_ERR_VALUE(data->rdma_ch))
data->i2s_port = cpu_dai->driver->id; return data->rdma_ch;
snd_soc_pcm_set_drvdata(soc_runtime, data); drvdata->substream[data->rdma_ch] = psubstream;
ret = lpass_platform_alloc_buffer(substream, soc_runtime); ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
if (ret) soc_runtime->platform->dev,
return ret; size, &psubstream->dma_buffer);
if (ret)
goto playback_alloc_err;
ret = regmap_write(drvdata->lpaif_map, ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(v, data->rdma_ch), 0); LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
if (ret) { if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", dev_err(soc_runtime->dev,
"%s() error writing to rdmactl reg: %d\n",
__func__, ret);
goto capture_alloc_err;
}
}
csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
if (csubstream) {
if (v->alloc_dma_channel)
data->wrdma_ch = v->alloc_dma_channel(drvdata,
SNDRV_PCM_STREAM_CAPTURE);
if (IS_ERR_VALUE(data->wrdma_ch))
goto capture_alloc_err;
drvdata->substream[data->wrdma_ch] = csubstream;
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
soc_runtime->platform->dev,
size, &csubstream->dma_buffer);
if (ret)
goto capture_alloc_err;
ret = regmap_write(drvdata->lpaif_map,
LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0);
if (ret) {
dev_err(soc_runtime->dev,
"%s() error writing to wrdmactl reg: %d\n",
__func__, ret); __func__, ret);
goto err_buf; goto capture_reg_err;
}
} }
return 0; return 0;
err_buf: capture_reg_err:
lpass_platform_free_buffer(substream, soc_runtime); if (csubstream)
snd_dma_free_pages(&csubstream->dma_buffer);
capture_alloc_err:
if (psubstream)
snd_dma_free_pages(&psubstream->dma_buffer);
playback_alloc_err:
dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
return ret; return ret;
} }
static void lpass_platform_pcm_free(struct snd_pcm *pcm) static void lpass_platform_pcm_free(struct snd_pcm *pcm)
{ {
struct snd_pcm_substream *substream = struct snd_soc_pcm_runtime *rt;
pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; struct lpass_data *drvdata;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; struct lpass_pcm_data *data;
struct lpass_data *drvdata = struct lpass_variant *v;
snd_soc_platform_get_drvdata(soc_runtime->platform); struct snd_pcm_substream *substream;
struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime); int ch, i;
struct lpass_variant *v = drvdata->variant;
for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
drvdata->substream[data->rdma_ch] = NULL; substream = pcm->streams[i].substream;
if (substream) {
if (v->free_dma_channel) rt = substream->private_data;
v->free_dma_channel(drvdata, data->rdma_ch); data = snd_soc_pcm_get_drvdata(rt);
drvdata = snd_soc_platform_get_drvdata(rt->platform);
lpass_platform_free_buffer(substream, soc_runtime);
ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
? data->rdma_ch
: data->wrdma_ch;
v = drvdata->variant;
drvdata->substream[ch] = NULL;
if (v->free_dma_channel)
v->free_dma_channel(drvdata, ch);
snd_dma_free_pages(&substream->dma_buffer);
substream->dma_buffer.area = NULL;
substream->dma_buffer.addr = 0;
}
}
} }
static struct snd_soc_platform_driver lpass_platform_driver = { static struct snd_soc_platform_driver lpass_platform_driver = {
......
...@@ -50,7 +50,7 @@ struct lpass_data { ...@@ -50,7 +50,7 @@ struct lpass_data {
struct lpass_variant *variant; struct lpass_variant *variant;
/* bit map to keep track of static channel allocations */ /* bit map to keep track of static channel allocations */
unsigned long rdma_ch_bit_map; unsigned long dma_ch_bit_map;
/* used it for handling interrupt per dma channel */ /* used it for handling interrupt per dma channel */
struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS]; struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
...@@ -71,16 +71,20 @@ struct lpass_variant { ...@@ -71,16 +71,20 @@ struct lpass_variant {
u32 rdma_reg_base; u32 rdma_reg_base;
u32 rdma_reg_stride; u32 rdma_reg_stride;
u32 rdma_channels; u32 rdma_channels;
u32 wrdma_reg_base;
u32 wrdma_reg_stride;
u32 wrdma_channels;
/** /**
* on SOCs like APQ8016 the channel control bits start * on SOCs like APQ8016 the channel control bits start
* at different offset to ipq806x * at different offset to ipq806x
**/ **/
u32 rdmactl_audif_start; u32 dmactl_audif_start;
u32 wrdma_channel_start;
/* SOC specific intialization like clocks */ /* SOC specific intialization like clocks */
int (*init)(struct platform_device *pdev); int (*init)(struct platform_device *pdev);
int (*exit)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev);
int (*alloc_dma_channel)(struct lpass_data *data); int (*alloc_dma_channel)(struct lpass_data *data, int direction);
int (*free_dma_channel)(struct lpass_data *data, int ch); int (*free_dma_channel)(struct lpass_data *data, int ch);
/* SOC specific dais */ /* SOC specific dais */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment