ASoC: jz4740: Use the generic dmaengine PCM driver

Now that there is a dmaengine driver for the jz4740 DMA core we can use the
generic dmaengine PCM driver. This allows us to remove the custom jz4740-pcm
code completely.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@linaro.org>
This commit is contained in:
Lars-Peter Clausen 2013-12-03 18:53:03 +01:00 committed by Mark Brown
parent b84c9ce809
commit 0406a40a09
5 changed files with 24 additions and 411 deletions

View File

@ -1,6 +1,7 @@
config SND_JZ4740_SOC config SND_JZ4740_SOC
tristate "SoC Audio for Ingenic JZ4740 SoC" tristate "SoC Audio for Ingenic JZ4740 SoC"
depends on MACH_JZ4740 && SND_SOC depends on MACH_JZ4740 && SND_SOC
select SND_SOC_GENERIC_DMAENGINE_PCM
help help
Say Y or M if you want to add support for codecs attached to Say Y or M if you want to add support for codecs attached to
the JZ4740 I2S interface. You will also need to select the audio the JZ4740 I2S interface. You will also need to select the audio

View File

@ -29,9 +29,11 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/dmaengine_pcm.h>
#include <asm/mach-jz4740/dma.h>
#include "jz4740-i2s.h" #include "jz4740-i2s.h"
#include "jz4740-pcm.h"
#define JZ_REG_AIC_CONF 0x00 #define JZ_REG_AIC_CONF 0x00
#define JZ_REG_AIC_CTRL 0x04 #define JZ_REG_AIC_CTRL 0x04
@ -89,8 +91,8 @@ struct jz4740_i2s {
struct clk *clk_aic; struct clk *clk_aic;
struct clk *clk_i2s; struct clk *clk_i2s;
struct jz4740_pcm_config pcm_config_playback; struct snd_dmaengine_dai_dma_data playback_dma_data;
struct jz4740_pcm_config pcm_config_capture; struct snd_dmaengine_dai_dma_data capture_dma_data;
}; };
static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s,
@ -233,8 +235,6 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{ {
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
enum jz4740_dma_width dma_width;
struct jz4740_pcm_config *pcm_config;
unsigned int sample_size; unsigned int sample_size;
uint32_t ctrl; uint32_t ctrl;
@ -243,11 +243,9 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8: case SNDRV_PCM_FORMAT_S8:
sample_size = 0; sample_size = 0;
dma_width = JZ4740_DMA_WIDTH_8BIT;
break; break;
case SNDRV_PCM_FORMAT_S16: case SNDRV_PCM_FORMAT_S16:
sample_size = 1; sample_size = 1;
dma_width = JZ4740_DMA_WIDTH_16BIT;
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -260,22 +258,13 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO; ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO;
else else
ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO; ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO;
pcm_config = &i2s->pcm_config_playback;
pcm_config->dma_config.dst_width = dma_width;
} else { } else {
ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK; ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK;
ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET; ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
pcm_config = &i2s->pcm_config_capture;
pcm_config->dma_config.src_width = dma_width;
} }
jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
snd_soc_dai_set_dma_data(dai, substream, pcm_config);
return 0; return 0;
} }
@ -342,25 +331,19 @@ static int jz4740_i2s_resume(struct snd_soc_dai *dai)
static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s) static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
{ {
struct jz4740_dma_config *dma_config; struct snd_dmaengine_dai_dma_data *dma_data;
/* Playback */ /* Playback */
dma_config = &i2s->pcm_config_playback.dma_config; dma_data = &i2s->playback_dma_data;
dma_config->src_width = JZ4740_DMA_WIDTH_32BIT; dma_data->maxburst = 16;
dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE; dma_data->slave_id = JZ4740_DMA_TYPE_AIC_TRANSMIT;
dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT; dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
dma_config->mode = JZ4740_DMA_MODE_SINGLE;
i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
/* Capture */ /* Capture */
dma_config = &i2s->pcm_config_capture.dma_config; dma_data = &i2s->capture_dma_data;
dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT; dma_data->maxburst = 16;
dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE; dma_data->slave_id = JZ4740_DMA_TYPE_AIC_RECEIVE;
dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE; dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO;
dma_config->flags = JZ4740_DMA_DST_AUTOINC;
dma_config->mode = JZ4740_DMA_MODE_SINGLE;
i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO;
} }
static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
@ -371,6 +354,8 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
clk_prepare_enable(i2s->clk_aic); clk_prepare_enable(i2s->clk_aic);
jz4740_i2c_init_pcm_config(i2s); jz4740_i2c_init_pcm_config(i2s);
snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data,
&i2s->capture_dma_data);
conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) |
(8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) |
@ -456,8 +441,13 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, i2s); platform_set_drvdata(pdev, i2s);
return devm_snd_soc_register_component(&pdev->dev, ret = devm_snd_soc_register_component(&pdev->dev,
&jz4740_i2s_component, &jz4740_i2s_dai, 1); &jz4740_i2s_component, &jz4740_i2s_dai, 1);
if (ret)
return ret;
return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
SND_DMAENGINE_PCM_FLAG_COMPAT);
} }
static struct platform_driver jz4740_i2s_driver = { static struct platform_driver jz4740_i2s_driver = {

View File

@ -1,358 +0,0 @@
/*
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <asm/mach-jz4740/dma.h>
#include "jz4740-pcm.h"
struct jz4740_runtime_data {
unsigned long dma_period;
dma_addr_t dma_start;
dma_addr_t dma_pos;
dma_addr_t dma_end;
struct jz4740_dma_chan *dma;
dma_addr_t fifo_addr;
};
/* identify hardware playback capabilities */
static const struct snd_pcm_hardware jz4740_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
.rates = SNDRV_PCM_RATE_8000_48000,
.channels_min = 1,
.channels_max = 2,
.period_bytes_min = 16,
.period_bytes_max = 2 * PAGE_SIZE,
.periods_min = 2,
.periods_max = 128,
.buffer_bytes_max = 128 * 2 * PAGE_SIZE,
.fifo_size = 32,
};
static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd,
struct snd_pcm_substream *substream)
{
unsigned long count;
if (prtd->dma_pos == prtd->dma_end)
prtd->dma_pos = prtd->dma_start;
if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
count = prtd->dma_end - prtd->dma_pos;
else
count = prtd->dma_period;
jz4740_dma_disable(prtd->dma);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos);
jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr);
} else {
jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr);
jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos);
}
jz4740_dma_set_transfer_count(prtd->dma, count);
prtd->dma_pos += count;
jz4740_dma_enable(prtd->dma);
}
static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
void *dev_id)
{
struct snd_pcm_substream *substream = dev_id;
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
snd_pcm_period_elapsed(substream);
jz4740_pcm_start_transfer(prtd, substream);
}
static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct jz4740_pcm_config *config;
config = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
if (!config)
return 0;
if (!prtd->dma) {
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
prtd->dma = jz4740_dma_request(substream, "PCM Capture");
else
prtd->dma = jz4740_dma_request(substream, "PCM Playback");
}
if (!prtd->dma)
return -EBUSY;
jz4740_dma_configure(prtd->dma, &config->dma_config);
prtd->fifo_addr = config->fifo_addr;
jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done);
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
runtime->dma_bytes = params_buffer_bytes(params);
prtd->dma_period = params_period_bytes(params);
prtd->dma_start = runtime->dma_addr;
prtd->dma_pos = prtd->dma_start;
prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
return 0;
}
static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream)
{
struct jz4740_runtime_data *prtd = substream->runtime->private_data;
snd_pcm_set_runtime_buffer(substream, NULL);
if (prtd->dma) {
jz4740_dma_free(prtd->dma);
prtd->dma = NULL;
}
return 0;
}
static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
{
struct jz4740_runtime_data *prtd = substream->runtime->private_data;
if (!prtd->dma)
return -EBUSY;
prtd->dma_pos = prtd->dma_start;
return 0;
}
static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
jz4740_pcm_start_transfer(prtd, substream);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
jz4740_dma_disable(prtd->dma);
break;
default:
break;
}
return 0;
}
static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
unsigned long byte_offset;
snd_pcm_uframes_t offset;
struct jz4740_dma_chan *dma = prtd->dma;
/* prtd->dma_pos points to the end of the current transfer. So by
* subtracting prdt->dma_start we get the offset to the end of the
* current period in bytes. By subtracting the residue of the transfer
* we get the current offset in bytes. */
byte_offset = prtd->dma_pos - prtd->dma_start;
byte_offset -= jz4740_dma_get_residue(dma);
offset = bytes_to_frames(runtime, byte_offset);
if (offset >= runtime->buffer_size)
offset = 0;
return offset;
}
static int jz4740_pcm_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd;
prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
if (prtd == NULL)
return -ENOMEM;
snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
runtime->private_data = prtd;
return 0;
}
static int jz4740_pcm_close(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
kfree(prtd);
return 0;
}
static int jz4740_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
return remap_pfn_range(vma, vma->vm_start,
substream->dma_buffer.addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
static struct snd_pcm_ops jz4740_pcm_ops = {
.open = jz4740_pcm_open,
.close = jz4740_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = jz4740_pcm_hw_params,
.hw_free = jz4740_pcm_hw_free,
.prepare = jz4740_pcm_prepare,
.trigger = jz4740_pcm_trigger,
.pointer = jz4740_pcm_pointer,
.mmap = jz4740_pcm_mmap,
};
static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
{
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
struct snd_dma_buffer *buf = &substream->dma_buffer;
size_t size = jz4740_pcm_hardware.buffer_bytes_max;
buf->dev.type = SNDRV_DMA_TYPE_DEV;
buf->dev.dev = pcm->card->dev;
buf->private_data = NULL;
buf->area = dma_alloc_noncoherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
if (!buf->area)
return -ENOMEM;
buf->bytes = size;
return 0;
}
static void jz4740_pcm_free(struct snd_pcm *pcm)
{
struct snd_pcm_substream *substream;
struct snd_dma_buffer *buf;
int stream;
for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) {
substream = pcm->streams[stream].substream;
if (!substream)
continue;
buf = &substream->dma_buffer;
if (!buf->area)
continue;
dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area,
buf->addr);
buf->area = NULL;
}
}
static int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_pcm *pcm = rtd->pcm;
int ret;
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
ret = jz4740_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_PLAYBACK);
if (ret)
goto err;
}
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
ret = jz4740_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto err;
}
err:
return ret;
}
static struct snd_soc_platform_driver jz4740_soc_platform = {
.ops = &jz4740_pcm_ops,
.pcm_new = jz4740_pcm_new,
.pcm_free = jz4740_pcm_free,
};
static int jz4740_pcm_probe(struct platform_device *pdev)
{
return snd_soc_register_platform(&pdev->dev, &jz4740_soc_platform);
}
static int jz4740_pcm_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&pdev->dev);
return 0;
}
static struct platform_driver jz4740_pcm_driver = {
.probe = jz4740_pcm_probe,
.remove = jz4740_pcm_remove,
.driver = {
.name = "jz4740-pcm-audio",
.owner = THIS_MODULE,
},
};
module_platform_driver(jz4740_pcm_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Ingenic SoC JZ4740 PCM driver");
MODULE_LICENSE("GPL");

View File

@ -1,20 +0,0 @@
/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _JZ4740_PCM_H
#define _JZ4740_PCM_H
#include <linux/dma-mapping.h>
#include <asm/mach-jz4740/dma.h>
struct jz4740_pcm_config {
struct jz4740_dma_config dma_config;
phys_addr_t fifo_addr;
};
#endif

View File

@ -73,7 +73,7 @@ static struct snd_soc_dai_link qi_lb60_dai = {
.name = "jz4740", .name = "jz4740",
.stream_name = "jz4740", .stream_name = "jz4740",
.cpu_dai_name = "jz4740-i2s", .cpu_dai_name = "jz4740-i2s",
.platform_name = "jz4740-pcm-audio", .platform_name = "jz4740-i2s",
.codec_dai_name = "jz4740-hifi", .codec_dai_name = "jz4740-hifi",
.codec_name = "jz4740-codec", .codec_name = "jz4740-codec",
.init = qi_lb60_codec_init, .init = qi_lb60_codec_init,