Skip to content

Commit

Permalink
ASoC: qcom: Add ability to handle interrupts per dma channel
Browse files Browse the repository at this point in the history
This patch adds ablity to lpass driver to handle interrupt per dma
channel. Without this patch its not possible to use multipl ports on the
lpass.

Tested-by: Kenneth Westfield <kwestfie@codeaurora.org>
Acked-by: Kenneth Westfield <kwestfie@codeaurora.org>
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
  • Loading branch information
Srinivas Kandagatla authored and Mark Brown committed May 22, 2015
1 parent 0054055 commit 4f629e4
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 35 deletions.
94 changes: 59 additions & 35 deletions sound/soc/qcom/lpass-platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,27 +356,15 @@ static struct snd_pcm_ops lpass_platform_pcm_ops = {
.mmap = lpass_platform_pcmops_mmap,
};

static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
static irqreturn_t lpass_dma_interrupt_handler(
struct snd_pcm_substream *substream,
struct lpass_data *drvdata,
int chan, u32 interrupts)
{
struct snd_pcm_substream *substream = data;
struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
struct lpass_data *drvdata =
snd_soc_platform_get_drvdata(soc_runtime->platform);
struct lpass_variant *v = drvdata->variant;
struct lpass_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(soc_runtime);
unsigned int interrupts;
irqreturn_t ret = IRQ_NONE;
int rv, chan = pcm_data->rdma_ch;

rv = regmap_read(drvdata->lpaif_map,
LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &interrupts);
if (rv) {
dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n",
__func__, rv);
return IRQ_NONE;
}

interrupts &= LPAIF_IRQ_ALL(chan);
int rv;

if (interrupts & LPAIF_IRQ_PER(chan)) {
rv = regmap_write(drvdata->lpaif_map,
Expand Down Expand Up @@ -422,6 +410,35 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
return ret;
}

static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
{
struct lpass_data *drvdata = data;
struct lpass_variant *v = drvdata->variant;
unsigned int irqs;
int rv, chan;

rv = regmap_read(drvdata->lpaif_map,
LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
if (rv) {
pr_err("%s() error reading from irqstat reg: %d\n",
__func__, rv);
return IRQ_NONE;
}

/* Handle per channel interrupts */
for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
rv = lpass_dma_interrupt_handler(
drvdata->substream[chan],
drvdata, chan, irqs);
if (rv != IRQ_HANDLED)
return rv;
}
}

return IRQ_HANDLED;
}

static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *soc_runtime)
{
Expand Down Expand Up @@ -477,6 +494,7 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
if (IS_ERR_VALUE(data->rdma_ch))
return data->rdma_ch;

drvdata->substream[data->rdma_ch] = substream;
data->i2s_port = cpu_dai->driver->id;

snd_soc_pcm_set_drvdata(soc_runtime, data);
Expand All @@ -488,29 +506,12 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
if (ret)
return ret;

ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq,
lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
"lpass-irq-lpaif", substream);
if (ret) {
dev_err(soc_runtime->dev, "%s() irq request failed: %d\n",
__func__, ret);
goto err_buf;
}

/* ensure audio hardware is disabled */
ret = regmap_write(drvdata->lpaif_map,
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
__func__, ret);
return ret;
}
ret = regmap_write(drvdata->lpaif_map,
LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
if (ret) {
dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
__func__, ret);
return ret;
goto err_buf;
}

return 0;
Expand All @@ -530,6 +531,8 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm)
struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
struct lpass_variant *v = drvdata->variant;

drvdata->substream[data->rdma_ch] = NULL;

if (v->free_dma_channel)
v->free_dma_channel(drvdata, data->rdma_ch);

Expand All @@ -545,6 +548,8 @@ static struct snd_soc_platform_driver lpass_platform_driver = {
int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
{
struct lpass_data *drvdata = platform_get_drvdata(pdev);
struct lpass_variant *v = drvdata->variant;
int ret;

drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
if (drvdata->lpaif_irq < 0) {
Expand All @@ -553,6 +558,25 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
return -ENODEV;
}

/* ensure audio hardware is disabled */
ret = regmap_write(drvdata->lpaif_map,
LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
if (ret) {
dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n",
__func__, ret);
return ret;
}

ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
"lpass-irq-lpaif", drvdata);
if (ret) {
dev_err(&pdev->dev, "%s() irq request failed: %d\n",
__func__, ret);
return ret;
}


return devm_snd_soc_register_platform(&pdev->dev,
&lpass_platform_driver);
}
Expand Down
4 changes: 4 additions & 0 deletions sound/soc/qcom/lpass.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000
#define LPASS_MAX_MI2S_PORTS (8)
#define LPASS_MAX_DMA_CHANNELS (8)

/* Both the CPU DAI and platform drivers will access this data */
struct lpass_data {
Expand All @@ -47,6 +48,9 @@ struct lpass_data {

/* SOC specific variations in the LPASS IP integration */
struct lpass_variant *variant;

/* used it for handling interrupt per dma channel */
struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
};

/* Vairant data per each SOC */
Expand Down

0 comments on commit 4f629e4

Please sign in to comment.