Commit fb5d1152 authored by Srinivas Kandagatla's avatar Srinivas Kandagatla Committed by Mark Brown

ASoC: qcom: add mic support

This patch adds mic support to the lpass driver, most of the driver is
reused as it is, only the register level access is changed depending on
te direction of the stream.
Signed-off-by: default avatarSrinivas Kandagatla <srinivas.kandagatla@linaro.org>
Acked-by: default avatarKenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent dad80614
...@@ -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);
......
...@@ -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,10 +91,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, ...@@ -90,10 +91,15 @@ 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 dir = substream->stream; int ch, dir = substream->stream;
int bitwidth; int bitwidth;
int ret, dma_port = pcm_data->i2s_port + v->dmactl_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) {
dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n", dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n",
...@@ -158,7 +164,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, ...@@ -158,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_DMACTL_REG(v, pcm_data->rdma_ch, dir), 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);
...@@ -175,10 +181,15 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) ...@@ -175,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);
...@@ -194,11 +205,15 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) ...@@ -194,11 +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;
int 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",
...@@ -244,8 +259,12 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, ...@@ -244,8 +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;
int 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:
...@@ -317,8 +336,12 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( ...@@ -317,8 +336,12 @@ 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;
int 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_DMABASE_REG(v, ch, dir), &base_addr); LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
...@@ -446,8 +469,7 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) ...@@ -446,8 +469,7 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
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);
...@@ -460,55 +482,108 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) ...@@ -460,55 +482,108 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
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);
SNDRV_PCM_STREAM_PLAYBACK);
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 = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
soc_runtime->platform->dev, soc_runtime->platform->dev,
size, &substream->dma_buffer); size, &psubstream->dma_buffer);
if (ret) if (ret)
return 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); __func__, ret);
goto err_buf; 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);
goto capture_reg_err;
}
} }
return 0; return 0;
err_buf: capture_reg_err:
snd_dma_free_pages(&substream->dma_buffer); 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);
snd_dma_free_pages(&substream->dma_buffer);
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 = {
......
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