[alsa-devel] ASoC DaVinci voicecodec based on copyfromuser
In dm365 cpu it is not possible to run CQ93VC Voice Codec together with McBSP I2S peripheral, because they share the same dma event. With this patchset it is possible to run the CQ93VC Voice Codec using copyfromuser.
From: Davide Bonfanti davide.bonfanti@bticino.it
If soc_pcm_ops is statically allocated, more than one call to soc_new_pcm cause operations overwrite. The problem is overcome usyng dynamic allocation.
Signed-off-by: Davide Bonfanti davide.bonfanti@bticino.it Signed-off-by: Raffaele Recalcati raffaele.recalcati@bticino.it --- sound/soc/soc-core.c | 38 +++++++++++++++++--------------------- 1 files changed, 17 insertions(+), 21 deletions(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index ad7f952..6500686 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -801,17 +801,6 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } return 0; } - -/* ASoC PCM operations */ -static struct snd_pcm_ops soc_pcm_ops = { - .open = soc_pcm_open, - .close = soc_codec_close, - .hw_params = soc_pcm_hw_params, - .hw_free = soc_pcm_hw_free, - .prepare = soc_pcm_prepare, - .trigger = soc_pcm_trigger, -}; - #ifdef CONFIG_PM /* powers down audio subsystem for suspend */ static int soc_suspend(struct device *dev) @@ -1306,6 +1295,7 @@ static int soc_new_pcm(struct snd_soc_device *socdev, struct snd_pcm *pcm; char new_name[64]; int ret = 0, playback = 0, capture = 0; + struct snd_pcm_ops *soc_pcm_ops;
rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL); if (rtd == NULL) @@ -1335,19 +1325,25 @@ static int soc_new_pcm(struct snd_soc_device *socdev,
dai_link->pcm = pcm; pcm->private_data = rtd; - soc_pcm_ops.mmap = platform->pcm_ops->mmap; - soc_pcm_ops.pointer = platform->pcm_ops->pointer; - soc_pcm_ops.ioctl = platform->pcm_ops->ioctl; - soc_pcm_ops.copy = platform->pcm_ops->copy; - soc_pcm_ops.silence = platform->pcm_ops->silence; - soc_pcm_ops.ack = platform->pcm_ops->ack; - soc_pcm_ops.page = platform->pcm_ops->page; + soc_pcm_ops = kmalloc(sizeof(struct snd_pcm_ops), GFP_KERNEL); + soc_pcm_ops->open = soc_pcm_open; + soc_pcm_ops->close = soc_codec_close; + soc_pcm_ops->hw_params = soc_pcm_hw_params; + soc_pcm_ops->hw_free = soc_pcm_hw_free; + soc_pcm_ops->prepare = soc_pcm_prepare; + soc_pcm_ops->trigger = soc_pcm_trigger; + soc_pcm_ops->mmap = platform->pcm_ops->mmap; + soc_pcm_ops->pointer = platform->pcm_ops->pointer; + soc_pcm_ops->ioctl = platform->pcm_ops->ioctl; + soc_pcm_ops->copy = platform->pcm_ops->copy; + soc_pcm_ops->silence = platform->pcm_ops->silence; + soc_pcm_ops->ack = platform->pcm_ops->ack; + soc_pcm_ops->page = platform->pcm_ops->page;
if (playback) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops); - + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops); if (capture) - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);
ret = platform->pcm_new(codec->card, codec_dai, pcm); if (ret < 0) {
On Fri, Jul 16, 2010 at 04:46:57PM +0200, Raffaele Recalcati wrote:
If soc_pcm_ops is statically allocated, more than one call to soc_new_pcm cause operations overwrite. The problem is overcome usyng dynamic allocation.
As I said previously you're looking for Liam's multi-component work here rather than this (or at least should be basing your work off his).
2010/7/16 Mark Brown broonie@opensource.wolfsonmicro.com
On Fri, Jul 16, 2010 at 04:46:57PM +0200, Raffaele Recalcati wrote:
If soc_pcm_ops is statically allocated, more than one call to
soc_new_pcm
cause operations overwrite. The problem is overcome usyng dynamic allocation.
As I said previously you're looking for Liam's multi-component work here rather than this (or at least should be basing your work off his).
ok. For us it is better to have also more feedback from you about the other two patches. So the next patchset we'll submit shall be more complete.
Thx
On Fri, Jul 16, 2010 at 05:25:19PM +0200, Raffaele Recalcati wrote:
For us it is better to have also more feedback from you about the other two patches. So the next patchset we'll submit shall be more complete.
Sure - those are bigger and take longer to read, should get to them by the end of the weekend at least.
From: Davide Bonfanti davide.bonfanti@bticino.it
This driver implements a pcm interface without the use of a DMA but with a copy_from_user. There's a buffer in the driver that is filled with davinci_pcm_copy. When pcm is running, a TIMER interrupt is activated in order to fill HW FIFO. BUG: It happens sometimes that the peripheral stops working so there's a trap.
This patch has been developed against the http://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git git tree and tested on bmx board.
Signed-off-by: Davide Bonfanti davide.bonfanti@bticino.it Signed-off-by: Raffaele Recalcati raffaele.recalcati@bticino.it --- sound/soc/davinci/Makefile | 1 + sound/soc/davinci/davinci-pcm-copy.h | 52 +++++ sound/soc/davinci/davinci-pcm-copyfromuser.c | 278 ++++++++++++++++++++++++++ sound/soc/davinci/davinci-pcm.h | 1 + 4 files changed, 332 insertions(+), 0 deletions(-) create mode 100644 sound/soc/davinci/davinci-pcm-copy.h create mode 100644 sound/soc/davinci/davinci-pcm-copyfromuser.c
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile index a93679d..7d6a9a1 100644 --- a/sound/soc/davinci/Makefile +++ b/sound/soc/davinci/Makefile @@ -1,5 +1,6 @@ # DAVINCI Platform Support snd-soc-davinci-objs := davinci-pcm.o +snd-soc-davinci-objs += davinci-pcm-copyfromuser.o snd-soc-davinci-i2s-objs := davinci-i2s.o snd-soc-davinci-mcasp-objs:= davinci-mcasp.o snd-soc-davinci-vcif-objs:= davinci-vcif.o diff --git a/sound/soc/davinci/davinci-pcm-copy.h b/sound/soc/davinci/davinci-pcm-copy.h new file mode 100644 index 0000000..c143fb3 --- /dev/null +++ b/sound/soc/davinci/davinci-pcm-copy.h @@ -0,0 +1,52 @@ +/* + * + * Copyright (C) 2010 Bticino S.p.a + * Author: Davide Bonfanti davide.bonfanti@bticino.it + * + * Contributors: + * Raffaele Recalcati raffaele.recalcati@bticino.it + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _DAVINCI_PCM_COPY_H +#define _DAVINCI_PCM_COPY_H + +struct davinci_pcm_copy_ops { + /* called to perform initial settings - optional */ + void (*init)(void); + /* called to enable codec - mandatory */ + void (*enable)(void); + /* called to push one data into hw_fifo - mandatory */ + void (*write)(u16); + /* called to wait hw_fifo is ready for more data - optional */ + void (*wait_fifo_ready)(void); + /* called to get hw_fifo size - mandatory */ + int (*get_fifo_size)(void); + /* called to get number of samples already into the hw_fifo */ + int (*get_fifo_status)(void); +}; + +struct davinci_pcm_copy_platform_data { + /* length required for samples buffer in bytes */ + int buffer_size; + /* minimum time in ps between an interrupt and another */ + int min_interrupt_interval_ps; + /* + * margin between next interrupt occurrency and hw_fifo end_of_play + * using loaded samples + */ + int interrupt_margin_ps; + /* codec operations as explained above */ + struct davinci_pcm_copy_ops *ops; +}; + +#endif /* _DAVINCI_PCM_COPY_H */ diff --git a/sound/soc/davinci/davinci-pcm-copyfromuser.c b/sound/soc/davinci/davinci-pcm-copyfromuser.c new file mode 100644 index 0000000..7494afe --- /dev/null +++ b/sound/soc/davinci/davinci-pcm-copyfromuser.c @@ -0,0 +1,278 @@ +/* + * + * Copyright (C) 2010 Bticino S.p.a + * Author: Davide Bonfanti davide.bonfanti@bticino.it + * + * Contributors: + * Raffaele Recalcati raffaele.recalcati@bticino.it + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/hrtimer.h> +#include <linux/clk.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <mach/gpio.h> + +#include "davinci-pcm.h" +#include "davinci-pcm-copy.h" + +#define DEF_BUF_SIZE 2048 +#define DEF_MIN_INT 500000 +#define DEF_INT_MARGIN 500000 + +int pointer_sub; +int hw_fifo_size; +u16 *local_buffer; +static struct hrtimer hrtimer; +struct snd_pcm_substream *substream_loc; +int ns_for_interrupt = 1500000; +int min_interrupt_ps; +int interrupt_margin; + +struct davinci_pcm_copy_ops *ops; + +static struct snd_pcm_hardware pcm_hardware_playback = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000), + .rate_min = 8000, + .rate_max = 8000, + .channels_min = 1, + .channels_max = 1, + .period_bytes_min = 512, + .period_bytes_max = 512, + .fifo_size = 0, +}; + +/* + * How this driver works... + * + * This driver implements a pcm interface without the use of a DMA but with + * a copy_from_user. + * There's a buffer of {platform_data->buffer_size} bytes in the driver + * that is filled with davinci_pcm_copy. + * When pcm is running, a TIMER interrupt is activated in order to fill + * HW FIFO. + * It happens that the peripheral stop working so there's a trap... + */ + +static snd_pcm_uframes_t +davinci_pcm_pointer(struct snd_pcm_substream *substream) +{ + return pointer_sub; +} + +static int davinci_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hardware *ppcm; + static ktime_t wakeups_per_second; + int ret = 0; + + pointer_sub = 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + ppcm = &pcm_hardware_playback; + else + return -ENODEV; + + snd_soc_set_runtime_hwparams(substream, ppcm); + /* ensure that buffer size is a multiple of period size */ + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) + return ret; + + hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL); + ops->enable(); + return 0; +} + +static int davinci_pcm_close(struct snd_pcm_substream *substream) +{ + hrtimer_cancel(&hrtimer); + return 0; +} + +static int davinci_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + long rate; + + rate = hw_params->rate_num * hw_params->rate_den; + rate = hw_params->msbits * (1000000000 / rate); + + /* let's take some margin of */ + rate -= interrupt_margin; + + /* assure the interrupt doesn't occupy too many resources */ + ns_for_interrupt = rate > min_interrupt_ps ? rate : min_interrupt_ps; + + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int davinci_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int davinci_pcm_copy(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + if (copy_from_user(local_buffer + hwoff, buf, + frames_to_bytes(runtime, frames))) { + printk(KERN_ERR "ERROR COPY_FROM_USER\n"); + return -EFAULT; + } + return 0; +} + +static struct snd_pcm_ops davinci_pcm_ops = { + .open = davinci_pcm_open, + .close = davinci_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = davinci_pcm_hw_params, + .hw_free = davinci_pcm_hw_free, + .pointer = davinci_pcm_pointer, + .copy = davinci_pcm_copy, +}; + +static u64 davinci_pcm_dmamask = 0xffffffff; + +static enum hrtimer_restart dm_vc_irq(struct hrtimer *handle) +{ + int fifo, diff, per_size, buf_size; + static int last_ptr; + gpio_set_value(69, 1); + + if (substream_loc->runtime && substream_loc->runtime->status && + snd_pcm_running(substream_loc)) { + fifo = ops->get_fifo_status(); + if (fifo >= (hw_fifo_size - 1)) + ops->enable(); + + buf_size = substream_loc->runtime->buffer_size; + per_size = substream_loc->runtime->period_size; + for (; fifo < hw_fifo_size; fifo++) { + ops->write(local_buffer[pointer_sub++]); + pointer_sub %= buf_size; + if (ops->wait_fifo_ready) + ops->wait_fifo_ready(); + } + if (last_ptr >= pointer_sub) + diff = buf_size + pointer_sub - last_ptr; + else + diff = pointer_sub - last_ptr; + if (diff >= per_size) { + snd_pcm_period_elapsed(substream_loc); + last_ptr += per_size; + if (last_ptr >= buf_size) + last_ptr -= buf_size; + } + } else + last_ptr = 0; + hrtimer_add_expires_ns(&hrtimer, ns_for_interrupt); + gpio_set_value(69, 0); + return HRTIMER_RESTART; +} + +static int davinci_pcm_new(struct snd_card *card, + struct snd_soc_dai *dai, struct snd_pcm *pcm) +{ + struct snd_dma_buffer *buf; + struct snd_pcm_substream *substream; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &davinci_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = 0xffffffff; + + gpio_direction_output(69, 0); + + if (dai->playback.channels_min) { + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + buf = &substream->dma_buffer; + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->bytes = pcm_hardware_playback.buffer_bytes_max; + substream_loc = substream; + } + hrtimer_init(&hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hrtimer.function = dm_vc_irq; + + return 0; +} + +int davinci_pcm_probe(struct platform_device *pdev) +{ + struct snd_soc_device *dev = platform_get_drvdata(pdev); + struct davinci_pcm_copy_platform_data *data; + + data = dev->codec_data; + ops = data->ops; + + if ((!ops) || (!ops->enable) || (!ops->write) || + (!ops->get_fifo_size) || (!ops->get_fifo_status)) + return -EINVAL; + min_interrupt_ps = data->min_interrupt_interval_ps ? + data->min_interrupt_interval_ps : DEF_MIN_INT; + interrupt_margin = data->interrupt_margin_ps ? + data->interrupt_margin_ps : DEF_INT_MARGIN; + pcm_hardware_playback.buffer_bytes_max = data->buffer_size ? + data->buffer_size : DEF_BUF_SIZE; + pcm_hardware_playback.periods_min = data->buffer_size / 512; + pcm_hardware_playback.periods_max = data->buffer_size / 512; + local_buffer = kmalloc(data->buffer_size, GFP_KERNEL); + if (ops->init) + ops->init(); + hw_fifo_size = ops->get_fifo_size(); + + return 0; +} + +struct snd_soc_platform davinci_soc_platform_copy = { + .name = "davinci-audio-copy", + .pcm_ops = &davinci_pcm_ops, + .pcm_new = davinci_pcm_new, + .probe = davinci_pcm_probe, +}; +EXPORT_SYMBOL_GPL(davinci_soc_platform_copy); + +static int __init davinci_soc_copy_platform_init(void) +{ + return snd_soc_register_platform(&davinci_soc_platform_copy); +} +module_init(davinci_soc_copy_platform_init); + +static void __exit davinci_soc_copy_platform_exit(void) +{ + snd_soc_unregister_platform(&davinci_soc_platform_copy); +} +module_exit(davinci_soc_copy_platform_exit); + +MODULE_AUTHOR("bticino s.p.a."); +MODULE_DESCRIPTION("TI DAVINCI PCM copy_from_user module"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index 0764944..cb7c2aa 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -29,5 +29,6 @@ struct davinci_pcm_dma_params {
extern struct snd_soc_platform davinci_soc_platform; +extern struct snd_soc_platform davinci_soc_platform_copy;
#endif
On Fri, Jul 16, 2010 at 04:46:58PM +0200, Raffaele Recalcati wrote:
From: Davide Bonfanti davide.bonfanti@bticino.it
This driver implements a pcm interface without the use of a DMA but with a copy_from_user. There's a buffer in the driver that is filled with davinci_pcm_copy. When pcm is running, a TIMER interrupt is activated in order to fill HW FIFO. BUG: It happens sometimes that the peripheral stops working so there's a trap.
Looking at this code the main thing that jumps out at me is that it doesn't look at all DaVinci specific - all the interaction with the hardware is hidden behind the ops structure you've defined, though the ops structure doesn't define things like the maximum number of channels and sample rate which I'd expect it to. This suggests that either the patch shouldn't be DaVinci specific or the splitting out of the ops structure isn't adding anything.
+int pointer_sub;
These should all be static.
+int hw_fifo_size; +u16 *local_buffer; +static struct hrtimer hrtimer; +struct snd_pcm_substream *substream_loc; +int ns_for_interrupt = 1500000;
Magic number?
gpio_set_value(69, 0);
Magic number again, and nothing requests this GPIO either. Looks like another thing for the ops structure.
From: Davide Bonfanti davide.bonfanti@bticino.it
The driver uses the pcm implementation without the use of DMA in order to support Voicecodec (cq93vc).
Signed-off-by: Davide Bonfanti davide.bonfanti@bticino.it Signed-off-by: Raffaele Recalcati raffaele.recalcati@bticino.it --- sound/soc/davinci/Makefile | 3 + sound/soc/davinci/cq93vc_copy.c | 130 +++++++++++++++++++++++++++++++++++++++ sound/soc/davinci/cq93vc_copy.h | 26 ++++++++ 3 files changed, 159 insertions(+), 0 deletions(-) create mode 100644 sound/soc/davinci/cq93vc_copy.c create mode 100644 sound/soc/davinci/cq93vc_copy.h
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile index 7d6a9a1..44c17ad 100644 --- a/sound/soc/davinci/Makefile +++ b/sound/soc/davinci/Makefile @@ -1,6 +1,7 @@ # DAVINCI Platform Support snd-soc-davinci-objs := davinci-pcm.o snd-soc-davinci-objs += davinci-pcm-copyfromuser.o +snd-soc-davinci-objs += cq93vc_copy.o snd-soc-davinci-i2s-objs := davinci-i2s.o snd-soc-davinci-mcasp-objs:= davinci-mcasp.o snd-soc-davinci-vcif-objs:= davinci-vcif.o @@ -13,9 +14,11 @@ obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o # DAVINCI Machine Support snd-soc-evm-objs := davinci-evm.o snd-soc-sffsdr-objs := davinci-sffsdr.o +snd-soc-bmx-objs := davinci-bmx.o
obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o obj-$(CONFIG_SND_DM6467_SOC_EVM) += snd-soc-evm.o obj-$(CONFIG_SND_DA830_SOC_EVM) += snd-soc-evm.o obj-$(CONFIG_SND_DA850_SOC_EVM) += snd-soc-evm.o obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o +obj-$(CONFIG_SND_DAVINCI_SOC_BMX) += snd-soc-bmx.o diff --git a/sound/soc/davinci/cq93vc_copy.c b/sound/soc/davinci/cq93vc_copy.c new file mode 100644 index 0000000..41747dd --- /dev/null +++ b/sound/soc/davinci/cq93vc_copy.c @@ -0,0 +1,130 @@ +/* + * + * Copyright (C) 2010 Bticino S.p.a + * Author: Davide Bonfanti davide.bonfanti@bticino.it + * + * Contributors: + * Raffaele Recalcati raffaele.recalcati@bticino.it + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/hrtimer.h> +#include <linux/clk.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <mach/gpio.h> + +#include "davinci-pcm-copy.h" +#include "cq93vc_copy.h" + +#define DAVINCI_VOICECODEC_MODULE_BASE 0x01D0C000 +#define VC_PID 0x00 +#define VC_CTRL 0x04 +#define VC_INTEN 0x08 +#define VC_INTSTATUS 0x0C +#define VC_INTCLR 0x10 +#define VC_EMUL_CTRL 0x14 +#define RFIFO 0x20 +#define WFIFO 0x24 +#define FIFOSTAT 0x28 +#define VC_REG00 0x80 +#define VC_REG01 0x84 +#define VC_REG02 0x88 +#define VC_REG03 0x8C +#define VC_REG04 0x90 +#define VC_REG05 0x94 +#define VC_REG06 0x98 +#define VC_REG09 0xA4 +#define VC_REG10 0xA8 +#define VC_REG12 0xB0 + +/* bit definitions */ +#define VC_CTRL_WFIFOMD BIT(14) +#define VC_CTRL_WFIFOCL BIT(13) +#define VC_CTRL_WFIFOEN BIT(12) +#define VC_CTRL_RFIFOMD BIT(10) +#define VC_CTRL_RFIFOCL BIT(9) +#define VC_CTRL_RFIFOEN BIT(8) +#define VC_CTRL_WDUNSIGNED BIT(7) +#define VC_CTRL_WDSIZE BIT(6) +#define VC_CTRL_RDUNSIGNED BIT(5) +#define VC_CTRL_RDSIZE BIT(4) +#define VC_CTRL_RSTDAC BIT(1) +#define VC_CTRL_RSTADC BIT(0) + +#define VC_INTSTATUS_WDREQ BIT(3) + +#define FIFOSTAT_WDATACOUNT_MASK 0x1F00 +#define FIFOSTAT_WDATACOUNT_OFFSET 8 + +#define HW_FIFO_SIZE 0x10 + +void __iomem *voice_codec_base; + +void cq93vc_enable(void) +{ + __raw_writel(0, voice_codec_base + VC_CTRL); + __raw_writel(0, voice_codec_base + VC_INTEN); + __raw_writel(VC_CTRL_RFIFOMD | VC_CTRL_WFIFOEN | + VC_CTRL_WFIFOMD, voice_codec_base + VC_CTRL); +} + +void cq93vc_write(u16 data) +{ + __raw_writew(data, voice_codec_base + WFIFO); +} + +void cq93vc_wait_fifo_ready(void) +{ + u32 diff; + do { + diff = __raw_readl(voice_codec_base + VC_INTSTATUS); + } while (!(diff & VC_INTSTATUS_WDREQ)); +} + +int cq93vc_get_fifo_size(void) +{ + return HW_FIFO_SIZE; +} + +int cq93vc_get_fifo_status(void) +{ + int fifo; + fifo = __raw_readl(voice_codec_base + FIFOSTAT); + fifo = (fifo & FIFOSTAT_WDATACOUNT_MASK) >> FIFOSTAT_WDATACOUNT_OFFSET; + return fifo; +} + +void cq93vc_init(void) +{ + voice_codec_base = ioremap(DAVINCI_VOICECODEC_MODULE_BASE, SZ_4K); + +} + +struct davinci_pcm_copy_ops cq93vc_pcm_copy_ops = { + .enable = cq93vc_enable, + .write = cq93vc_write, + .wait_fifo_ready = cq93vc_wait_fifo_ready, + .get_fifo_size = cq93vc_get_fifo_size, + .get_fifo_status = cq93vc_get_fifo_status, + .init = cq93vc_init, +}; +EXPORT_SYMBOL_GPL(cq93vc_pcm_copy_ops); diff --git a/sound/soc/davinci/cq93vc_copy.h b/sound/soc/davinci/cq93vc_copy.h new file mode 100644 index 0000000..bcc4458 --- /dev/null +++ b/sound/soc/davinci/cq93vc_copy.h @@ -0,0 +1,26 @@ +/* + * + * Copyright (C) 2010 Bticino S.p.a + * Author: Davide Bonfanti davide.bonfanti@bticino.it + * + * Contributors: + * Raffaele Recalcati raffaele.recalcati@bticino.it + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _DAVINCI_CQ93VC_COPY_H +#define _DAVINCI_CQ93VC_COPY_H +#include "davinci-pcm-copy.h" + +extern struct davinci_pcm_copy_ops cq93vc_pcm_copy_ops; + +#endif /* _DAVINCI_CQ93VC_COPY_H */
participants (2)
-
Mark Brown
-
Raffaele Recalcati