[alsa-devel] [PATCH V2 00/10]
This patch series is to add audio support based on saif for mxs platform. The driver only supports playback currently. For capturing, due to hardware limitation that we have to use two saif instances to implement full duplex (playback & recording), we need to figure out a good design to fit in ASoC as the following work.
In addition, we simply just support master mode since saif tx can only work as a master due to hw limitation(rx can work in both master & slave mode).
Code is written based on the branch imx-for-3.1 in sascha's tree http://git.pengutronix.de/git/imx/linux-2.6.git
Tested on MX28EVK.
Main changes since v1: * export saif mclk control interface to machine driver. Machine driver can decide whether it needs to use mclk supplied by saif according to its requirement. * error checking improvement * remove a few unneccesary code
Dong Aisheng (10): ASoc: mxs: add mxs-pcm driver ASoc: mxs: add mxs-saif driver ASoc: mxs: add mxs-sgtl5000 machine driver ASoc: mxs: add asoc configuration files ARM: mxs: add saif clock ARM: mxs: add saif device ARM: mxs: add sgtl5000 i2c device ARM: mxs: add mxs-sgtl5000 device ARM: mxs: correct the using of frac div for saif ARM: mxs-dma: include <linux/dmaengine.h>
arch/arm/mach-mxs/Kconfig | 1 + arch/arm/mach-mxs/clock-mx28.c | 8 +- arch/arm/mach-mxs/devices-mx28.h | 3 + arch/arm/mach-mxs/devices/Kconfig | 3 + arch/arm/mach-mxs/devices/Makefile | 1 + arch/arm/mach-mxs/devices/platform-mxs-saif.c | 60 ++ arch/arm/mach-mxs/include/mach/devices-common.h | 12 + arch/arm/mach-mxs/include/mach/dma.h | 2 + arch/arm/mach-mxs/mach-mx28evk.c | 39 ++ sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/mxs/Kconfig | 19 + sound/soc/mxs/Makefile | 10 + sound/soc/mxs/mxs-pcm.c | 367 +++++++++++++ sound/soc/mxs/mxs-pcm.h | 43 ++ sound/soc/mxs/mxs-saif.c | 656 +++++++++++++++++++++++ sound/soc/mxs/mxs-saif.h | 130 +++++ sound/soc/mxs/mxs-sgtl5000.c | 175 ++++++ 18 files changed, 1529 insertions(+), 2 deletions(-) create mode 100644 arch/arm/mach-mxs/devices/platform-mxs-saif.c create mode 100644 sound/soc/mxs/Kconfig create mode 100644 sound/soc/mxs/Makefile create mode 100644 sound/soc/mxs/mxs-pcm.c create mode 100644 sound/soc/mxs/mxs-pcm.h create mode 100644 sound/soc/mxs/mxs-saif.c create mode 100644 sound/soc/mxs/mxs-saif.h create mode 100644 sound/soc/mxs/mxs-sgtl5000.c
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Liam Girdwood lrg@ti.com Cc: Sascha Hauer s.hauer@pengutronix.de --- sound/soc/mxs/mxs-pcm.c | 367 +++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/mxs/mxs-pcm.h | 43 ++++++ 2 files changed, 410 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c new file mode 100644 index 0000000..3e3a8c8 --- /dev/null +++ b/sound/soc/mxs/mxs-pcm.c @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * Refer to sound/soc/imx/imx-pcm-dma-mx2.c + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dmaengine.h> + +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include <mach/dma.h> +#include "mxs-pcm.h" + +static struct snd_pcm_hardware snd_mxs_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = 32, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 255, + .buffer_bytes_max = 64 * 1024, + .fifo_size = 32, + +}; + +static void audio_dma_irq(void *data) +{ + struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct mxs_pcm_runtime_data *iprtd = runtime->private_data; + + iprtd->offset += iprtd->period_bytes; + iprtd->offset %= iprtd->period_bytes * iprtd->periods; + snd_pcm_period_elapsed(substream); +} + +static bool filter(struct dma_chan *chan, void *param) +{ + struct mxs_pcm_runtime_data *iprtd = param; + struct mxs_pcm_dma_params *dma_params = iprtd->dma_params; + + if (!mxs_dma_is_apbx(chan)) + return false; + + if (chan->chan_id != dma_params->chan_num) + return false; + + chan->private = &iprtd->dma_data; + + return true; +} + +static int mxs_dma_alloc(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_pcm_runtime *runtime = substream->runtime; + struct mxs_pcm_runtime_data *iprtd = runtime->private_data; + dma_cap_mask_t mask; + + iprtd->dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + iprtd->dma_data.chan_irq = iprtd->dma_params->chan_irq; + iprtd->dma_chan = dma_request_channel(mask, filter, iprtd); + if (!iprtd->dma_chan) + return -EINVAL; + + return 0; +} + +static int snd_mxs_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mxs_pcm_runtime_data *iprtd = runtime->private_data; + unsigned long dma_addr; + struct dma_chan *chan; + int ret; + + ret = mxs_dma_alloc(substream, params); + if (ret) + return ret; + chan = iprtd->dma_chan; + + iprtd->size = params_buffer_bytes(params); + iprtd->periods = params_periods(params); + iprtd->period_bytes = params_period_bytes(params); + iprtd->offset = 0; + iprtd->period_time = HZ / (params_rate(params) / + params_period_size(params)); + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + dma_addr = runtime->dma_addr; + + iprtd->buf = substream->dma_buffer.area; + + iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr, + iprtd->period_bytes * iprtd->periods, + iprtd->period_bytes, + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (!iprtd->desc) { + dev_err(&chan->dev->device, "cannot prepare slave dma\n"); + return -EINVAL; + } + + iprtd->desc->callback = audio_dma_irq; + iprtd->desc->callback_param = substream; + + return 0; +} + +static int snd_mxs_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mxs_pcm_runtime_data *iprtd = runtime->private_data; + + if (iprtd->dma_chan) { + dma_release_channel(iprtd->dma_chan); + iprtd->dma_chan = NULL; + } + + return 0; +} + +static int snd_mxs_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mxs_pcm_runtime_data *iprtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dmaengine_submit(iprtd->desc); + + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dmaengine_terminate_all(iprtd->dma_chan); + + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t snd_mxs_pcm_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mxs_pcm_runtime_data *iprtd = runtime->private_data; + + pr_debug("%s: %ld %ld\n", __func__, iprtd->offset, + bytes_to_frames(substream->runtime, iprtd->offset)); + + return bytes_to_frames(substream->runtime, iprtd->offset); +} + +static int snd_mxs_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mxs_pcm_runtime_data *iprtd; + int ret; + + iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); + if (iprtd == NULL) + return -ENOMEM; + runtime->private_data = iprtd; + + ret = snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + kfree(iprtd); + return ret; + } + + snd_soc_set_runtime_hwparams(substream, &snd_mxs_hardware); + + return 0; +} + +static int snd_mxs_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mxs_pcm_runtime_data *iprtd = runtime->private_data; + + kfree(iprtd); + + return 0; +} + +static int snd_mxs_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + ret = dma_mmap_coherent(NULL, vma, runtime->dma_area, + runtime->dma_addr, runtime->dma_bytes); + + pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); + return ret; +} + +static struct snd_pcm_ops mxs_pcm_ops = { + .open = snd_mxs_open, + .close = snd_mxs_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_mxs_pcm_hw_params, + .hw_free = snd_mxs_pcm_hw_free, + .trigger = snd_mxs_pcm_trigger, + .pointer = snd_mxs_pcm_pointer, + .mmap = snd_mxs_pcm_mmap, +}; + +static int mxs_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 = 64 * 1024; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + buf->bytes = size; + + return 0; +} + +static u64 mxs_pcm_dmamask = DMA_BIT_MASK(32); +static int mxs_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, + struct snd_pcm *pcm) +{ + + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &mxs_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + if (dai->driver->playback.channels_min) { + ret = mxs_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (dai->driver->capture.channels_min) { + ret = mxs_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + +out: + return ret; +} + +static void mxs_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + +static struct snd_soc_platform_driver mxs_soc_platform = { + .ops = &mxs_pcm_ops, + .pcm_new = mxs_pcm_new, + .pcm_free = mxs_pcm_free, +}; + +static int __devinit mxs_soc_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_platform(&pdev->dev, &mxs_soc_platform); +} + +static int __devexit mxs_soc_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + + return 0; +} + +static struct platform_driver mxs_pcm_driver = { + .driver = { + .name = "mxs-pcm-audio", + .owner = THIS_MODULE, + }, + .probe = mxs_soc_platform_probe, + .remove = __devexit_p(mxs_soc_platform_remove), +}; + +static int __init snd_mxs_pcm_init(void) +{ + return platform_driver_register(&mxs_pcm_driver); +} +module_init(snd_mxs_pcm_init); + +static void __exit snd_mxs_pcm_exit(void) +{ + platform_driver_unregister(&mxs_pcm_driver); +} +module_exit(snd_mxs_pcm_exit); diff --git a/sound/soc/mxs/mxs-pcm.h b/sound/soc/mxs/mxs-pcm.h new file mode 100644 index 0000000..f55ac4f --- /dev/null +++ b/sound/soc/mxs/mxs-pcm.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MXS_PCM_H +#define _MXS_PCM_H + +#include <mach/dma.h> + +struct mxs_pcm_dma_params { + int chan_irq; + int chan_num; +}; + +struct mxs_pcm_runtime_data { + int period_bytes; + int periods; + int dma; + unsigned long offset; + unsigned long size; + void *buf; + int period_time; + struct dma_async_tx_descriptor *desc; + struct dma_chan *dma_chan; + struct mxs_dma_data dma_data; + struct mxs_pcm_dma_params *dma_params; +}; + +#endif
On Tue, Jul 12, 2011 at 11:04:36PM +0800, Dong Aisheng wrote:
- if (dai->driver->capture.channels_min) {
ret = mxs_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto out;
- }
For robustness replace the channels_min check with a check for the relevant substream being present (see pxa2xx-pcm.c for a recent example).
-----Original Message----- From: Mark Brown [mailto:broonie@opensource.wolfsonmicro.com] Sent: Wednesday, July 13, 2011 7:27 AM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; linux-arm-kernel@lists.infradead.org; lrg@ti.com; s.hauer@pengutronix.de; u.kleine-koenig@pengutronix.de Subject: Re: [PATCH V2 01/10] ASoc: mxs: add mxs-pcm driver
On Tue, Jul 12, 2011 at 11:04:36PM +0800, Dong Aisheng wrote:
- if (dai->driver->capture.channels_min) {
ret = mxs_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto out;
- }
For robustness replace the channels_min check with a check for the relevant substream being present (see pxa2xx-pcm.c for a recent example).
Thanks for the reminder. I will check it.
Regards Dong Aisheng
-----Original Message----- From: Mark Brown [mailto:broonie@opensource.wolfsonmicro.com] Sent: Wednesday, July 13, 2011 7:27 AM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; linux-arm-kernel@lists.infradead.org; lrg@ti.com; s.hauer@pengutronix.de; u.kleine-koenig@pengutronix.de Subject: Re: [PATCH V2 01/10] ASoc: mxs: add mxs-pcm driver
On Tue, Jul 12, 2011 at 11:04:36PM +0800, Dong Aisheng wrote:
- if (dai->driver->capture.channels_min) {
ret = mxs_pcm_preallocate_dma_buffer(pcm,
SNDRV_PCM_STREAM_CAPTURE);
if (ret)
goto out;
- }
For robustness replace the channels_min check with a check for the relevant substream being present (see pxa2xx-pcm.c for a recent example).
Is this change ok? diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c index 3e3a8c8..a385ba9 100644 --- a/sound/soc/mxs/mxs-pcm.c +++ b/sound/soc/mxs/mxs-pcm.c @@ -288,14 +288,14 @@ static int mxs_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, card->dev->dma_mask = &mxs_pcm_dmamask; if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - if (dai->driver->playback.channels_min) { + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { ret = mxs_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) goto out; }
- if (dai->driver->capture.channels_min) { + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { ret = mxs_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret)
Regards Dong Aisheng
On Tue, Jul 12, 2011 at 11:04:36PM +0800, Dong Aisheng wrote:
+static int mxs_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 = 64 * 1024;
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
...
+static int snd_mxs_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
+{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
- ret = dma_mmap_coherent(NULL, vma, runtime->dma_area,
runtime->dma_addr, runtime->dma_bytes);
This is wrong. Hint 1:
int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma, void *cpu_addr, dma_addr_t dma_addr, size_t size)
Hint 2: struct device *dev - you have one above.
-----Original Message----- From: Russell King - ARM Linux [mailto:linux@arm.linux.org.uk] Sent: Wednesday, July 13, 2011 3:45 PM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; broonie@opensource.wolfsonmicro.com; lrg@ti.com; linux-arm- kernel@lists.infradead.org; u.kleine-koenig@pengutronix.de Subject: Re: [PATCH V2 01/10] ASoc: mxs: add mxs-pcm driver
On Tue, Jul 12, 2011 at 11:04:36PM +0800, Dong Aisheng wrote:
+static int mxs_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 = 64 * 1024;
- buf->dev.type = SNDRV_DMA_TYPE_DEV;
- buf->dev.dev = pcm->card->dev;
- buf->private_data = NULL;
- buf->area = dma_alloc_writecombine(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
...
+static int snd_mxs_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
+{
- struct snd_pcm_runtime *runtime = substream->runtime;
- int ret;
- ret = dma_mmap_coherent(NULL, vma, runtime->dma_area,
runtime->dma_addr, runtime->dma_bytes);
This is wrong. Hint 1:
int dma_mmap_writecombine(struct device *dev, struct vm_area_struct *vma, void *cpu_addr, dma_addr_t dma_addr, size_t size)
Hint 2: struct device *dev - you have one above.
Got it. Thanks for the reminder.
Regards Dong Aisheng
+static struct snd_pcm_hardware snd_mxs_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
- .channels_min = 2,
- .channels_max = 2,
- .period_bytes_min = 32,
- .period_bytes_max = 8192,
- .periods_min = 1,
- .periods_max = 255,
When using madplay (for MP3) instead of aplay, I get
[ 3310.010000] mxs-dma mxs-dma-apbx: maximum number of sg exceeded: 128 > 53 [ 3310.020000] dma dma1chan4: cannot prepare slave dma
So, the mxs-dma limit should be taken into account here.
- .buffer_bytes_max = 64 * 1024,
- .fifo_size = 32,
+};
2011/7/14 Wolfram Sang w.sang@pengutronix.de:
+static struct snd_pcm_hardware snd_mxs_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_INTERLEAVED,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S20_3LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
- .channels_min = 2,
- .channels_max = 2,
- .period_bytes_min = 32,
- .period_bytes_max = 8192,
- .periods_min = 1,
- .periods_max = 255,
When using madplay (for MP3) instead of aplay, I get
[ 3310.010000] mxs-dma mxs-dma-apbx: maximum number of sg exceeded: 128 > 53 [ 3310.020000] dma dma1chan4: cannot prepare slave dma
So, the mxs-dma limit should be taken into account here.
I also have tried a lot on mp3 playing with madplay, however still have not met this issue. Can you tell how can i get the same mp3 as you played? Maybe it helps me to reproduce this issue. I will check it tomorrow. Thanks for the reminder.
- .buffer_bytes_max = 64 * 1024,
- .fifo_size = 32,
+};
-----Original Message----- From: Dong Aisheng [mailto:dongas86@gmail.com] Sent: Friday, July 15, 2011 12:15 AM To: Wolfram Sang Cc: Dong Aisheng-B29396; alsa-devel@alsa-project.org; s.hauer@pengutronix.de; broonie@opensource.wolfsonmicro.com; u.kleine- koenig@pengutronix.de; lrg@ti.com; linux-arm-kernel@lists.infradead.org Subject: Re: [alsa-devel] [PATCH V2 01/10] ASoc: mxs: add mxs-pcm driver
2011/7/14 Wolfram Sang w.sang@pengutronix.de:
+static struct snd_pcm_hardware snd_mxs_hardware = {
- .info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME |
- SNDRV_PCM_INFO_INTERLEAVED,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S20_3LE |
- SNDRV_PCM_FMTBIT_S24_LE |
- SNDRV_PCM_FMTBIT_S32_LE,
- .channels_min = 2,
- .channels_max = 2,
- .period_bytes_min = 32,
- .period_bytes_max = 8192,
- .periods_min = 1,
- .periods_max = 255,
When using madplay (for MP3) instead of aplay, I get
[ 3310.010000] mxs-dma mxs-dma-apbx: maximum number of sg exceeded: 128 > 53 [ 3310.020000] dma dma1chan4: cannot prepare slave dma
So, the mxs-dma limit should be taken into account here.
I also have tried a lot on mp3 playing with madplay, however still have not met this issue. Can you tell how can i get the same mp3 as you played? Maybe it helps me to reproduce this issue. I will check it tomorrow. Thanks for the reminder.
Changed to 52 due for periods_max. .periods_min = 1, - .periods_max = 255, + .periods_max = 52,
Thanks.
Regards Dong Aisheng
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Liam Girdwood lrg@ti.com Cc: Sascha Hauer s.hauer@pengutronix.de --- sound/soc/mxs/mxs-saif.c | 656 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/mxs/mxs-saif.h | 130 +++++++++ 2 files changed, 786 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c new file mode 100644 index 0000000..08cb893 --- /dev/null +++ b/sound/soc/mxs/mxs-saif.c @@ -0,0 +1,656 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <mach/dma.h> +#include <asm/mach-types.h> +#include <mach/hardware.h> +#include <mach/mxs.h> + +#include "mxs-saif.h" + +static struct mxs_saif *mxs_saif[2]; + +static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); + + switch (clk_id) { + case MXS_SAIF_MCLK: + saif->mclk = freq; + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * Set SAIF clock and MCLK + */ +static int mxs_saif_set_clk(struct mxs_saif *saif, + unsigned int mclk, + unsigned int rate) +{ + u32 scr; + int ret; + + /* SAIF MCLK should be either 32*fs or 48*fs */ + if (saif->mclk_in_use && (mclk % 32 != 0) && (mclk % 48 != 0)) + return -EINVAL; + + scr = __raw_readl(saif->base + SAIF_CTRL); + scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE; + scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; + + /* The SAIF clock should be either 384*fs or 512*fs */ + if (saif->mclk_in_use) { + if (mclk % 32 == 0) { + scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; + ret = clk_set_rate(saif->clk, 512 * rate); + } else if (mclk % 48 == 0) { + scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE; + ret = clk_set_rate(saif->clk, 384 * rate); + } + } else { + /* mclk is not used, just set saif clock to 512*fs */ + ret = clk_set_rate(saif->clk, 512 * rate); + scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; + } + + if (ret) + return -EINVAL; + + if (!saif->mclk_in_use) { + __raw_writel(scr, saif->base + SAIF_CTRL); + return 0; + } + + /* select the multiple of the base frequency rate of MCLK */ + switch (mclk / rate) { + case 32: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(4); + break; + case 64: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3); + break; + case 128: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2); + break; + case 256: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1); + break; + case 512: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0); + break; + case 48: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3); + break; + case 96: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2); + break; + case 192: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1); + break; + case 384: + scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0); + break; + default: + return -EINVAL; + } + + __raw_writel(scr, saif->base + SAIF_CTRL); + + return 0; +} + +/* + * Put and disable MCLK. + */ +int mxs_saif_put_mclk(unsigned int saif_id) +{ + struct mxs_saif *saif = mxs_saif[saif_id]; + u32 stat; + + if (!saif) + return -EINVAL; + + stat = __raw_readl(saif->base + SAIF_STAT); + if (stat & BM_SAIF_STAT_BUSY) { + dev_err(saif->dev, "error: busy\n"); + return -EBUSY; + } + + clk_disable(saif->clk); + + /* disable MCLK output */ + __raw_writel(BM_SAIF_CTRL_CLKGATE, + saif->base + SAIF_CTRL + MXS_SET_ADDR); + __raw_writel(BM_SAIF_CTRL_RUN, + saif->base + SAIF_CTRL + MXS_CLR_ADDR); + + saif->mclk_in_use = 0; + return 0; +} + +/* + * Get MCLK and set clock rate to mclk, then enable it + * The available MCLK range is 32x, 48x... 512x. The rate + * could be from 8kHz to 192kH. + * + * This interface is used for codecs which are using MCLK provided + * by saif. + */ +int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk, + unsigned int rate) +{ + struct mxs_saif *saif = mxs_saif[saif_id]; + u32 stat; + int ret; + + if (!saif) + return -EINVAL; + + stat = __raw_readl(saif->base + SAIF_STAT); + if (stat & BM_SAIF_STAT_BUSY) { + dev_err(saif->dev, "error: busy\n"); + return -EBUSY; + } + + /* Clear Reset */ + __raw_writel(BM_SAIF_CTRL_SFTRST, + saif->base + SAIF_CTRL + MXS_CLR_ADDR); + + saif->mclk_in_use = 1; + ret = mxs_saif_set_clk(saif, mclk, rate); + if (ret) + return -EINVAL; + + ret = clk_enable(saif->clk); + if (ret) + return -EINVAL; + + /* enable MCLK output */ + __raw_writel(BM_SAIF_CTRL_CLKGATE, + saif->base + SAIF_CTRL + MXS_CLR_ADDR); + __raw_writel(BM_SAIF_CTRL_RUN, + saif->base + SAIF_CTRL + MXS_SET_ADDR); + + return 0; +} + +/* + * SAIF DAI format configuration. + * Should only be called when port is inactive. + */ +static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + u32 scr, stat; + u32 scr0; + struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); + + stat = __raw_readl(saif->base + SAIF_STAT); + if (stat & BM_SAIF_STAT_BUSY) { + dev_err(cpu_dai->dev, "error: busy\n"); + return -EBUSY; + } + + scr0 = __raw_readl(saif->base + SAIF_CTRL); + scr0 = scr0 & ~BM_SAIF_CTRL_BITCLK_EDGE & ~BM_SAIF_CTRL_LRCLK_POLARITY \ + & ~BM_SAIF_CTRL_JUSTIFY & ~BM_SAIF_CTRL_DELAY; + scr = 0; + + /* DAI mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* data frame low 1clk before data */ + scr |= BM_SAIF_CTRL_DELAY; + scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; + break; + case SND_SOC_DAIFMT_LEFT_J: + /* data frame high with data */ + scr &= ~BM_SAIF_CTRL_DELAY; + scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; + scr &= ~BM_SAIF_CTRL_JUSTIFY; + break; + default: + return -EINVAL; + } + + /* DAI clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + scr |= BM_SAIF_CTRL_BITCLK_EDGE; + scr |= BM_SAIF_CTRL_LRCLK_POLARITY; + break; + case SND_SOC_DAIFMT_IB_NF: + scr |= BM_SAIF_CTRL_BITCLK_EDGE; + scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; + break; + case SND_SOC_DAIFMT_NB_IF: + scr &= ~BM_SAIF_CTRL_BITCLK_EDGE; + scr |= BM_SAIF_CTRL_LRCLK_POLARITY; + break; + case SND_SOC_DAIFMT_NB_NF: + scr &= ~BM_SAIF_CTRL_BITCLK_EDGE; + scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY; + break; + } + + /* + * Note: We simply just support master mode since SAIF TX can only + * work as master. + */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + scr &= ~BM_SAIF_CTRL_SLAVE_MODE; + __raw_writel(scr | scr0, saif->base + SAIF_CTRL); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mxs_saif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); + snd_soc_dai_set_dma_data(cpu_dai, substream, &saif->dma_param); + + /* clear error status to 0 for each re-open */ + saif->fifo_underrun = 0; + saif->fifo_overrun = 0; + + /* Clear Reset for normal operations */ + __raw_writel(BM_SAIF_CTRL_SFTRST, + saif->base + SAIF_CTRL + MXS_CLR_ADDR); + + return 0; +} + +/* + * Should only be called when port is inactive. + * although can be called multiple times by upper layers. + */ +static int mxs_saif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); + u32 scr, stat; + int ret; + + /* mclk should already be set */ + if (!saif->mclk && saif->mclk_in_use) { + dev_err(cpu_dai->dev, "set mclk first\n"); + return -EINVAL; + } + + stat = __raw_readl(saif->base + SAIF_STAT); + if (stat & BM_SAIF_STAT_BUSY) { + dev_err(cpu_dai->dev, "error: busy\n"); + return -EBUSY; + } + + ret = mxs_saif_set_clk(saif, saif->mclk, params_rate(params)); + if (ret) { + dev_err(cpu_dai->dev, "unable to get proper mclk\n"); + return -EINVAL; + } + + scr = __raw_readl(saif->base + SAIF_CTRL); + + scr &= ~BM_SAIF_CTRL_WORD_LENGTH; + scr &= ~BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + scr |= BF_SAIF_CTRL_WORD_LENGTH(0); + break; + case SNDRV_PCM_FORMAT_S20_3LE: + scr |= BF_SAIF_CTRL_WORD_LENGTH(4); + scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; + break; + case SNDRV_PCM_FORMAT_S24_LE: + scr |= BF_SAIF_CTRL_WORD_LENGTH(8); + scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE; + break; + default: + return -EINVAL; + } + + /* Tx/Rx config */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* enable TX mode */ + scr &= ~BM_SAIF_CTRL_READ_MODE; + } else { + /* enable RX mode */ + scr |= BM_SAIF_CTRL_READ_MODE; + } + + __raw_writel(scr, saif->base + SAIF_CTRL); + return 0; +} + +static int mxs_saif_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); + + /* clear clock gate */ + __raw_writel(BM_SAIF_CTRL_CLKGATE, + saif->base + SAIF_CTRL + MXS_CLR_ADDR); + + /* enable FIFO error irqs */ + __raw_writel(BM_SAIF_CTRL_FIFO_ERROR_IRQ_EN, + saif->base + SAIF_CTRL + MXS_SET_ADDR); + + return 0; +} + +static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + dev_dbg(cpu_dai->dev, "start\n"); + + clk_enable(saif->clk); + if (!saif->mclk_in_use) + __raw_writel(BM_SAIF_CTRL_RUN, + saif->base + SAIF_CTRL + MXS_SET_ADDR); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* + * write a data to saif data register to trigger + * the transfer + */ + __raw_writel(0, saif->base + SAIF_DATA); + } else { + /* + * read a data from saif data register to trigger + * the receive + */ + __raw_readl(saif->base + SAIF_DATA); + } + + dev_dbg(cpu_dai->dev, "CTRL 0x%x STAT 0x%x\n", + __raw_readl(saif->base + SAIF_CTRL), + __raw_readl(saif->base + SAIF_STAT)); + + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + dev_dbg(cpu_dai->dev, "stop\n"); + + clk_disable(saif->clk); + if (!saif->mclk_in_use) + __raw_writel(BM_SAIF_CTRL_RUN, + saif->base + SAIF_CTRL + MXS_CLR_ADDR); + + break; + default: + return -EINVAL; + } + + return 0; +} + +#define MXS_SAIF_RATES SNDRV_PCM_RATE_8000_192000 +#define MXS_SAIF_FORMATS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static struct snd_soc_dai_ops mxs_saif_dai_ops = { + .startup = mxs_saif_startup, + .trigger = mxs_saif_trigger, + .prepare = mxs_saif_prepare, + .hw_params = mxs_saif_hw_params, + .set_sysclk = mxs_saif_set_dai_sysclk, + .set_fmt = mxs_saif_set_dai_fmt, +}; + +static int mxs_saif_dai_probe(struct snd_soc_dai *dai) +{ + struct mxs_saif *saif = dev_get_drvdata(dai->dev); + + snd_soc_dai_set_drvdata(dai, saif); + + return 0; +} + +static struct snd_soc_dai_driver mxs_saif_dai = { + .name = "mxs-saif", + .probe = mxs_saif_dai_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = MXS_SAIF_RATES, + .formats = MXS_SAIF_FORMATS, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = MXS_SAIF_RATES, + .formats = MXS_SAIF_FORMATS, + }, + .ops = &mxs_saif_dai_ops, +}; + +static irqreturn_t mxs_saif_irq(int irq, void *dev_id) +{ + struct mxs_saif *saif = dev_id; + unsigned int stat; + + stat = __raw_readl(saif->base + SAIF_STAT); + if (stat & BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ) + dev_dbg(saif->dev, "underrun!!! %d\n", ++saif->fifo_underrun); + + if (stat & BM_SAIF_STAT_FIFO_OVERFLOW_IRQ) + dev_dbg(saif->dev, "overrun!!! %d\n", ++saif->fifo_overrun); + + dev_dbg(saif->dev, "SAIF_CTRL %x SAIF_STAT %x\n", + __raw_readl(saif->base + SAIF_CTRL), + __raw_readl(saif->base + SAIF_STAT)); + + __raw_writel(BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ | + BM_SAIF_STAT_FIFO_OVERFLOW_IRQ, + saif->base + SAIF_STAT + MXS_CLR_ADDR); + + return IRQ_HANDLED; +} + +static int mxs_saif_probe(struct platform_device *pdev) +{ + struct resource *res; + struct mxs_saif *saif; + int ret = 0; + + saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL); + if (!saif) + return -ENOMEM; + + if (pdev->id >= 2) + return -EINVAL; + mxs_saif[pdev->id] = saif; + + saif->irq = platform_get_irq(pdev, 0); + if (saif->irq < 0) { + ret = saif->irq; + dev_err(&pdev->dev, "failed to get irq resource: %d\n", + ret); + goto failed_get_irq1; + } + + saif->dev = &pdev->dev; + ret = request_irq(saif->irq, mxs_saif_irq, 0, "mxs-saif", saif); + if (ret) { + dev_err(&pdev->dev, "failed to request irq\n"); + goto failed_req_irq; + } + + saif->dma_param.chan_irq = platform_get_irq(pdev, 1); + if (saif->dma_param.chan_irq < 0) { + ret = saif->dma_param.chan_irq; + dev_err(&pdev->dev, "failed to get dma irq resource: %d\n", + ret); + goto failed_get_irq2; + } + + saif->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(saif->clk)) { + ret = PTR_ERR(saif->clk); + dev_err(&pdev->dev, "Cannot get the clock: %d\n", + ret); + goto failed_clk; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + dev_err(&pdev->dev, "failed to get io resource: %d\n", + ret); + goto failed_get_resource; + } + + if (!request_mem_region(res->start, resource_size(res), "mxs-saif")) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + ret = -EBUSY; + goto failed_get_resource; + } + + saif->base = ioremap(res->start, resource_size(res)); + if (!saif->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENODEV; + goto failed_ioremap; + } + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + ret = -ENODEV; + dev_err(&pdev->dev, "failed to get dma resource: %d\n", + ret); + goto failed_get_resource; + } + saif->dma_param.chan_num = res->start; + + platform_set_drvdata(pdev, saif); + + ret = snd_soc_register_dai(&pdev->dev, &mxs_saif_dai); + if (ret) { + dev_err(&pdev->dev, "register DAI failed\n"); + goto failed_register; + } + + saif->soc_platform_pdev = platform_device_alloc( + "mxs-pcm-audio", pdev->id); + if (!saif->soc_platform_pdev) { + ret = -ENOMEM; + goto failed_pdev_alloc; + } + + platform_set_drvdata(saif->soc_platform_pdev, saif); + ret = platform_device_add(saif->soc_platform_pdev); + if (ret) { + dev_err(&pdev->dev, "failed to add soc platform device\n"); + goto failed_pdev_add; + } + + return 0; + +failed_pdev_add: + platform_device_put(saif->soc_platform_pdev); +failed_pdev_alloc: + snd_soc_unregister_dai(&pdev->dev); +failed_register: + iounmap(saif->base); +failed_ioremap: + release_mem_region(res->start, resource_size(res)); +failed_get_resource: + clk_put(saif->clk); +failed_clk: +failed_get_irq2: + free_irq(saif->irq, saif); +failed_req_irq: +failed_get_irq1: + kfree(saif); + + return ret; +} + +static int __devexit mxs_saif_remove(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct mxs_saif *saif = platform_get_drvdata(pdev); + + platform_device_unregister(saif->soc_platform_pdev); + + snd_soc_unregister_dai(&pdev->dev); + + iounmap(saif->base); + release_mem_region(res->start, resource_size(res)); + free_irq(saif->irq, saif); + + clk_put(saif->clk); + + return 0; +} + +static struct platform_driver mxs_saif_driver = { + .probe = mxs_saif_probe, + .remove = __devexit_p(mxs_saif_remove), + + .driver = { + .name = "mxs-saif", + .owner = THIS_MODULE, + }, +}; + +static int __init mxs_saif_init(void) +{ + return platform_driver_register(&mxs_saif_driver); +} + +static void __exit mxs_saif_exit(void) +{ + platform_driver_unregister(&mxs_saif_driver); +} + +module_init(mxs_saif_init); +module_exit(mxs_saif_exit); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXS ASoC SAIF driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h new file mode 100644 index 0000000..0e2ff8c --- /dev/null +++ b/sound/soc/mxs/mxs-saif.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#ifndef _MXS_SAIF_H +#define _MXS_SAIF_H + +#define SAIF_CTRL 0x0 +#define SAIF_STAT 0x10 +#define SAIF_DATA 0x20 +#define SAIF_VERSION 0X30 + +/* SAIF_CTRL */ +#define BM_SAIF_CTRL_SFTRST 0x80000000 +#define BM_SAIF_CTRL_CLKGATE 0x40000000 +#define BP_SAIF_CTRL_BITCLK_MULT_RATE 27 +#define BM_SAIF_CTRL_BITCLK_MULT_RATE 0x38000000 +#define BF_SAIF_CTRL_BITCLK_MULT_RATE(v) \ + (((v) << 27) & BM_SAIF_CTRL_BITCLK_MULT_RATE) +#define BM_SAIF_CTRL_BITCLK_BASE_RATE 0x04000000 +#define BM_SAIF_CTRL_FIFO_ERROR_IRQ_EN 0x02000000 +#define BM_SAIF_CTRL_FIFO_SERVICE_IRQ_EN 0x01000000 +#define BP_SAIF_CTRL_RSRVD2 21 +#define BM_SAIF_CTRL_RSRVD2 0x00E00000 + +#define BP_SAIF_CTRL_DMAWAIT_COUNT 16 +#define BM_SAIF_CTRL_DMAWAIT_COUNT 0x001F0000 +#define BF_SAIF_CTRL_DMAWAIT_COUNT(v) \ + (((v) << 16) & BM_SAIF_CTRL_DMAWAIT_COUNT) +#define BP_SAIF_CTRL_CHANNEL_NUM_SELECT 14 +#define BM_SAIF_CTRL_CHANNEL_NUM_SELECT 0x0000C000 +#define BF_SAIF_CTRL_CHANNEL_NUM_SELECT(v) \ + (((v) << 14) & BM_SAIF_CTRL_CHANNEL_NUM_SELECT) +#define BM_SAIF_CTRL_LRCLK_PULSE 0x00002000 +#define BM_SAIF_CTRL_BIT_ORDER 0x00001000 +#define BM_SAIF_CTRL_DELAY 0x00000800 +#define BM_SAIF_CTRL_JUSTIFY 0x00000400 +#define BM_SAIF_CTRL_LRCLK_POLARITY 0x00000200 +#define BM_SAIF_CTRL_BITCLK_EDGE 0x00000100 +#define BP_SAIF_CTRL_WORD_LENGTH 4 +#define BM_SAIF_CTRL_WORD_LENGTH 0x000000F0 +#define BF_SAIF_CTRL_WORD_LENGTH(v) \ + (((v) << 4) & BM_SAIF_CTRL_WORD_LENGTH) +#define BM_SAIF_CTRL_BITCLK_48XFS_ENABLE 0x00000008 +#define BM_SAIF_CTRL_SLAVE_MODE 0x00000004 +#define BM_SAIF_CTRL_READ_MODE 0x00000002 +#define BM_SAIF_CTRL_RUN 0x00000001 + +/* SAIF_STAT */ +#define BM_SAIF_STAT_PRESENT 0x80000000 +#define BP_SAIF_STAT_RSRVD2 17 +#define BM_SAIF_STAT_RSRVD2 0x7FFE0000 +#define BF_SAIF_STAT_RSRVD2(v) \ + (((v) << 17) & BM_SAIF_STAT_RSRVD2) +#define BM_SAIF_STAT_DMA_PREQ 0x00010000 +#define BP_SAIF_STAT_RSRVD1 7 +#define BM_SAIF_STAT_RSRVD1 0x0000FF80 +#define BF_SAIF_STAT_RSRVD1(v) \ + (((v) << 7) & BM_SAIF_STAT_RSRVD1) + +#define BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ 0x00000040 +#define BM_SAIF_STAT_FIFO_OVERFLOW_IRQ 0x00000020 +#define BM_SAIF_STAT_FIFO_SERVICE_IRQ 0x00000010 +#define BP_SAIF_STAT_RSRVD0 1 +#define BM_SAIF_STAT_RSRVD0 0x0000000E +#define BF_SAIF_STAT_RSRVD0(v) \ + (((v) << 1) & BM_SAIF_STAT_RSRVD0) +#define BM_SAIF_STAT_BUSY 0x00000001 + +/* SAFI_DATA */ +#define BP_SAIF_DATA_PCM_RIGHT 16 +#define BM_SAIF_DATA_PCM_RIGHT 0xFFFF0000 +#define BF_SAIF_DATA_PCM_RIGHT(v) \ + (((v) << 16) & BM_SAIF_DATA_PCM_RIGHT) +#define BP_SAIF_DATA_PCM_LEFT 0 +#define BM_SAIF_DATA_PCM_LEFT 0x0000FFFF +#define BF_SAIF_DATA_PCM_LEFT(v) \ + (((v) << 0) & BM_SAIF_DATA_PCM_LEFT) + +/* SAIF_VERSION */ +#define BP_SAIF_VERSION_MAJOR 24 +#define BM_SAIF_VERSION_MAJOR 0xFF000000 +#define BF_SAIF_VERSION_MAJOR(v) \ + (((v) << 24) & BM_SAIF_VERSION_MAJOR) +#define BP_SAIF_VERSION_MINOR 16 +#define BM_SAIF_VERSION_MINOR 0x00FF0000 +#define BF_SAIF_VERSION_MINOR(v) \ + (((v) << 16) & BM_SAIF_VERSION_MINOR) +#define BP_SAIF_VERSION_STEP 0 +#define BM_SAIF_VERSION_STEP 0x0000FFFF +#define BF_SAIF_VERSION_STEP(v) \ + (((v) << 0) & BM_SAIF_VERSION_STEP) + +#define MXS_SAIF_MCLK 0 + +#include "mxs-pcm.h" + +struct mxs_saif { + struct device *dev; + struct clk *clk; + unsigned int mclk; + unsigned int mclk_in_use; + void __iomem *base; + int irq; + struct mxs_pcm_dma_params dma_param; + + struct platform_device *soc_platform_pdev; + u32 fifo_underrun; + u32 fifo_overrun; +}; + +extern int mxs_saif_put_mclk(unsigned int saif_id); +extern int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk, + unsigned int rate); +#endif
On Tue, Jul 12, 2011 at 11:04:37PM +0800, Dong Aisheng wrote:
Looks pretty good, a few small issues below.
- /* The SAIF clock should be either 384*fs or 512*fs */
- if (saif->mclk_in_use) {
if (mclk % 32 == 0) {
scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
ret = clk_set_rate(saif->clk, 512 * rate);
} else if (mclk % 48 == 0) {
scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
ret = clk_set_rate(saif->clk, 384 * rate);
}
What if MCLK is not set corectly for some reason? We'll just silently do nothing.
- if (ret)
return -EINVAL;
Should pass through any error codes we get.
In hw_params()...
- stat = __raw_readl(saif->base + SAIF_STAT);
- if (stat & BM_SAIF_STAT_BUSY) {
dev_err(cpu_dai->dev, "error: busy\n");
return -EBUSY;
- }
Does this work for simultaneous playback and record? Perhaps you need to set symmetric_rates in the DAI (if the hardware needs the same sample rate for playback and record) and have a check here to see if we're trying to apply the same configuration as we already have.
- struct mxs_saif *saif = dev_id;
- unsigned int stat;
- stat = __raw_readl(saif->base + SAIF_STAT);
- if (stat & BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ)
dev_dbg(saif->dev, "underrun!!! %d\n", ++saif->fifo_underrun);
- if (stat & BM_SAIF_STAT_FIFO_OVERFLOW_IRQ)
dev_dbg(saif->dev, "overrun!!! %d\n", ++saif->fifo_overrun);
Probably dev_err()?
- __raw_writel(BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ |
BM_SAIF_STAT_FIFO_OVERFLOW_IRQ,
saif->base + SAIF_STAT + MXS_CLR_ADDR);
- return IRQ_HANDLED;
Should really check to see if underflow or overflow occurred and only report handled if they did - otherwise we might have trouble with new hardware blocks that have other interrupts.
- if (pdev->id >= 2)
return -EINVAL;
- mxs_saif[pdev->id] = saif;
The id check should check for ARRAY_SIZE() mxs_saif[] to make updates easier if a new chip has more ports.
- saif->dma_param.chan_irq = platform_get_irq(pdev, 1);
- if (saif->dma_param.chan_irq < 0) {
Does the IRQ requesting need to happen after you've set the base address? Otherwise there's a potential race if for some reason the IRQ fires before we've got the data structures set up.
-----Original Message----- From: alsa-devel-bounces@alsa-project.org [mailto:alsa-devel- bounces@alsa-project.org] On Behalf Of Mark Brown Sent: Wednesday, July 13, 2011 9:04 AM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; lrg@ti.com; linux-arm-kernel@lists.infradead.org; u.kleine-koenig@pengutronix.de Subject: Re: [alsa-devel] [PATCH V2 02/10] ASoc: mxs: add mxs-saif driver
On Tue, Jul 12, 2011 at 11:04:37PM +0800, Dong Aisheng wrote:
Looks pretty good, a few small issues below.
- /* The SAIF clock should be either 384*fs or 512*fs */
- if (saif->mclk_in_use) {
if (mclk % 32 == 0) {
scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
ret = clk_set_rate(saif->clk, 512 * rate);
} else if (mclk % 48 == 0) {
scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
ret = clk_set_rate(saif->clk, 384 * rate);
}
What if MCLK is not set corectly for some reason? We'll just silently do nothing.
We will just return an error if MCLK is not correct (not 32x or 48x). /* SAIF MCLK should be either 32*fs or 48*fs */ if (saif->mclk_in_use && (mclk % 32 != 0) && (mclk % 48 != 0)) return -EINVAL; Machine driver should guarantee to set a correct value since it's HW limitation. Is that ok?
- if (ret)
return -EINVAL;
Should pass through any error codes we get.
In hw_params()...
OK, I will change it.
- stat = __raw_readl(saif->base + SAIF_STAT);
- if (stat & BM_SAIF_STAT_BUSY) {
dev_err(cpu_dai->dev, "error: busy\n");
return -EBUSY;
- }
Does this work for simultaneous playback and record? Perhaps you need to set symmetric_rates in the DAI (if the hardware needs the same sample rate for playback and record) and have a check here to see if we're trying to apply the same configuration as we already have.
The SAIF does not support full-duplex (simultaneous playback & record). However, it allows to use two saif instances to implement it (one playback While another capture). For the two SAIFs, one can master or driver the clock pins while the other SAIF, in slave mode, receives clock from the master. This also means that both SAIFs must operate at the same sample rate.
So it seems, as you said, we may need to set symmetric_rates in the DAI.
- struct mxs_saif *saif = dev_id;
- unsigned int stat;
- stat = __raw_readl(saif->base + SAIF_STAT);
- if (stat & BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ)
dev_dbg(saif->dev, "underrun!!! %d\n", ++saif->fifo_underrun);
- if (stat & BM_SAIF_STAT_FIFO_OVERFLOW_IRQ)
dev_dbg(saif->dev, "overrun!!! %d\n", ++saif->fifo_overrun);
Probably dev_err()?
I met an issue that during each playback when I pressed CTRL+C to stop, I would always see a line of "underrun!!!" message. This might because we actually do not stop SAIF (clear HW_SAIF_CTRL RUN bit) When SNDRV_PCM_TRIGGER_STOP due to codec still needs the clock. (clear HW_SAIF_CTRL RUN bit may cause mclk stop) So I observed that there was at least one underrun happened. It's limitation that sgtl5000 codec needs SAIF mclk. Bothered by this "underrun!!!", i use the dev_dbg only for debug purpose.
- __raw_writel(BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ |
BM_SAIF_STAT_FIFO_OVERFLOW_IRQ,
saif->base + SAIF_STAT + MXS_CLR_ADDR);
- return IRQ_HANDLED;
Should really check to see if underflow or overflow occurred and only report handled if they did - otherwise we might have trouble with new hardware blocks that have other interrupts.
I may clear those two interrupts separately. But I'm not quite understand how it may cause trouble with new hardware Block that have other interrupts since it only clear underrun&overrun irq.
- if (pdev->id >= 2)
return -EINVAL;
- mxs_saif[pdev->id] = saif;
The id check should check for ARRAY_SIZE() mxs_saif[] to make updates easier if a new chip has more ports.
Yes, got it.
- saif->dma_param.chan_irq = platform_get_irq(pdev, 1);
- if (saif->dma_param.chan_irq < 0) {
Does the IRQ requesting need to happen after you've set the base address? Otherwise there's a potential race if for some reason the IRQ fires before we've got the data structures set up.
Yes, neglect it. Thanks for reminder. :)
Regards Dong Aisheng
On Wed, Jul 13, 2011 at 08:14:09AM +0000, Dong Aisheng-B29396 wrote:
What if MCLK is not set corectly for some reason? We'll just silently do nothing.
We will just return an error if MCLK is not correct (not 32x or 48x). /* SAIF MCLK should be either 32*fs or 48*fs */ if (saif->mclk_in_use && (mclk % 32 != 0) && (mclk % 48 != 0)) return -EINVAL; Machine driver should guarantee to set a correct value since it's HW limitation. Is that ok?
Please fix the code so you only check if the ratio is valid in one place, the code currently is too hard to read.
So it seems, as you said, we may need to set symmetric_rates in the DAI.
Not if there are two separate SAIFs used for each direction - this is only relevant if one interface provides both playback and capture.
- stat = __raw_readl(saif->base + SAIF_STAT);
- if (stat & BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ)
dev_dbg(saif->dev, "underrun!!! %d\n", ++saif->fifo_underrun);
- if (stat & BM_SAIF_STAT_FIFO_OVERFLOW_IRQ)
dev_dbg(saif->dev, "overrun!!! %d\n", ++saif->fifo_overrun);
Probably dev_err()?
I met an issue that during each playback when I pressed CTRL+C to stop, I would always see a line of "underrun!!!" message. This might because we actually do not stop SAIF (clear HW_SAIF_CTRL RUN bit) When SNDRV_PCM_TRIGGER_STOP due to codec still needs the clock. (clear HW_SAIF_CTRL RUN bit may cause mclk stop) So I observed that there was at least one underrun happened. It's limitation that sgtl5000 codec needs SAIF mclk. Bothered by this "underrun!!!", i use the dev_dbg only for debug purpose.
Hrm, OK. I guess that's reasonable.
- __raw_writel(BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ |
BM_SAIF_STAT_FIFO_OVERFLOW_IRQ,
saif->base + SAIF_STAT + MXS_CLR_ADDR);
- return IRQ_HANDLED;
Should really check to see if underflow or overflow occurred and only report handled if they did - otherwise we might have trouble with new hardware blocks that have other interrupts.
I may clear those two interrupts separately. But I'm not quite understand how it may cause trouble with new hardware Block that have other interrupts since it only clear underrun&overrun irq.
Because you return IRQ_HANDLED you're telling the IRQ infrastructure that you handled the interrupt but you didn't. If you didn't handle an interrupt you should return IRQ_NONE instead.
-----Original Message----- From: Mark Brown [mailto:broonie@opensource.wolfsonmicro.com] Sent: Thursday, July 14, 2011 11:07 AM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; lrg@ti.com; linux-arm-kernel@lists.infradead.org; u.kleine-koenig@pengutronix.de Subject: Re: [alsa-devel] [PATCH V2 02/10] ASoc: mxs: add mxs-saif driver
We will just return an error if MCLK is not correct (not 32x or 48x). /* SAIF MCLK should be either 32*fs or 48*fs */ if (saif->mclk_in_use && (mclk % 32 != 0) && (mclk % 48 != 0)) return -EINVAL; Machine driver should guarantee to set a correct value since it's HW
limitation.
Is that ok?
Please fix the code so you only check if the ratio is valid in one place, the code currently is too hard to read.
Sorry, here the comment is wrong: /* SAIF MCLK should be either 32*fs or 48*fs */ if (saif->mclk_in_use && (mclk % 32 != 0) && (mclk % 48 != 0)) return -EINVAL; should be changed to: /* SAIF MCLK should be either 32x or 48x */
BTW, do you mean the duplicate ratio checking is the following line? /* select the multiple of the base frequency rate of MCLK */ switch (mclk / rate) { ..... This is used to calculate the real ratio for mclk. (The range is from 32x to 512x)
Yes, the code is a little confuse. Since the mxs_saif_set_clk sets both saif clk ratio and saif mclk ratio, I placed the mclk ratio checking in mxs_saif_set_clk rather than in mxs_saif_get_mclk to avoid duplication. And saif clk ratio needs to meet mclk ratio. Maybe I need to add more comments in code as follows. /* * The SAIF clock should be either 384*fs or 512*fs * * For 32x mclk, set saif clk as 512x. * For 48x mclk, set saif clk as 384x. */
Do you have any suggestion to make it more easy to read?
So it seems, as you said, we may need to set symmetric_rates in the DAI.
Not if there are two separate SAIFs used for each direction - this is only relevant if one interface provides both playback and capture.
OK. But how to treat our case that although there are two separate SAIFs used for each direction, they cannot work on different sample rate? Is there any parameter like symmetric_rates to tell ASoc core that do not play a different rate stream While another SAIF is already working?
Because you return IRQ_HANDLED you're telling the IRQ infrastructure that you handled the interrupt but you didn't. If you didn't handle an interrupt you should return IRQ_NONE instead.
Ok, understand. Thanks. How about adding handle IRQ_NONE as follows: diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index bb32ee9..fefab24 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -475,6 +475,10 @@ static irqreturn_t mxs_saif_irq(int irq, void *dev_id) unsigned int stat;
stat = __raw_readl(saif->base + SAIF_STAT); + if (stat & (BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ | + BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ)) + return IRQ_NONE; + if (stat & BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ) dev_dbg(saif->dev, "underrun!!! %d\n", ++saif->fifo_underrun);
Regards Dong Aisheng
-----Original Message----- From: Mark Brown [mailto:broonie@opensource.wolfsonmicro.com] Sent: Thursday, July 14, 2011 11:07 AM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; lrg@ti.com; linux-arm-kernel@lists.infradead.org; u.kleine-koenig@pengutronix.de Subject: Re: [alsa-devel] [PATCH V2 02/10] ASoc: mxs: add mxs-saif driver
On Wed, Jul 13, 2011 at 08:14:09AM +0000, Dong Aisheng-B29396 wrote:
What if MCLK is not set corectly for some reason? We'll just silently do nothing.
We will just return an error if MCLK is not correct (not 32x or 48x). /* SAIF MCLK should be either 32*fs or 48*fs */ if (saif->mclk_in_use && (mclk % 32 != 0) && (mclk % 48 != 0)) return -EINVAL; Machine driver should guarantee to set a correct value since it's HW
limitation.
Is that ok?
Please fix the code so you only check if the ratio is valid in one place, the code currently is too hard to read.
Hi Mark,
So far, thanks a lot for your kindly review. I removed the duplicated checking as you indicated and changed the clk setting flow a bit(also adding more comments) to try to make it more easy to read.
The code is as follows. Is it ok for you? Or you have any better suggestions?
/* * Set SAIF clock and MCLK */ static int mxs_saif_set_clk(struct mxs_saif *saif, unsigned int mclk, unsigned int rate) { u32 scr; int ret;
scr = __raw_readl(saif->base + SAIF_CTRL); scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE; scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
/* * Set SAIF clock * * The SAIF clock should be either 384*fs or 512*fs. * If MCLK is used, the SAIF clk ratio needs to match mclk ratio. * For 32x mclk, set saif clk as 512*fs. * For 48x mclk, set saif clk as 384*fs. * * If MCLK is not used, we just set saif clk to 512*fs. */ if (saif->mclk_in_use) { if (mclk % 32 == 0) { scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; ret = clk_set_rate(saif->clk, 512 * rate); } else if (mclk % 48 == 0) { scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE; ret = clk_set_rate(saif->clk, 384 * rate); } else { /* SAIF MCLK should be either 32x or 48x */ return -EINVAL; } } else { ret = clk_set_rate(saif->clk, 512 * rate); scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; }
if (ret) return ret;
if (!saif->mclk_in_use) { __raw_writel(scr, saif->base + SAIF_CTRL); return 0; }
/* * Program the over-sample rate for MCLK output if used * * The available MCLK range is 32x, 48x... 512x. The rate * could be from 8kHz to 192kH. */ switch (mclk / rate) { case 32: scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(4); break; case 64: scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3); break; case 128: scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2); break; case 256: scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1); break; case 512: scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0); break; case 48: scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3); break; case 96: scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2); break; case 192: scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1); break; case 384: scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0); break; default: return -EINVAL; }
__raw_writel(scr, saif->base + SAIF_CTRL);
return 0; }
Regards Dong Aisheng
On Thu, Jul 14, 2011 at 10:39:13AM +0000, Dong Aisheng-B29396 wrote:
The code is as follows. Is it ok for you? Or you have any better suggestions?
That looks much clearer, I've no better ideas right now. Thanks!
2011/7/14 Mark Brown broonie@opensource.wolfsonmicro.com:
On Thu, Jul 14, 2011 at 10:39:13AM +0000, Dong Aisheng-B29396 wrote:
The code is as follows. Is it ok for you? Or you have any better suggestions?
That looks much clearer, I've no better ideas right now. Thanks!
Thanks, Mark.
Regards Dong Aisheng
The driver only supports playback firstly. For recording, as we have to use two saif instances to implement full duplex (playback & recording) due to hardware limitation, we need to figure out a good design to fit in ASoC.
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Liam Girdwood lrg@ti.com Cc: Sascha Hauer s.hauer@pengutronix.de --- sound/soc/mxs/mxs-sgtl5000.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 170 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c new file mode 100644 index 0000000..be9b5d5 --- /dev/null +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -0,0 +1,170 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/fsl_devices.h> +#include <linux/gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/soc-dapm.h> +#include <asm/mach-types.h> + +#include "../codecs/sgtl5000.h" +#include "mxs-saif.h" + +static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + unsigned int rate = params_rate(params); + u32 dai_format, mclk; + int ret; + + /* sgtl5000 does not support 512*rate when in 96000 fs */ + rate = params_rate(params); + switch (rate) { + case 96000: + mclk = 256 * rate; + break; + default: + mclk = 512 * rate; + break; + } + + /* Sgtl5000 sysclk should be >= 8MHz and <= 27M */ + if (mclk < 8000000 || mclk > 27000000) + return -EINVAL; + + /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */ + ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0); + if (ret) + return -EINVAL; + + /* The SAIF MCLK should be the same as SGTL5000_SYSCLK */ + ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0); + if (ret) + return -EINVAL; + + /* set codec to slave mode */ + dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS; + + /* set codec DAI configuration */ + ret = snd_soc_dai_set_fmt(codec_dai, dai_format); + if (ret) + return -EINVAL; + + /* set cpu DAI configuration */ + ret = snd_soc_dai_set_fmt(cpu_dai, dai_format); + if (ret) + return -EINVAL; + + return 0; +} + +static struct snd_soc_ops mxs_sgtl5000_hifi_ops = { + .hw_params = mxs_sgtl5000_hw_params, +}; + +static struct snd_soc_dai_link mxs_sgtl5000_dai[] = { + { + .name = "HiFi", + .stream_name = "HiFi Playback", + .codec_dai_name = "sgtl5000", + .codec_name = "sgtl5000.0-000a", + .cpu_dai_name = "mxs-saif.0", + .platform_name = "mxs-pcm-audio.0", + .ops = &mxs_sgtl5000_hifi_ops, + }, +}; + +static struct snd_soc_card mxs_sgtl5000 = { + .name = "mxs_sgtl5000", + .dai_link = mxs_sgtl5000_dai, + .num_links = ARRAY_SIZE(mxs_sgtl5000_dai), +}; + +static int __devinit mxs_sgtl5000_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &mxs_sgtl5000; + int ret; + + /* + * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w). + * The Sgtl5000 sysclk is derived from saif0 mclk and it's range + * should be >= 8MHz and <= 27M. + */ + ret = mxs_saif_get_mclk(0, 44100 * 256, 44100); + if (ret) + return -EINVAL; + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + return ret; + } + + return 0; +} + +static int __devexit mxs_sgtl5000_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + mxs_saif_put_mclk(0); + + snd_soc_unregister_card(card); + + return 0; +} + +static struct platform_driver mxs_sgtl5000_audio_driver = { + .driver = { + .name = "mxs-sgtl5000", + .owner = THIS_MODULE, + }, + .probe = mxs_sgtl5000_probe, + .remove = __devexit_p(mxs_sgtl5000_remove), +}; + +static int __init mxs_sgtl5000_init(void) +{ + return platform_driver_register(&mxs_sgtl5000_audio_driver); +} +module_init(mxs_sgtl5000_init); + +static void __exit mxs_sgtl5000_exit(void) +{ + platform_driver_unregister(&mxs_sgtl5000_audio_driver); +} +module_exit(mxs_sgtl5000_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXS ALSA SoC Machine driver"); +MODULE_LICENSE("GPL");
On Tue, Jul 12, 2011 at 11:04:38PM +0800, Dong Aisheng wrote:
The driver only supports playback firstly. For recording, as we have to use two saif instances to implement full duplex (playback & recording) due to hardware limitation, we need to figure out a good design to fit in ASoC.
Signed-off-by: Dong Aisheng b29396@freescale.com
Which version of the codec do you have? I have:
[ 0.370000] sgtl5000 0-000a: sgtl5000 revision 17 [ 0.370000] sgtl5000 0-000a: asoc: failed to probe CODEC sgtl5000.0-000a: -22 [ 0.380000] asoc: failed to instantiate card mxs_sgtl5000: -22
The failure is because CONFIG_REGULATOR is not set and it thus fails in the codec-driver:
#else /* CONFIG_REGULATOR */ static int ldo_regulator_register(struct snd_soc_codec *codec, struct regulator_init_data *init_data, int voltage) { return -EINVAL; }
(I can patch it to load properly, but well...)
Proper regulator support for mxs is not in mainline yet. Is that planned? How do you do it?
Regards,
Wolfram
-----Original Message----- From: Wolfram Sang [mailto:w.sang@pengutronix.de] Sent: Wednesday, July 13, 2011 7:57 PM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; broonie@opensource.wolfsonmicro.com; lrg@ti.com; linux-arm- kernel@lists.infradead.org; u.kleine-koenig@pengutronix.de Subject: Re: [PATCH V2 03/10] ASoc: mxs: add mxs-sgtl5000 machine driver
Which version of the codec do you have? I have:
Yes, it is revision 17.
[ 0.370000] sgtl5000 0-000a: sgtl5000 revision 17 [ 0.370000] sgtl5000 0-000a: asoc: failed to probe CODEC sgtl5000.0- 000a: -22 [ 0.380000] asoc: failed to instantiate card mxs_sgtl5000: -22
The failure is because CONFIG_REGULATOR is not set and it thus fails in the codec-driver:
#else /* CONFIG_REGULATOR */ static int ldo_regulator_register(struct snd_soc_codec *codec, struct regulator_init_data *init_data, int voltage) { return -EINVAL; }
(I can patch it to load properly, but well...)
Proper regulator support for mxs is not in mainline yet. Is that planned? How do you do it?
Yes, it's not in mainline yet. Fixed regulator may be a method. However currently I just removed VDDIO and VDDA checking in sgtl5000 driver. Additionally, it seems sgtl5000 driver still needs another fix to get it running. I copy the patch as below for your info(Just for test). I'm going to do that in my following work.
Regards Dong Aisheng
-----Original Message----- From: linux-arm-kernel-bounces@lists.infradead.org [mailto:linux-arm- kernel-bounces@lists.infradead.org] On Behalf Of Dong Aisheng-B29396 Sent: Wednesday, July 13, 2011 8:19 PM To: Wolfram Sang Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; broonie@opensource.wolfsonmicro.com; u.kleine-koenig@pengutronix.de; lrg@ti.com; linux-arm-kernel@lists.infradead.org Subject: RE: [PATCH V2 03/10] ASoc: mxs: add mxs-sgtl5000 machine driver
Proper regulator support for mxs is not in mainline yet. Is that
planned?
How do you do it?
Yes, it's not in mainline yet. Fixed regulator may be a method. However currently I just removed VDDIO and VDDA checking in sgtl5000 driver. Additionally, it seems sgtl5000 driver still needs another fix to get it running. I copy the patch as below for your info(Just for test). I'm going to do that in my following work.
For regulator issue, One method is that just add two regulator (VDDA&VDDIO) needed by sgtl5000 in platform code to make sgtl5000 work first. If that I could add it in my the following patches.
Or we do not add it (just leave sgtl5000 unwork), instead, we directly implement the full regulator support for MX28 power management?
What's your suggestion?
Regards Dong Aisheng
For regulator issue, One method is that just add two regulator (VDDA&VDDIO) needed by sgtl5000 in platform code to make sgtl5000 work first. If that I could add it in my the following patches.
Or we do not add it (just leave sgtl5000 unwork), instead, we directly implement the full regulator support for MX28 power management?
What's your suggestion?
Hmmm, as we started now with the audio patches, I'd think we should concentrate on getting them in shape first. So, my preference would be adding just the two regulators. Whenever the power management gets complete, they should be easy to include/adapt, if I didn't miss anything.
With your sgtl-patch, I can set the alsa-controls like volume. Yet the WAV played via simple aplay has a massive hall (like a concert at the other end of the cathedral ;)). Interrupting aplay, and trying again gives me sometimes:
Playing WAVE 'machinae supremacy - great gianna sisters.wav' : Signed 16 bit Little Endian, Rate 44100 Hz, Stereo [ 15.940000] private_candidate: dma0chan0 busy [ 15.950000] private_candidate: dma0chan1 busy [ 15.950000] private_candidate: dma0chan2 filter said false [ 15.960000] private_candidate: dma0chan3 filter said false [ 15.960000] private_candidate: dma0chan4 filter said false [ 15.970000] private_candidate: dma0chan5 filter said false [ 15.970000] private_candidate: dma0chan6 filter said false [ 15.980000] private_candidate: dma0chan7 filter said false [ 15.980000] private_candidate: dma0chan8 filter said false [ 15.990000] private_candidate: dma0chan9 filter said false [ 15.990000] private_candidate: dma0chan10 filter said false [ 16.000000] private_candidate: dma0chan11 filter said false [ 16.010000] private_candidate: dma0chan12 filter said false [ 16.010000] private_candidate: dma0chan13 filter said false [ 16.020000] private_candidate: dma0chan14 filter said false [ 16.020000] private_candidate: dma0chan15 filter said false [ 16.030000] private_candidate: dma1chan0 filter said false [ 16.030000] private_candidate: dma1chan1 filter said false [ 16.040000] private_candidate: dma1chan2 filter said false [ 16.050000] private_candidate: dma1chan3 filter said false [ 16.050000] Division by zero in kernel. [ 16.050000] [<c0025cac>] (unwind_backtrace+0x0/0xe4) from [<c012a1b4>] (Ldiv0+0x8/0x10) [ 16.060000] [<c012a1b4>] (Ldiv0+0x8/0x10) from [<c012a184>] (__aeabi_uidivmod+0x8/0x18) [ 16.070000] [<c012a184>] (__aeabi_uidivmod+0x8/0x18) from [<c01ad5c8>] (audio_dma_irq+0x28/0x38) [ 16.080000] [<c01ad5c8>] (audio_dma_irq+0x28/0x38) from [<c013b200>] (mxs_dma_tasklet+0x18/0x1c) [ 16.090000] [<c013b200>] (mxs_dma_tasklet+0x18/0x1c) from [<c0037430>] (tasklet_action+0x88/0xe0) [ 16.100000] [<c0037430>] (tasklet_action+0x88/0xe0) from [<c0037918>] (__do_softirq+0x84/0x11c) [ 16.110000] [<c0037918>] (__do_softirq+0x84/0x11c) from [<c0037d1c>] (irq_exit+0x40/0x90) [ 16.110000] [<c0037d1c>] (irq_exit+0x40/0x90) from [<c0021064>] (asm_do_IRQ+0x64/0x84) [ 16.120000] [<c0021064>] (asm_do_IRQ+0x64/0x84) from [<c0021b14>] (__irq_svc+0x34/0x60) [ 16.130000] Exception stack(0xc7a13de0 to 0xc7a13e28) [ 16.140000] 3de0: 00000000 00000000 00000000 00000000 c0316368 c7927b00 00000050 60000013 [ 16.140000] 3e00: 00000000 00000000 c0316390 c031f13c 00000000 c7a13e28 c0060fd0 c0060298 [ 16.150000] 3e20: 60000013 ffffffff [ 16.160000] [<c0021b14>] (__irq_svc+0x34/0x60) from [<c0060298>] (__setup_irq+0x280/0x340) [ 16.160000] [<c0060298>] (__setup_irq+0x280/0x340) from [<c0060424>] (request_threaded_irq+0xcc/0x11c) [ 16.170000] [<c0060424>] (request_threaded_irq+0xcc/0x11c) from [<c013b684>] (mxs_dma_alloc_chan_resources+0x70/0xe8) [ 16.180000] [<c013b684>] (mxs_dma_alloc_chan_resources+0x70/0xe8) from [<c013a3d0>] (dma_chan_get+0x88/0x110) [ 16.190000] [<c013a3d0>] (dma_chan_get+0x88/0x110) from [<c013a4d0>] (__dma_request_channel+0x78/0x18c) [ 16.200000] [<c013a4d0>] (__dma_request_channel+0x78/0x18c) from [<c01ad3e0>] (snd_mxs_pcm_hw_params+0x70/0x1d4) [ 16.210000] [<c01ad3e0>] (snd_mxs_pcm_hw_params+0x70/0x1d4) from [<c01a3a0c>] (soc_pcm_hw_params+0x100/0x1ac) [ 16.220000] [<c01a3a0c>] (soc_pcm_hw_params+0x100/0x1ac) from [<c019cd1c>] (snd_pcm_hw_params+0x98/0x33c) [ 16.230000] [<c019cd1c>] (snd_pcm_hw_params+0x98/0x33c) from [<c019dc10>] (snd_pcm_common_ioctl1+0x1e4/0x4f8) [ 16.240000] [<c019dc10>] (snd_pcm_common_ioctl1+0x1e4/0x4f8) from [<c019e374>] (snd_pcm_playback_ioctl1+0x200/0x220) [ 16.250000] [<c019e374>] (snd_pcm_playback_ioctl1+0x200/0x220) from [<c0097b24>] (do_vfs_ioctl+0x258/0x288) [ 16.260000] [<c0097b24>] (do_vfs_ioctl+0x258/0x288) from [<c0097b88>] (sys_ioctl+0x34/0x54) [ 16.270000] [<c0097b88>] (sys_ioctl+0x34/0x54) from [<c0021ea0>] (ret_fast_syscall+0x0/0x2c) [ 16.280000] __dma_request_channel: success (dma1chan4) [ 16.290000] dma dma1chan4: cannot prepare slave dma [ 16.290000] asoc: platform mxs-pcm-audio.0 hw params failed aplay: set_params:1053: Unable to install hw params: ACCESS: RW_INTERLEAVED FORMAT: S16_LE SUBFORMAT: STD SAMPLE_BITS: 16 FRAME_BITS: 32 CHANNELS: 2 RATE: 44100 PERIOD_TIME: (46439 46440) PERIOD_SIZE: 2048 PERIOD_BYTES: 8192 PERIODS: 8 BUFFER_TIME: (371519 371520) BUFFER_SIZE: 16384 BUFFER_BYTES: 65536 TICK_TIME: 0
After that OOPS, playing works again (still with the hall). Didn't have the time to look at these issues yet, but since I was writing to you anyway, I thought I'd mention it...
Regards,
Wolfram
-----Original Message----- From: Wolfram Sang [mailto:w.sang@pengutronix.de] Sent: Wednesday, July 13, 2011 10:01 PM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; broonie@opensource.wolfsonmicro.com; u.kleine-koenig@pengutronix.de; lrg@ti.com; linux-arm-kernel@lists.infradead.org Subject: Re: [PATCH V2 03/10] ASoc: mxs: add mxs-sgtl5000 machine driver
For regulator issue, One method is that just add two regulator (VDDA&VDDIO) needed by sgtl5000 in platform code to make sgtl5000 work
first.
If that I could add it in my the following patches.
Or we do not add it (just leave sgtl5000 unwork), instead, we directly implement the full regulator support for MX28 power management?
What's your suggestion?
Hmmm, as we started now with the audio patches, I'd think we should concentrate on getting them in shape first. So, my preference would be adding just the two regulators. Whenever the power management gets complete, they should be easy to include/adapt, if I didn't miss anything.
OK, I will add it in my following patch.
With your sgtl-patch, I can set the alsa-controls like volume. Yet the WAV played via simple aplay has a massive hall (like a concert at the other end of the cathedral ;)). Interrupting aplay, and trying again gives me sometimes:
After that OOPS, playing works again (still with the hall). Didn't have the time to look at these issues yet, but since I was writing to you anyway, I thought I'd mention it...
I did not meet it in the first time of playing. However, it can happen in the second play. The root cause is that we get an unexpected dma irq before iprtd is initialized when request dma channel(still not start channel).
It seemed to be a DMA issue that the dma channel of saif was in an abnormal state after disable.
A patch already prepared to fix this issue is as follows: I am going to send it out for review. Can you help try if it can fix your issue?
commit d2e4437ddca8bf909ca6c53257e1de504274017e Author: Dong Aisheng b29396@freescale.com Date: Tue Jul 12 21:52:55 2011 +0800
ARM: mxs-dma: reset after disable channel
We met some channels in abnormal state after disable. Reset it to get a clean state.
Signed-off-by: Dong Aisheng b29396@freescale.com
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index 88aad4f..5a24010 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -535,6 +535,7 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, switch (cmd) { case DMA_TERMINATE_ALL: mxs_dma_disable_chan(mxs_chan); + mxs_dma_reset_chan(mxs_chan); break; case DMA_PAUSE: mxs_dma_pause_chan(mxs_chan);
Regards Dong Aisheng
-----Original Message----- From: Wolfram Sang [mailto:w.sang@pengutronix.de] Sent: Wednesday, July 13, 2011 10:01 PM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; broonie@opensource.wolfsonmicro.com; u.kleine-koenig@pengutronix.de; lrg@ti.com; linux-arm-kernel@lists.infradead.org Subject: Re: [PATCH V2 03/10] ASoc: mxs: add mxs-sgtl5000 machine driver
With your sgtl-patch, I can set the alsa-controls like volume. Yet the WAV played via simple aplay has a massive hall (like a concert at the other end of the cathedral ;)).
You mean the sound is incorrect?
Regards Dong Aisheng
On Wed, Jul 13, 2011 at 02:22:42PM +0000, Dong Aisheng-B29396 wrote:
-----Original Message----- From: Wolfram Sang [mailto:w.sang@pengutronix.de] Sent: Wednesday, July 13, 2011 10:01 PM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; broonie@opensource.wolfsonmicro.com; u.kleine-koenig@pengutronix.de; lrg@ti.com; linux-arm-kernel@lists.infradead.org Subject: Re: [PATCH V2 03/10] ASoc: mxs: add mxs-sgtl5000 machine driver
With your sgtl-patch, I can set the alsa-controls like volume. Yet the WAV played via simple aplay has a massive hall (like a concert at the other end of the cathedral ;)).
You mean the sound is incorrect?
It was, but is fixed now. There was something wrong in our audio-path electrically. Together with your DMA patch, music sounds fine now \o/
2011/7/13 Wolfram Sang w.sang@pengutronix.de:
On Wed, Jul 13, 2011 at 02:22:42PM +0000, Dong Aisheng-B29396 wrote:
You mean the sound is incorrect?
It was, but is fixed now. There was something wrong in our audio-path electrically. Together with your DMA patch, music sounds fine now \o/
It's good. Thanks for the test.
Regards Dong Aisheng
On Wed, Jul 13, 2011 at 01:57:11PM +0200, Wolfram Sang wrote:
Proper regulator support for mxs is not in mainline yet. Is that planned? How do you do it?
Regulator support is not a feature of the platform, it's a generic Linux feature.
On Wed, Jul 13, 2011 at 09:46:52PM +0900, Mark Brown wrote:
On Wed, Jul 13, 2011 at 01:57:11PM +0200, Wolfram Sang wrote:
Proper regulator support for mxs is not in mainline yet. Is that planned? How do you do it?
Regulator support is not a feature of the platform, it's a generic Linux feature.
Yup, sorry, meant s/mxs/mx28evk/
On Tue, Jul 12, 2011 at 11:04:38PM +0800, Dong Aisheng wrote:
The driver only supports playback firstly. For recording, as we have to use two saif instances to implement full duplex (playback & recording) due to hardware limitation, we need to figure out a good design to fit in ASoC.
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Liam Girdwood lrg@ti.com Cc: Sascha Hauer s.hauer@pengutronix.de
sound/soc/mxs/mxs-sgtl5000.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 170 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c new file mode 100644 index 0000000..be9b5d5 --- /dev/null +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -0,0 +1,170 @@ +/*
- Copyright 2011 Freescale Semiconductor, Inc.
- 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.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- 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.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+#include <linux/module.h> +#include <linux/moduleparam.h>
No parameters here, drop it.
+#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/fsl_devices.h> +#include <linux/gpio.h>
The last three, too.
+#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/soc-dapm.h> +#include <asm/mach-types.h>
+#include "../codecs/sgtl5000.h" +#include "mxs-saif.h"
+static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int rate = params_rate(params);
Here you initialize 'rate'...
- u32 dai_format, mclk;
- int ret;
- /* sgtl5000 does not support 512*rate when in 96000 fs */
- rate = params_rate(params);
...so this line can go.
Thanks,
Wolfram
-----Original Message----- From: alsa-devel-bounces@alsa-project.org [mailto:alsa-devel- bounces@alsa-project.org] On Behalf Of Wolfram Sang Sent: Wednesday, July 13, 2011 11:58 PM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; broonie@opensource.wolfsonmicro.com; u.kleine-koenig@pengutronix.de; lrg@ti.com; linux-arm-kernel@lists.infradead.org Subject: Re: [alsa-devel] [PATCH V2 03/10] ASoc: mxs: add mxs-sgtl5000 machine driver
+#include <linux/module.h> +#include <linux/moduleparam.h>
No parameters here, drop it.
+#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/fsl_devices.h> +#include <linux/gpio.h>
The last three, too.
- /* sgtl5000 does not support 512*rate when in 96000 fs */
- rate = params_rate(params);
...so this line can go.
Updated. Thanks.
Regards Dong Aisheng
On Tue, Jul 12, 2011 at 11:04:38PM +0800, Dong Aisheng wrote:
The driver only supports playback firstly. For recording, as we have to use two saif instances to implement full duplex (playback & recording) due to hardware limitation, we need to figure out a good design to fit in ASoC.
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Liam Girdwood lrg@ti.com Cc: Sascha Hauer s.hauer@pengutronix.de
sound/soc/mxs/mxs-sgtl5000.c | 170 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 170 insertions(+), 0 deletions(-)
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c new file mode 100644 index 0000000..be9b5d5 --- /dev/null +++ b/sound/soc/mxs/mxs-sgtl5000.c @@ -0,0 +1,170 @@ +/*
- Copyright 2011 Freescale Semiconductor, Inc.
- 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.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- 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.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
+#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/fsl_devices.h> +#include <linux/gpio.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/jack.h> +#include <sound/soc-dapm.h> +#include <asm/mach-types.h>
+#include "../codecs/sgtl5000.h" +#include "mxs-saif.h"
+static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
+{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- unsigned int rate = params_rate(params);
- u32 dai_format, mclk;
- int ret;
- /* sgtl5000 does not support 512*rate when in 96000 fs */
- rate = params_rate(params);
- switch (rate) {
- case 96000:
mclk = 256 * rate;
break;
- default:
mclk = 512 * rate;
break;
- }
- /* Sgtl5000 sysclk should be >= 8MHz and <= 27M */
- if (mclk < 8000000 || mclk > 27000000)
return -EINVAL;
- /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */
- ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0);
- if (ret)
return -EINVAL;
- /* The SAIF MCLK should be the same as SGTL5000_SYSCLK */
- ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0);
- if (ret)
return -EINVAL;
- /* set codec to slave mode */
- dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS;
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
- if (ret)
return -EINVAL;
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
- if (ret)
return -EINVAL;
- return 0;
+}
+static struct snd_soc_ops mxs_sgtl5000_hifi_ops = {
- .hw_params = mxs_sgtl5000_hw_params,
+};
+static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
- {
.name = "HiFi",
.stream_name = "HiFi Playback",
.codec_dai_name = "sgtl5000",
.codec_name = "sgtl5000.0-000a",
.cpu_dai_name = "mxs-saif.0",
.platform_name = "mxs-pcm-audio.0",
.ops = &mxs_sgtl5000_hifi_ops,
- },
+};
It's not related this patch itself, and I'm not against this patch anyhow. I just use it as an example to start a device tree related discussion here. [Cc-ed Grant and devicetree-discuss list]
Currently, soc-core requires machine driver register snd_soc_dai_link with proper codec, cpu_dai and platform device name coded, so that it can match/bind dai link. It works for existing non-dt devices probe, because they can have static device id assigned by platform code to map the actual hardware instance.
For codec device example above, if the codec sgtl5000 connects to i2c.0, the .codec_name has to be sgtl5000.0-000a, and if i2c.1 is used, it has to be sgtl5000.1-000a. And it can scale a little bit by checking machine_is_xxx() and fix it up before calling snd_soc_register_card(), if a board uses i2c instance different than the default one.
But when I recently migrate imx audio driver to be device tree aware, I feel that matching/binding codec using device name in soc-core does not fit there. The devices in device tree probe case either have an unused device id (-1) or a dynamically assigned one which is not necessarily mapped to actually hardware id. This will make device name of codec, cpu_dai, and platform too dynamic for soc-core to match/bind them.
Actually, I found matching cpu_dai using name does not scale in imx-ssi case even for non-dt probe. imx provides some flexibility on the connection between codec and ssi. With proper audmux configuration, codec can be connected to either ssi.0 or ssi.1 for given board design. That said even for given board, .cpu_dai_name can be dynamic. This is something the current soc-core matching/binding approach hates to see.
I'm not sure if this is something that we can solve in soc-core (with an improved matching/binding approach), or we have to sort it out in every single machine driver when they move to device tree. Mark, Grant, you can tell that's why I start discussing this thing with you guys :)
Regards, Shawn
+static struct snd_soc_card mxs_sgtl5000 = {
- .name = "mxs_sgtl5000",
- .dai_link = mxs_sgtl5000_dai,
- .num_links = ARRAY_SIZE(mxs_sgtl5000_dai),
+};
+static int __devinit mxs_sgtl5000_probe(struct platform_device *pdev) +{
- struct snd_soc_card *card = &mxs_sgtl5000;
- int ret;
- /*
* Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w).
* The Sgtl5000 sysclk is derived from saif0 mclk and it's range
* should be >= 8MHz and <= 27M.
*/
- ret = mxs_saif_get_mclk(0, 44100 * 256, 44100);
- if (ret)
return -EINVAL;
- card->dev = &pdev->dev;
- platform_set_drvdata(pdev, card);
- ret = snd_soc_register_card(card);
- if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
ret);
return ret;
- }
- return 0;
+}
+static int __devexit mxs_sgtl5000_remove(struct platform_device *pdev) +{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- mxs_saif_put_mclk(0);
- snd_soc_unregister_card(card);
- return 0;
+}
+static struct platform_driver mxs_sgtl5000_audio_driver = {
- .driver = {
.name = "mxs-sgtl5000",
.owner = THIS_MODULE,
- },
- .probe = mxs_sgtl5000_probe,
- .remove = __devexit_p(mxs_sgtl5000_remove),
+};
+static int __init mxs_sgtl5000_init(void) +{
- return platform_driver_register(&mxs_sgtl5000_audio_driver);
+} +module_init(mxs_sgtl5000_init);
+static void __exit mxs_sgtl5000_exit(void) +{
- platform_driver_unregister(&mxs_sgtl5000_audio_driver);
+} +module_exit(mxs_sgtl5000_exit);
+MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXS ALSA SoC Machine driver");
+MODULE_LICENSE("GPL");
1.7.0.4
linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Fri, Jul 15, 2011 at 11:11:06PM +0800, Shawn Guo wrote:
Currently, soc-core requires machine driver register snd_soc_dai_link with proper codec, cpu_dai and platform device name coded, so that it can match/bind dai link. It works for existing non-dt devices probe, because they can have static device id assigned by platform code to map the actual hardware instance.
There's existing device tree code for PowerPC which seems to figure this out by doing a DT lookup in the machine driver which appears to be sane enough. If we can't look up a struct device via the device tree we'd seem to have a fairly serious problem as struct devices are how we talk about devices at runtime in general.
To be honest I've not paid too much attention to the device tree code people are currently using since discussion around it has always involved repeatedly going over all the reasons why we actually need machine drivers and that gets tedious after a while.
Actually, I found matching cpu_dai using name does not scale in imx-ssi case even for non-dt probe. imx provides some flexibility on the connection between codec and ssi. With proper audmux configuration, codec can be connected to either ssi.0 or ssi.1 for given board design. That said even for given board, .cpu_dai_name can be dynamic. This is something the current soc-core matching/binding approach hates to see.
This isn't a scalability thing and it's not related to binding, it's because the AUXMUX isn't properly represented as a driver. It probably wants to be moved over to use the soc-dsp stuff Liam's doing at the minute. Currently we don't tell the core about the AUDMUX at all so it's unreasonable to expect it to do anything except a static route.
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Mark Brown broonie@opensource.wolfsonmicro.com Cc: Liam Girdwood lrg@ti.com Cc: Sascha Hauer s.hauer@pengutronix.de --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/mxs/Kconfig | 19 +++++++++++++++++++ sound/soc/mxs/Makefile | 10 ++++++++++ 4 files changed, 31 insertions(+), 0 deletions(-)
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 8224db5..47d07ce 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -51,6 +51,7 @@ source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" source "sound/soc/mid-x86/Kconfig" +source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/s6000/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 1ed61c5..fc4136e 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += imx/ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += mid-x86/ +obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ obj-$(CONFIG_SND_SOC) += kirkwood/ diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig new file mode 100644 index 0000000..778b2dc --- /dev/null +++ b/sound/soc/mxs/Kconfig @@ -0,0 +1,19 @@ +menuconfig SND_MXS_SOC + tristate "SoC Audio for Freescale MXS CPUs" + depends on ARCH_MXS + select SND_PCM + help + Say Y or M if you want to add support for codecs attached to + the MXS SAIF interface. + + +if SND_MXS_SOC + +config SND_SOC_MXS_SGTL5000 + tristate "SoC Audio support for i.MX boards with sgtl5000" + select SND_SOC_SGTL5000 + help + Say Y if you want to add support for SoC audio on an MXS board with + a sgtl5000 codec. + +endif # SND_MXS_SOC diff --git a/sound/soc/mxs/Makefile b/sound/soc/mxs/Makefile new file mode 100644 index 0000000..565b5b5 --- /dev/null +++ b/sound/soc/mxs/Makefile @@ -0,0 +1,10 @@ +# MXS Platform Support +snd-soc-mxs-objs := mxs-saif.o +snd-soc-mxs-pcm-objs := mxs-pcm.o + +obj-$(CONFIG_SND_MXS_SOC) += snd-soc-mxs.o snd-soc-mxs-pcm.o + +# i.MX Machine Support +snd-soc-mxs-sgtl5000-objs := mxs-sgtl5000.o + +obj-$(CONFIG_SND_SOC_MXS_SGTL5000) += snd-soc-mxs-sgtl5000.o
+if SND_MXS_SOC
+config SND_SOC_MXS_SGTL5000
- tristate "SoC Audio support for i.MX boards with sgtl5000"
depends on I2C
- select SND_SOC_SGTL5000
- help
Say Y if you want to add support for SoC audio on an MXS board with
a sgtl5000 codec.
+endif # SND_MXS_SOC
2011/7/13 Wolfram Sang w.sang@pengutronix.de:
+if SND_MXS_SOC
+config SND_SOC_MXS_SGTL5000
- tristate "SoC Audio support for i.MX boards with sgtl5000"
depends on I2C
Added. Thanks.
Regards Dong Aisheng
Set pll0 as parent.
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de --- arch/arm/mach-mxs/clock-mx28.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mxs/clock-mx28.c b/arch/arm/mach-mxs/clock-mx28.c index 5dcc59d..7b3657b 100644 --- a/arch/arm/mach-mxs/clock-mx28.c +++ b/arch/arm/mach-mxs/clock-mx28.c @@ -640,6 +640,8 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "lradc", lradc_clk) _REGISTER_CLOCK(NULL, "spdif", spdif_clk) _REGISTER_CLOCK("imx28-fb", NULL, lcdif_clk) + _REGISTER_CLOCK("mxs-saif.0", NULL, saif0_clk) + _REGISTER_CLOCK("mxs-saif.1", NULL, saif1_clk) };
static int clk_misc_init(void) @@ -774,6 +776,8 @@ int __init mx28_clocks_init(void) clk_enable(&uart_clk);
clk_set_parent(&lcdif_clk, &ref_pix_clk); + clk_set_parent(&saif0_clk, &pll0_clk); + clk_set_parent(&saif1_clk, &pll0_clk);
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de --- arch/arm/mach-mxs/Kconfig | 1 + arch/arm/mach-mxs/devices-mx28.h | 3 + arch/arm/mach-mxs/devices/Kconfig | 3 + arch/arm/mach-mxs/devices/Makefile | 1 + arch/arm/mach-mxs/devices/platform-mxs-saif.c | 60 +++++++++++++++++++++++ arch/arm/mach-mxs/include/mach/devices-common.h | 12 +++++ arch/arm/mach-mxs/mach-mx28evk.c | 20 ++++++++ 7 files changed, 100 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mxs/Kconfig b/arch/arm/mach-mxs/Kconfig index 4cd0231..405c28d 100644 --- a/arch/arm/mach-mxs/Kconfig +++ b/arch/arm/mach-mxs/Kconfig @@ -48,6 +48,7 @@ config MACH_MX28EVK select MXS_HAVE_PLATFORM_FLEXCAN select MXS_HAVE_PLATFORM_MXS_MMC select MXS_HAVE_PLATFORM_MXSFB + select MXS_HAVE_PLATFORM_MXS_SAIF select MXS_OCOTP help Include support for MX28EVK platform. This includes specific diff --git a/arch/arm/mach-mxs/devices-mx28.h b/arch/arm/mach-mxs/devices-mx28.h index 79b9452..c0f21ce 100644 --- a/arch/arm/mach-mxs/devices-mx28.h +++ b/arch/arm/mach-mxs/devices-mx28.h @@ -45,3 +45,6 @@ extern const struct mxs_mxs_mmc_data mx28_mxs_mmc_data[] __initconst;
struct platform_device *__init mx28_add_mxsfb( const struct mxsfb_platform_data *pdata); + +extern const struct mxs_saif_data mx28_saif_data[] __initconst; +#define mx28_add_saif(id) mxs_add_saif(&mx28_saif_data[id]) diff --git a/arch/arm/mach-mxs/devices/Kconfig b/arch/arm/mach-mxs/devices/Kconfig index acf9eea..b554371 100644 --- a/arch/arm/mach-mxs/devices/Kconfig +++ b/arch/arm/mach-mxs/devices/Kconfig @@ -23,3 +23,6 @@ config MXS_HAVE_PLATFORM_MXS_PWM
config MXS_HAVE_PLATFORM_MXSFB bool + +config MXS_HAVE_PLATFORM_MXS_SAIF + bool diff --git a/arch/arm/mach-mxs/devices/Makefile b/arch/arm/mach-mxs/devices/Makefile index 351915c..d3e8cc3 100644 --- a/arch/arm/mach-mxs/devices/Makefile +++ b/arch/arm/mach-mxs/devices/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_MXS_HAVE_PLATFORM_MXS_MMC) += platform-mxs-mmc.o obj-$(CONFIG_MXS_HAVE_PLATFORM_MXS_PWM) += platform-mxs-pwm.o obj-y += platform-gpio-mxs.o obj-$(CONFIG_MXS_HAVE_PLATFORM_MXSFB) += platform-mxsfb.o +obj-$(CONFIG_MXS_HAVE_PLATFORM_MXS_SAIF) += platform-mxs-saif.o diff --git a/arch/arm/mach-mxs/devices/platform-mxs-saif.c b/arch/arm/mach-mxs/devices/platform-mxs-saif.c new file mode 100644 index 0000000..1ec965e --- /dev/null +++ b/arch/arm/mach-mxs/devices/platform-mxs-saif.c @@ -0,0 +1,60 @@ +/* + * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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. + */ +#include <linux/compiler.h> +#include <linux/err.h> +#include <linux/init.h> + +#include <mach/mx23.h> +#include <mach/mx28.h> +#include <mach/devices-common.h> + +#define mxs_saif_data_entry_single(soc, _id) \ + { \ + .id = _id, \ + .iobase = soc ## _SAIF ## _id ## _BASE_ADDR, \ + .irq = soc ## _INT_SAIF ## _id, \ + .dma = soc ## _DMA_SAIF ## _id, \ + .dmairq = soc ## _INT_SAIF ## _id ##_DMA, \ + } + +#define mxs_saif_data_entry(soc, _id) \ + [_id] = mxs_saif_data_entry_single(soc, _id) + +#ifdef CONFIG_SOC_IMX28 +const struct mxs_saif_data mx28_saif_data[] __initconst = { + mxs_saif_data_entry(MX28, 0), + mxs_saif_data_entry(MX28, 1), +}; +#endif + +struct platform_device *__init mxs_add_saif(const struct mxs_saif_data *data) +{ + struct resource res[] = { + { + .start = data->iobase, + .end = data->iobase + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, { + .start = data->irq, + .end = data->irq, + .flags = IORESOURCE_IRQ, + }, { + .start = data->dma, + .end = data->dma, + .flags = IORESOURCE_DMA, + }, { + .start = data->dmairq, + .end = data->dmairq, + .flags = IORESOURCE_IRQ, + }, + + }; + + return mxs_add_platform_device("mxs-saif", data->id, res, + ARRAY_SIZE(res), NULL, 0); +} diff --git a/arch/arm/mach-mxs/include/mach/devices-common.h b/arch/arm/mach-mxs/include/mach/devices-common.h index 812d7a8..a8080f4 100644 --- a/arch/arm/mach-mxs/include/mach/devices-common.h +++ b/arch/arm/mach-mxs/include/mach/devices-common.h @@ -92,3 +92,15 @@ struct platform_device *__init mxs_add_mxs_mmc( /* pwm */ struct platform_device *__init mxs_add_mxs_pwm( resource_size_t iobase, int id); + +/* saif */ +struct mxs_saif_data { + int id; + resource_size_t iobase; + resource_size_t irq; + resource_size_t dma; + resource_size_t dmairq; +}; + +struct platform_device *__init mxs_add_saif( + const struct mxs_saif_data *data); diff --git a/arch/arm/mach-mxs/mach-mx28evk.c b/arch/arm/mach-mxs/mach-mx28evk.c index eaaf6ff..7837a87 100644 --- a/arch/arm/mach-mxs/mach-mx28evk.c +++ b/arch/arm/mach-mxs/mach-mx28evk.c @@ -40,6 +40,8 @@ #define MX28EVK_MMC0_SLOT_POWER MXS_GPIO_NR(3, 28) #define MX28EVK_MMC1_SLOT_POWER MXS_GPIO_NR(3, 29)
+#define DIGCTRL_BASE_ADDR MX28_IO_ADDRESS(MX28_DIGCTL_BASE_ADDR) + static const iomux_cfg_t mx28evk_pads[] __initconst = { /* duart */ MX28_PAD_PWM0__DUART_RX | MXS_PAD_CTRL, @@ -183,6 +185,18 @@ static const iomux_cfg_t mx28evk_pads[] __initconst = {
/* led */ MX28_PAD_AUART1_TX__GPIO_3_5 | MXS_PAD_CTRL, + + /* saif0 & saif1 */ + MX28_PAD_SAIF0_MCLK__SAIF0_MCLK | + (MXS_PAD_12MA | MXS_PAD_3V3 | MXS_PAD_PULLUP), + MX28_PAD_SAIF0_LRCLK__SAIF0_LRCLK | + (MXS_PAD_12MA | MXS_PAD_3V3 | MXS_PAD_PULLUP), + MX28_PAD_SAIF0_BITCLK__SAIF0_BITCLK | + (MXS_PAD_12MA | MXS_PAD_3V3 | MXS_PAD_PULLUP), + MX28_PAD_SAIF0_SDATA0__SAIF0_SDATA0 | + (MXS_PAD_12MA | MXS_PAD_3V3 | MXS_PAD_PULLUP), + MX28_PAD_SAIF1_SDATA0__SAIF1_SDATA0 | + (MXS_PAD_12MA | MXS_PAD_3V3 | MXS_PAD_PULLUP), };
/* led */ @@ -392,6 +406,12 @@ static void __init mx28evk_init(void)
mx28_add_mxsfb(&mx28evk_mxsfb_pdata);
+ mx28_add_saif(0); + mx28_add_saif(1); + + /*set the saif clk mux, both saif0/saif1 use saif0 clk*/ + __raw_writel(0x2 << 10, DIGCTRL_BASE_ADDR); + /* power on mmc slot by writing 0 to the gpio */ ret = gpio_request_one(MX28EVK_MMC0_SLOT_POWER, GPIOF_OUT_INIT_LOW, "mmc0-slot-power");
Hi,
just noticed this one...
diff --git a/arch/arm/mach-mxs/mach-mx28evk.c b/arch/arm/mach-mxs/mach-mx28evk.c index eaaf6ff..7837a87 100644 --- a/arch/arm/mach-mxs/mach-mx28evk.c +++ b/arch/arm/mach-mxs/mach-mx28evk.c @@ -40,6 +40,8 @@ #define MX28EVK_MMC0_SLOT_POWER MXS_GPIO_NR(3, 28) #define MX28EVK_MMC1_SLOT_POWER MXS_GPIO_NR(3, 29)
+#define DIGCTRL_BASE_ADDR MX28_IO_ADDRESS(MX28_DIGCTL_BASE_ADDR)
Hmm, maybe define a function mx28_set_saif_clkmux(val)?
- mx28_add_saif(0);
- mx28_add_saif(1);
- /*set the saif clk mux, both saif0/saif1 use saif0 clk*/
- __raw_writel(0x2 << 10, DIGCTRL_BASE_ADDR);
You might overwrite previous settings here. The hardcoded numbers need defines, too.
2011/7/15 Wolfram Sang w.sang@pengutronix.de:
+#define DIGCTRL_BASE_ADDR MX28_IO_ADDRESS(MX28_DIGCTL_BASE_ADDR)
Hmm, maybe define a function mx28_set_saif_clkmux(val)?
- mx28_add_saif(0);
- mx28_add_saif(1);
- /*set the saif clk mux, both saif0/saif1 use saif0 clk*/
- __raw_writel(0x2 << 10, DIGCTRL_BASE_ADDR);
You might overwrite previous settings here. The hardcoded numbers need defines, too.
It's correct. I'm originally going to do that when implement record because this setting may affect both machine driver and saif driver on recording. Since currently we only implement playback, so i just hardcode it. Do you think if that's ok? Or i need to do it now?
Regards Dong Aisheng
On Fri, Jul 15, 2011 at 11:02:36PM +0800, Dong Aisheng wrote:
2011/7/15 Wolfram Sang w.sang@pengutronix.de:
+#define DIGCTRL_BASE_ADDR MX28_IO_ADDRESS(MX28_DIGCTL_BASE_ADDR)
Hmm, maybe define a function mx28_set_saif_clkmux(val)?
- mx28_add_saif(0);
- mx28_add_saif(1);
- /*set the saif clk mux, both saif0/saif1 use saif0 clk*/
- __raw_writel(0x2 << 10, DIGCTRL_BASE_ADDR);
You might overwrite previous settings here. The hardcoded numbers need defines, too.
It's correct. I'm originally going to do that when implement record because this setting may affect both machine driver and saif driver on recording. Since currently we only implement playback, so i just hardcode it. Do you think if that's ok? Or i need to do it now?
If you just do playback, do you need to mux SAIF1-clk to SAIF0-clk ? If not (what I'd think), just leave changing DIGCTL out completely since the reset-default for that register should work. If you do need changing DIGCTL, some helper function like mentioned above would be better, I think.
2011/7/15 Wolfram Sang w.sang@pengutronix.de:
If you just do playback, do you need to mux SAIF1-clk to SAIF0-clk ? If not (what I'd think), just leave changing DIGCTL out completely since the reset-default for that register should work. If you do need changing DIGCTL, some helper function like mentioned above would be better, I think.
You're right. I will remove that line of code firstly before doing record and we will provide that helper funtion later. Thanks for your suggestions.
Regards Dong Aisheng
On Tue, Jul 12, 2011 at 11:04:35PM +0800, Dong Aisheng wrote:
This patch series is to add audio support based on saif for mxs platform.
Seems like only 6 out of 10 patches made it to the lists? Pity, I was looking forward to all of them :)
Isn't there someone at Freescale or Linaro who can give you developers access to a git server? I think it would speed up development if reviewers/testers could simply pull your branches.
Regards,
Wolfram
-----Original Message----- From: Wolfram Sang [mailto:w.sang@pengutronix.de] Sent: Wednesday, July 13, 2011 2:23 AM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; broonie@opensource.wolfsonmicro.com; lrg@ti.com; linux-arm- kernel@lists.infradead.org; u.kleine-koenig@pengutronix.de Subject: Re: [PATCH V2 00/10]
On Tue, Jul 12, 2011 at 11:04:35PM +0800, Dong Aisheng wrote:
This patch series is to add audio support based on saif for mxs
platform.
Seems like only 6 out of 10 patches made it to the lists? Pity, I was looking forward to all of them :)
Sorry for missed patches (our mail server may get problems). I will send missed ones out.
Isn't there someone at Freescale or Linaro who can give you developers access to a git server? I think it would speed up development if reviewers/testers could simply pull your branches.
Not yet for a public git server. We will check internally with IT to see if we can build one.
Regards Dong Aisheng
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de --- arch/arm/mach-mxs/mach-mx28evk.c | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mxs/mach-mx28evk.c b/arch/arm/mach-mxs/mach-mx28evk.c index 7837a87..c8b154a 100644 --- a/arch/arm/mach-mxs/mach-mx28evk.c +++ b/arch/arm/mach-mxs/mach-mx28evk.c @@ -186,6 +186,12 @@ static const iomux_cfg_t mx28evk_pads[] __initconst = { /* led */ MX28_PAD_AUART1_TX__GPIO_3_5 | MXS_PAD_CTRL,
+ /* I2C */ + MX28_PAD_I2C0_SCL__I2C0_SCL | + (MXS_PAD_8MA | MXS_PAD_3V3 | MXS_PAD_PULLUP), + MX28_PAD_I2C0_SDA__I2C0_SDA | + (MXS_PAD_8MA | MXS_PAD_3V3 | MXS_PAD_PULLUP), + /* saif0 & saif1 */ MX28_PAD_SAIF0_MCLK__SAIF0_MCLK | (MXS_PAD_12MA | MXS_PAD_3V3 | MXS_PAD_PULLUP), @@ -366,6 +372,12 @@ static struct mxs_mmc_platform_data mx28evk_mmc_pdata[] __initdata = { }, };
+static struct i2c_board_info mxs_i2c0_board_info[] __initdata = { + { + I2C_BOARD_INFO("sgtl5000", 0x0a), + }, +}; + static void __init mx28evk_init(void) { int ret; @@ -409,6 +421,10 @@ static void __init mx28evk_init(void) mx28_add_saif(0); mx28_add_saif(1);
+ mx28_add_mxs_i2c(0); + i2c_register_board_info(0, mxs_i2c0_board_info, + ARRAY_SIZE(mxs_i2c0_board_info)); + /*set the saif clk mux, both saif0/saif1 use saif0 clk*/ __raw_writel(0x2 << 10, DIGCTRL_BASE_ADDR);
On Wed, Jul 13, 2011 at 11:36:07AM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de
arch/arm/mach-mxs/mach-mx28evk.c | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-)
Also needs in Kconfig:
+ select MXS_HAVE_PLATFORM_MXS_I2C
On Wed, Jul 13, 2011 at 11:13:14AM +0200, Wolfram Sang wrote:
On Wed, Jul 13, 2011 at 11:36:07AM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de
arch/arm/mach-mxs/mach-mx28evk.c | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-)
Also needs in Kconfig:
select MXS_HAVE_PLATFORM_MXS_I2C
#include <linux/i2c.h> for i2c_register_board_info() might be good, too...
-----Original Message----- From: Wolfram Sang [mailto:w.sang@pengutronix.de] Sent: Wednesday, July 13, 2011 5:56 PM To: Dong Aisheng-B29396 Cc: alsa-devel@alsa-project.org; s.hauer@pengutronix.de; broonie@opensource.wolfsonmicro.com; u.kleine-koenig@pengutronix.de; lrg@ti.com; linux-arm-kernel@lists.infradead.org Subject: Re: [PATCH V2 07/10] ARM: mxs: add sgtl5000 i2c device
On Wed, Jul 13, 2011 at 11:13:14AM +0200, Wolfram Sang wrote:
On Wed, Jul 13, 2011 at 11:36:07AM +0800, Dong Aisheng wrote:
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de
arch/arm/mach-mxs/mach-mx28evk.c | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-)
Also needs in Kconfig:
select MXS_HAVE_PLATFORM_MXS_I2C
#include <linux/i2c.h> for i2c_register_board_info() might be good, too...
Will add them all. Thanks.
Regards Dong Aisheng
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de --- arch/arm/mach-mxs/mach-mx28evk.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mxs/mach-mx28evk.c b/arch/arm/mach-mxs/mach-mx28evk.c index c8b154a..03f75fe 100644 --- a/arch/arm/mach-mxs/mach-mx28evk.c +++ b/arch/arm/mach-mxs/mach-mx28evk.c @@ -425,6 +425,9 @@ static void __init mx28evk_init(void) i2c_register_board_info(0, mxs_i2c0_board_info, ARRAY_SIZE(mxs_i2c0_board_info));
+ mxs_add_platform_device("mxs-sgtl5000", 0, NULL, 0, + NULL, 0); + /*set the saif clk mux, both saif0/saif1 use saif0 clk*/ __raw_writel(0x2 << 10, DIGCTRL_BASE_ADDR);
According to spec, set to 1 is the enable of fractional devide or the clock can not be generated properly.
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de --- arch/arm/mach-mxs/clock-mx28.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-mxs/clock-mx28.c b/arch/arm/mach-mxs/clock-mx28.c index 7b3657b..7954013 100644 --- a/arch/arm/mach-mxs/clock-mx28.c +++ b/arch/arm/mach-mxs/clock-mx28.c @@ -710,11 +710,11 @@ static int clk_misc_init(void)
/* SAIF has to use frac div for functional operation */ reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF0); - reg &= ~BM_CLKCTRL_SAIF0_DIV_FRAC_EN; + reg |= BM_CLKCTRL_SAIF0_DIV_FRAC_EN; __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF0);
reg = __raw_readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF1); - reg &= ~BM_CLKCTRL_SAIF1_DIV_FRAC_EN; + reg |= BM_CLKCTRL_SAIF1_DIV_FRAC_EN; __raw_writel(reg, CLKCTRL_BASE_ADDR + HW_CLKCTRL_SAIF1);
/*
Other files using dma.h may fail to compile as follows: In file included from sound/soc/mxs/mxs-pcm.h:22, from sound/soc/mxs/mxs-saif.h:112, from sound/soc/mxs/mxs-sgtl5000.c:34: arch/arm/mach-mxs/include/mach/dma.h:16: warning: 'struct dma_chan' declared inside parameter list arch/arm/mach-mxs/include/mach/dma.h:16: warning: its scope is only this definition or declaration, which is probably not what you want arch/arm/mach-mxs/include/mach/dma.h: In function 'mxs_dma_is_apbh': arch/arm/mach-mxs/include/mach/dma.h:18: error: dereferencing pointer to incomplete type arch/arm/mach-mxs/include/mach/dma.h: At top level: arch/arm/mach-mxs/include/mach/dma.h:21: warning: 'struct dma_chan' declared inside parameter list arch/arm/mach-mxs/include/mach/dma.h: In function 'mxs_dma_is_apbx': arch/arm/mach-mxs/include/mach/dma.h:23: error: dereferencing pointer to incomplete type make[3]: *** [sound/soc/mxs/mxs-sgtl5000.o] Error 1 make[2]: *** [sound/soc/mxs] Error 2 make[1]: *** [sound/soc] Error 2 make: *** [sound] Error 2
It seems it's better for dma.h to include dmaengine.h himself.
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de Cc: Shawn Guo shawn.guo@linaro.org --- arch/arm/mach-mxs/include/mach/dma.h | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mxs/include/mach/dma.h b/arch/arm/mach-mxs/include/mach/dma.h index 7f4aeea..203d7c4 100644 --- a/arch/arm/mach-mxs/include/mach/dma.h +++ b/arch/arm/mach-mxs/include/mach/dma.h @@ -9,6 +9,8 @@ #ifndef __MACH_MXS_DMA_H__ #define __MACH_MXS_DMA_H__
+#include <linux/dmaengine.h> + struct mxs_dma_data { int chan_irq; };
On Wed, Jul 13, 2011 at 11:40:54AM +0800, Dong Aisheng wrote:
Other files using dma.h may fail to compile as follows: In file included from sound/soc/mxs/mxs-pcm.h:22, from sound/soc/mxs/mxs-saif.h:112, from sound/soc/mxs/mxs-sgtl5000.c:34: arch/arm/mach-mxs/include/mach/dma.h:16: warning: 'struct dma_chan' declared inside parameter list arch/arm/mach-mxs/include/mach/dma.h:16: warning: its scope is only this definition or declaration, which is probably not what you want arch/arm/mach-mxs/include/mach/dma.h: In function 'mxs_dma_is_apbh': arch/arm/mach-mxs/include/mach/dma.h:18: error: dereferencing pointer to incomplete type arch/arm/mach-mxs/include/mach/dma.h: At top level: arch/arm/mach-mxs/include/mach/dma.h:21: warning: 'struct dma_chan' declared inside parameter list arch/arm/mach-mxs/include/mach/dma.h: In function 'mxs_dma_is_apbx': arch/arm/mach-mxs/include/mach/dma.h:23: error: dereferencing pointer to incomplete type make[3]: *** [sound/soc/mxs/mxs-sgtl5000.o] Error 1 make[2]: *** [sound/soc/mxs] Error 2 make[1]: *** [sound/soc] Error 2 make: *** [sound] Error 2
It seems it's better for dma.h to include dmaengine.h himself.
Signed-off-by: Dong Aisheng b29396@freescale.com Cc: Sascha Hauer s.hauer@pengutronix.de Cc: Shawn Guo shawn.guo@linaro.org
Acked-by: Wolfram Sang w.sang@pengutronix.de
Sascha, maybe we can pick this one up already?
arch/arm/mach-mxs/include/mach/dma.h | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-mxs/include/mach/dma.h b/arch/arm/mach-mxs/include/mach/dma.h index 7f4aeea..203d7c4 100644 --- a/arch/arm/mach-mxs/include/mach/dma.h +++ b/arch/arm/mach-mxs/include/mach/dma.h @@ -9,6 +9,8 @@ #ifndef __MACH_MXS_DMA_H__ #define __MACH_MXS_DMA_H__
+#include <linux/dmaengine.h>
struct mxs_dma_data { int chan_irq; }; -- 1.7.0.4
linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
participants (7)
-
Dong Aisheng
-
Dong Aisheng
-
Dong Aisheng-B29396
-
Mark Brown
-
Russell King - ARM Linux
-
Shawn Guo
-
Wolfram Sang